test(ivy): add onChanges acceptance tests (#30445)
- moves render3 tests to acceptance tests. PR Close #30445
This commit is contained in:
parent
222dde129d
commit
d18c58816f
@ -8,11 +8,12 @@
|
|||||||
|
|
||||||
import {CommonModule} from '@angular/common';
|
import {CommonModule} from '@angular/common';
|
||||||
import {Component, ComponentFactoryResolver, Directive, Input, NgModule, OnChanges, SimpleChanges, ViewChild, ViewContainerRef} from '@angular/core';
|
import {Component, ComponentFactoryResolver, Directive, Input, NgModule, OnChanges, SimpleChanges, ViewChild, ViewContainerRef} from '@angular/core';
|
||||||
|
import {SimpleChange} from '@angular/core/src/core';
|
||||||
import {TestBed} from '@angular/core/testing';
|
import {TestBed} from '@angular/core/testing';
|
||||||
import {By} from '@angular/platform-browser';
|
import {By} from '@angular/platform-browser';
|
||||||
import {onlyInIvy} from '@angular/private/testing';
|
import {onlyInIvy} from '@angular/private/testing';
|
||||||
|
|
||||||
describe('ngOnChanges', () => {
|
describe('onChanges', () => {
|
||||||
it('should correctly support updating one Input among many', () => {
|
it('should correctly support updating one Input among many', () => {
|
||||||
let log: string[] = [];
|
let log: string[] = [];
|
||||||
|
|
||||||
@ -59,6 +60,930 @@ describe('ngOnChanges', () => {
|
|||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
expect(log).toEqual(['c: 0 -> 3']);
|
expect(log).toEqual(['c: 0 -> 3']);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should call onChanges method after inputs are set in creation and update mode', () => {
|
||||||
|
const events: any[] = [];
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'comp',
|
||||||
|
template: `<p>test</p>`,
|
||||||
|
})
|
||||||
|
class Comp {
|
||||||
|
@Input()
|
||||||
|
val1 = 'a';
|
||||||
|
|
||||||
|
@Input('publicVal2')
|
||||||
|
val2 = 'b';
|
||||||
|
|
||||||
|
ngOnChanges(changes: SimpleChanges) { events.push({name: 'comp', changes}); }
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component({template: `<comp [val1]="val1" [publicVal2]="val2"></comp>`})
|
||||||
|
class App {
|
||||||
|
val1 = 'a2';
|
||||||
|
|
||||||
|
val2 = 'b2';
|
||||||
|
}
|
||||||
|
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
declarations: [App, Comp],
|
||||||
|
});
|
||||||
|
const fixture = TestBed.createComponent(App);
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
expect(events).toEqual([{
|
||||||
|
name: 'comp',
|
||||||
|
changes: {
|
||||||
|
val1: new SimpleChange(undefined, 'a2', true),
|
||||||
|
val2: new SimpleChange(undefined, 'b2', true),
|
||||||
|
}
|
||||||
|
}]);
|
||||||
|
|
||||||
|
events.length = 0;
|
||||||
|
fixture.componentInstance.val1 = 'a3';
|
||||||
|
fixture.componentInstance.val2 = 'b3';
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
|
||||||
|
expect(events).toEqual([{
|
||||||
|
name: 'comp',
|
||||||
|
changes: {
|
||||||
|
val1: new SimpleChange('a2', 'a3', false),
|
||||||
|
val2: new SimpleChange('b2', 'b3', false),
|
||||||
|
}
|
||||||
|
}]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should call parent onChanges before child onChanges', () => {
|
||||||
|
const events: any[] = [];
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'parent',
|
||||||
|
template: `<child [val]="val"></child>`,
|
||||||
|
})
|
||||||
|
class Parent {
|
||||||
|
@Input()
|
||||||
|
val = '';
|
||||||
|
|
||||||
|
ngOnChanges(changes: SimpleChanges) { events.push({name: 'parent', changes}); }
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'child',
|
||||||
|
template: `<p>test</p>`,
|
||||||
|
})
|
||||||
|
class Child {
|
||||||
|
@Input()
|
||||||
|
val = '';
|
||||||
|
|
||||||
|
ngOnChanges(changes: SimpleChanges) { events.push({name: 'child', changes}); }
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component({template: `<parent [val]="val"></parent>`})
|
||||||
|
class App {
|
||||||
|
val = 'foo';
|
||||||
|
}
|
||||||
|
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
declarations: [App, Child, Parent],
|
||||||
|
});
|
||||||
|
const fixture = TestBed.createComponent(App);
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
expect(events).toEqual([
|
||||||
|
{
|
||||||
|
name: 'parent',
|
||||||
|
changes: {
|
||||||
|
val: new SimpleChange(undefined, 'foo', true),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'child',
|
||||||
|
changes: {
|
||||||
|
val: new SimpleChange(undefined, 'foo', true),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]);
|
||||||
|
|
||||||
|
events.length = 0;
|
||||||
|
fixture.componentInstance.val = 'bar';
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
expect(events).toEqual([
|
||||||
|
{
|
||||||
|
name: 'parent',
|
||||||
|
changes: {
|
||||||
|
val: new SimpleChange('foo', 'bar', false),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'child',
|
||||||
|
changes: {
|
||||||
|
val: new SimpleChange('foo', 'bar', false),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should call all parent onChanges across view before calling children onChanges', () => {
|
||||||
|
const events: any[] = [];
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'parent',
|
||||||
|
template: `<child [name]="name" [val]="val"></child>`,
|
||||||
|
})
|
||||||
|
class Parent {
|
||||||
|
@Input()
|
||||||
|
val = '';
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
name = '';
|
||||||
|
|
||||||
|
ngOnChanges(changes: SimpleChanges) { events.push({name: 'parent ' + this.name, changes}); }
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'child',
|
||||||
|
template: `<p>test</p>`,
|
||||||
|
})
|
||||||
|
class Child {
|
||||||
|
@Input()
|
||||||
|
val = '';
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
name = '';
|
||||||
|
|
||||||
|
ngOnChanges(changes: SimpleChanges) { events.push({name: 'child ' + this.name, changes}); }
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
template: `
|
||||||
|
<parent name="1" [val]="val"></parent>
|
||||||
|
<parent name="2" [val]="val"></parent>
|
||||||
|
`
|
||||||
|
})
|
||||||
|
class App {
|
||||||
|
val = 'foo';
|
||||||
|
}
|
||||||
|
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
declarations: [App, Child, Parent],
|
||||||
|
});
|
||||||
|
const fixture = TestBed.createComponent(App);
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
expect(events).toEqual([
|
||||||
|
{
|
||||||
|
name: 'parent 1',
|
||||||
|
changes: {
|
||||||
|
name: new SimpleChange(undefined, '1', true),
|
||||||
|
val: new SimpleChange(undefined, 'foo', true),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'parent 2',
|
||||||
|
changes: {
|
||||||
|
name: new SimpleChange(undefined, '2', true),
|
||||||
|
val: new SimpleChange(undefined, 'foo', true),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'child 1',
|
||||||
|
changes: {
|
||||||
|
name: new SimpleChange(undefined, '1', true),
|
||||||
|
val: new SimpleChange(undefined, 'foo', true),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'child 2',
|
||||||
|
changes: {
|
||||||
|
name: new SimpleChange(undefined, '2', true),
|
||||||
|
val: new SimpleChange(undefined, 'foo', true),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
|
events.length = 0;
|
||||||
|
fixture.componentInstance.val = 'bar';
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
expect(events).toEqual([
|
||||||
|
{
|
||||||
|
name: 'parent 1',
|
||||||
|
changes: {
|
||||||
|
val: new SimpleChange('foo', 'bar', false),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'parent 2',
|
||||||
|
changes: {
|
||||||
|
val: new SimpleChange('foo', 'bar', false),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'child 1',
|
||||||
|
changes: {
|
||||||
|
val: new SimpleChange('foo', 'bar', false),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'child 2',
|
||||||
|
changes: {
|
||||||
|
val: new SimpleChange('foo', 'bar', false),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should call onChanges every time a new view is created with ngIf', () => {
|
||||||
|
const events: any[] = [];
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'comp',
|
||||||
|
template: `<p>{{val}}</p>`,
|
||||||
|
})
|
||||||
|
class Comp {
|
||||||
|
@Input()
|
||||||
|
val = '';
|
||||||
|
|
||||||
|
ngOnChanges(changes: SimpleChanges) { events.push({name: 'comp', changes}); }
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component({template: `<comp *ngIf="show" [val]="val"></comp>`})
|
||||||
|
class App {
|
||||||
|
show = true;
|
||||||
|
|
||||||
|
val = 'a';
|
||||||
|
}
|
||||||
|
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
declarations: [App, Comp],
|
||||||
|
imports: [CommonModule],
|
||||||
|
});
|
||||||
|
const fixture = TestBed.createComponent(App);
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
expect(events).toEqual([{
|
||||||
|
name: 'comp',
|
||||||
|
changes: {
|
||||||
|
val: new SimpleChange(undefined, 'a', true),
|
||||||
|
}
|
||||||
|
}]);
|
||||||
|
|
||||||
|
events.length = 0;
|
||||||
|
fixture.componentInstance.show = false;
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
expect(events).toEqual([]);
|
||||||
|
|
||||||
|
fixture.componentInstance.val = 'b';
|
||||||
|
fixture.componentInstance.show = true;
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
expect(events).toEqual([{
|
||||||
|
name: 'comp',
|
||||||
|
changes: {
|
||||||
|
val: new SimpleChange(undefined, 'b', true),
|
||||||
|
}
|
||||||
|
}]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should call onChanges in hosts before their content children', () => {
|
||||||
|
const events: any[] = [];
|
||||||
|
@Component({
|
||||||
|
selector: 'projected',
|
||||||
|
template: `<p>{{val}}</p>`,
|
||||||
|
})
|
||||||
|
class Projected {
|
||||||
|
@Input()
|
||||||
|
val = '';
|
||||||
|
|
||||||
|
ngOnChanges(changes: SimpleChanges) { events.push({name: 'projected', changes}); }
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'comp',
|
||||||
|
template: `<div><ng-content></ng-content></div>`,
|
||||||
|
})
|
||||||
|
class Comp {
|
||||||
|
@Input()
|
||||||
|
val = '';
|
||||||
|
|
||||||
|
ngOnChanges(changes: SimpleChanges) { events.push({name: 'comp', changes}); }
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
template: `<comp [val]="val"><projected [val]="val"></projected></comp>`,
|
||||||
|
})
|
||||||
|
class App {
|
||||||
|
val = 'a';
|
||||||
|
}
|
||||||
|
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
declarations: [App, Comp, Projected],
|
||||||
|
});
|
||||||
|
const fixture = TestBed.createComponent(App);
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
expect(events).toEqual([
|
||||||
|
{
|
||||||
|
name: 'comp',
|
||||||
|
changes: {
|
||||||
|
val: new SimpleChange(undefined, 'a', true),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'projected',
|
||||||
|
changes: {
|
||||||
|
val: new SimpleChange(undefined, 'a', true),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
|
events.length = 0;
|
||||||
|
fixture.componentInstance.val = 'b';
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
expect(events).toEqual([
|
||||||
|
{
|
||||||
|
name: 'comp',
|
||||||
|
changes: {
|
||||||
|
val: new SimpleChange('a', 'b', false),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'projected',
|
||||||
|
changes: {
|
||||||
|
val: new SimpleChange('a', 'b', false),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should call onChanges in host and its content children before next host', () => {
|
||||||
|
const events: any[] = [];
|
||||||
|
@Component({
|
||||||
|
selector: 'projected',
|
||||||
|
template: `<p>{{val}}</p>`,
|
||||||
|
})
|
||||||
|
class Projected {
|
||||||
|
@Input()
|
||||||
|
val = '';
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
name = '';
|
||||||
|
|
||||||
|
ngOnChanges(changes: SimpleChanges) {
|
||||||
|
events.push({name: 'projected ' + this.name, changes});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'comp',
|
||||||
|
template: `<div><ng-content></ng-content></div>`,
|
||||||
|
})
|
||||||
|
class Comp {
|
||||||
|
@Input()
|
||||||
|
val = '';
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
name = '';
|
||||||
|
|
||||||
|
ngOnChanges(changes: SimpleChanges) { events.push({name: 'comp ' + this.name, changes}); }
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
template: `
|
||||||
|
<comp name="1" [val]="val">
|
||||||
|
<projected name="1" [val]="val"></projected>
|
||||||
|
</comp>
|
||||||
|
<comp name="2" [val]="val">
|
||||||
|
<projected name="2" [val]="val"></projected>
|
||||||
|
</comp>
|
||||||
|
`,
|
||||||
|
})
|
||||||
|
class App {
|
||||||
|
val = 'a';
|
||||||
|
}
|
||||||
|
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
declarations: [App, Comp, Projected],
|
||||||
|
});
|
||||||
|
const fixture = TestBed.createComponent(App);
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
expect(events).toEqual([
|
||||||
|
{
|
||||||
|
name: 'comp 1',
|
||||||
|
changes: {
|
||||||
|
name: new SimpleChange(undefined, '1', true),
|
||||||
|
val: new SimpleChange(undefined, 'a', true),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'projected 1',
|
||||||
|
changes: {
|
||||||
|
name: new SimpleChange(undefined, '1', true),
|
||||||
|
val: new SimpleChange(undefined, 'a', true),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'comp 2',
|
||||||
|
changes: {
|
||||||
|
name: new SimpleChange(undefined, '2', true),
|
||||||
|
val: new SimpleChange(undefined, 'a', true),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'projected 2',
|
||||||
|
changes: {
|
||||||
|
name: new SimpleChange(undefined, '2', true),
|
||||||
|
val: new SimpleChange(undefined, 'a', true),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
|
events.length = 0;
|
||||||
|
fixture.componentInstance.val = 'b';
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
expect(events).toEqual([
|
||||||
|
{
|
||||||
|
name: 'comp 1',
|
||||||
|
changes: {
|
||||||
|
val: new SimpleChange('a', 'b', false),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'projected 1',
|
||||||
|
changes: {
|
||||||
|
val: new SimpleChange('a', 'b', false),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'comp 2',
|
||||||
|
changes: {
|
||||||
|
val: new SimpleChange('a', 'b', false),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'projected 2',
|
||||||
|
changes: {
|
||||||
|
val: new SimpleChange('a', 'b', false),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be called on directives after component', () => {
|
||||||
|
const events: any[] = [];
|
||||||
|
|
||||||
|
@Directive({
|
||||||
|
selector: '[dir]',
|
||||||
|
})
|
||||||
|
class Dir {
|
||||||
|
@Input()
|
||||||
|
dir = '';
|
||||||
|
|
||||||
|
ngOnChanges(changes: SimpleChanges) { events.push({name: 'dir', changes}); }
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'comp',
|
||||||
|
template: `<p>{{val}}</p>`,
|
||||||
|
})
|
||||||
|
class Comp {
|
||||||
|
@Input()
|
||||||
|
val = '';
|
||||||
|
|
||||||
|
ngOnChanges(changes: SimpleChanges) { events.push({name: 'comp', changes}); }
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
template: `<comp [dir]="val" [val]="val"></comp>`,
|
||||||
|
})
|
||||||
|
class App {
|
||||||
|
val = 'a';
|
||||||
|
}
|
||||||
|
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
declarations: [App, Comp, Dir],
|
||||||
|
});
|
||||||
|
const fixture = TestBed.createComponent(App);
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
expect(events).toEqual([
|
||||||
|
{
|
||||||
|
name: 'comp',
|
||||||
|
changes: {
|
||||||
|
val: new SimpleChange(undefined, 'a', true),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'dir',
|
||||||
|
changes: {
|
||||||
|
dir: new SimpleChange(undefined, 'a', true),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]);
|
||||||
|
|
||||||
|
events.length = 0;
|
||||||
|
fixture.componentInstance.val = 'b';
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
expect(events).toEqual([
|
||||||
|
{
|
||||||
|
name: 'comp',
|
||||||
|
changes: {
|
||||||
|
val: new SimpleChange('a', 'b', false),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'dir',
|
||||||
|
changes: {
|
||||||
|
dir: new SimpleChange('a', 'b', false),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be called on directives on an element', () => {
|
||||||
|
const events: any[] = [];
|
||||||
|
|
||||||
|
@Directive({
|
||||||
|
selector: '[dir]',
|
||||||
|
})
|
||||||
|
class Dir {
|
||||||
|
@Input()
|
||||||
|
dir = '';
|
||||||
|
|
||||||
|
@Input('dir-val')
|
||||||
|
val = '';
|
||||||
|
|
||||||
|
ngOnChanges(changes: SimpleChanges) { events.push({name: 'dir', changes}); }
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component({template: `<div [dir]="val1" [dir-val]="val2"></div>`})
|
||||||
|
class App {
|
||||||
|
val1 = 'a';
|
||||||
|
val2 = 'b';
|
||||||
|
}
|
||||||
|
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
declarations: [App, Dir],
|
||||||
|
});
|
||||||
|
const fixture = TestBed.createComponent(App);
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
expect(events).toEqual([{
|
||||||
|
name: 'dir',
|
||||||
|
changes: {
|
||||||
|
dir: new SimpleChange(undefined, 'a', true),
|
||||||
|
val: new SimpleChange(undefined, 'b', true),
|
||||||
|
}
|
||||||
|
}]);
|
||||||
|
|
||||||
|
events.length = 0;
|
||||||
|
fixture.componentInstance.val1 = 'a1';
|
||||||
|
fixture.componentInstance.val2 = 'b1';
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(events).toEqual([{
|
||||||
|
name: 'dir',
|
||||||
|
changes: {
|
||||||
|
dir: new SimpleChange('a', 'a1', false),
|
||||||
|
val: new SimpleChange('b', 'b1', false),
|
||||||
|
}
|
||||||
|
}]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should call onChanges properly in for loop', () => {
|
||||||
|
const events: any[] = [];
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'comp',
|
||||||
|
template: `<p>{{val}}</p>`,
|
||||||
|
})
|
||||||
|
class Comp {
|
||||||
|
@Input()
|
||||||
|
val = '';
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
name = '';
|
||||||
|
|
||||||
|
ngOnChanges(changes: SimpleChanges) { events.push({name: 'comp ' + this.name, changes}); }
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
template: `
|
||||||
|
<comp name="0" [val]="val"></comp>
|
||||||
|
<comp *ngFor="let number of numbers" [name]="number" [val]="val"></comp>
|
||||||
|
<comp name="1" [val]="val"></comp>
|
||||||
|
`
|
||||||
|
})
|
||||||
|
class App {
|
||||||
|
val = 'a';
|
||||||
|
|
||||||
|
numbers = ['2', '3', '4'];
|
||||||
|
}
|
||||||
|
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
declarations: [App, Comp],
|
||||||
|
imports: [CommonModule],
|
||||||
|
});
|
||||||
|
const fixture = TestBed.createComponent(App);
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
expect(events).toEqual([
|
||||||
|
{
|
||||||
|
name: 'comp 0',
|
||||||
|
changes: {
|
||||||
|
name: new SimpleChange(undefined, '0', true),
|
||||||
|
val: new SimpleChange(undefined, 'a', true),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'comp 1',
|
||||||
|
changes: {
|
||||||
|
name: new SimpleChange(undefined, '1', true),
|
||||||
|
val: new SimpleChange(undefined, 'a', true),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'comp 2',
|
||||||
|
changes: {
|
||||||
|
name: new SimpleChange(undefined, '2', true),
|
||||||
|
val: new SimpleChange(undefined, 'a', true),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'comp 3',
|
||||||
|
changes: {
|
||||||
|
name: new SimpleChange(undefined, '3', true),
|
||||||
|
val: new SimpleChange(undefined, 'a', true),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'comp 4',
|
||||||
|
changes: {
|
||||||
|
name: new SimpleChange(undefined, '4', true),
|
||||||
|
val: new SimpleChange(undefined, 'a', true),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]);
|
||||||
|
|
||||||
|
events.length = 0;
|
||||||
|
fixture.componentInstance.val = 'b';
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
expect(events).toEqual([
|
||||||
|
{
|
||||||
|
name: 'comp 0',
|
||||||
|
changes: {
|
||||||
|
val: new SimpleChange('a', 'b', false),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'comp 1',
|
||||||
|
changes: {
|
||||||
|
val: new SimpleChange('a', 'b', false),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'comp 2',
|
||||||
|
changes: {
|
||||||
|
val: new SimpleChange('a', 'b', false),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'comp 3',
|
||||||
|
changes: {
|
||||||
|
val: new SimpleChange('a', 'b', false),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'comp 4',
|
||||||
|
changes: {
|
||||||
|
val: new SimpleChange('a', 'b', false),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should call onChanges properly in for loop with children', () => {
|
||||||
|
const events: any[] = [];
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'child',
|
||||||
|
template: `<p>{{val}}</p>`,
|
||||||
|
})
|
||||||
|
class Child {
|
||||||
|
@Input()
|
||||||
|
val = '';
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
name = '';
|
||||||
|
|
||||||
|
ngOnChanges(changes: SimpleChanges) {
|
||||||
|
events.push({name: 'child of parent ' + this.name, changes});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'parent',
|
||||||
|
template: `<child [name]="name" [val]="val"></child>`,
|
||||||
|
})
|
||||||
|
class Parent {
|
||||||
|
@Input()
|
||||||
|
val = '';
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
name = '';
|
||||||
|
|
||||||
|
ngOnChanges(changes: SimpleChanges) { events.push({name: 'parent ' + this.name, changes}); }
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
template: `
|
||||||
|
<parent name="0" [val]="val"></parent>
|
||||||
|
<parent *ngFor="let number of numbers" [name]="number" [val]="val"></parent>
|
||||||
|
<parent name="1" [val]="val"></parent>
|
||||||
|
`
|
||||||
|
})
|
||||||
|
class App {
|
||||||
|
val = 'a';
|
||||||
|
numbers = ['2', '3', '4'];
|
||||||
|
}
|
||||||
|
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
declarations: [App, Child, Parent],
|
||||||
|
imports: [CommonModule],
|
||||||
|
});
|
||||||
|
const fixture = TestBed.createComponent(App);
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
expect(events).toEqual([
|
||||||
|
{
|
||||||
|
name: 'parent 0',
|
||||||
|
changes: {
|
||||||
|
name: new SimpleChange(undefined, '0', true),
|
||||||
|
val: new SimpleChange(undefined, 'a', true),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'parent 1',
|
||||||
|
changes: {
|
||||||
|
name: new SimpleChange(undefined, '1', true),
|
||||||
|
val: new SimpleChange(undefined, 'a', true),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'parent 2',
|
||||||
|
changes: {
|
||||||
|
name: new SimpleChange(undefined, '2', true),
|
||||||
|
val: new SimpleChange(undefined, 'a', true),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'child of parent 2',
|
||||||
|
changes: {
|
||||||
|
name: new SimpleChange(undefined, '2', true),
|
||||||
|
val: new SimpleChange(undefined, 'a', true),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'parent 3',
|
||||||
|
changes: {
|
||||||
|
name: new SimpleChange(undefined, '3', true),
|
||||||
|
val: new SimpleChange(undefined, 'a', true),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'child of parent 3',
|
||||||
|
changes: {
|
||||||
|
name: new SimpleChange(undefined, '3', true),
|
||||||
|
val: new SimpleChange(undefined, 'a', true),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'parent 4',
|
||||||
|
changes: {
|
||||||
|
name: new SimpleChange(undefined, '4', true),
|
||||||
|
val: new SimpleChange(undefined, 'a', true),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'child of parent 4',
|
||||||
|
changes: {
|
||||||
|
name: new SimpleChange(undefined, '4', true),
|
||||||
|
val: new SimpleChange(undefined, 'a', true),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'child of parent 0',
|
||||||
|
changes: {
|
||||||
|
name: new SimpleChange(undefined, '0', true),
|
||||||
|
val: new SimpleChange(undefined, 'a', true),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'child of parent 1',
|
||||||
|
changes: {
|
||||||
|
name: new SimpleChange(undefined, '1', true),
|
||||||
|
val: new SimpleChange(undefined, 'a', true),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
|
events.length = 0;
|
||||||
|
fixture.componentInstance.val = 'b';
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
expect(events).toEqual([
|
||||||
|
{
|
||||||
|
name: 'parent 0',
|
||||||
|
changes: {
|
||||||
|
val: new SimpleChange('a', 'b', false),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'parent 1',
|
||||||
|
changes: {
|
||||||
|
val: new SimpleChange('a', 'b', false),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'parent 2',
|
||||||
|
changes: {
|
||||||
|
val: new SimpleChange('a', 'b', false),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'child of parent 2',
|
||||||
|
changes: {
|
||||||
|
val: new SimpleChange('a', 'b', false),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'parent 3',
|
||||||
|
changes: {
|
||||||
|
val: new SimpleChange('a', 'b', false),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'child of parent 3',
|
||||||
|
changes: {
|
||||||
|
val: new SimpleChange('a', 'b', false),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'parent 4',
|
||||||
|
changes: {
|
||||||
|
val: new SimpleChange('a', 'b', false),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'child of parent 4',
|
||||||
|
changes: {
|
||||||
|
val: new SimpleChange('a', 'b', false),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'child of parent 0',
|
||||||
|
changes: {
|
||||||
|
val: new SimpleChange('a', 'b', false),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'child of parent 1',
|
||||||
|
changes: {
|
||||||
|
val: new SimpleChange('a', 'b', false),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not call onChanges if props are set directly', () => {
|
||||||
|
const events: any[] = [];
|
||||||
|
|
||||||
|
@Component({template: `<p>{{value}}</p>`})
|
||||||
|
class App {
|
||||||
|
value = 'a';
|
||||||
|
ngOnChanges(changes: SimpleChanges) { events.push(changes); }
|
||||||
|
}
|
||||||
|
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
declarations: [App],
|
||||||
|
});
|
||||||
|
const fixture = TestBed.createComponent(App);
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
expect(events).toEqual([]);
|
||||||
|
|
||||||
|
fixture.componentInstance.value = 'b';
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
expect(events).toEqual([]);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should call all hooks in correct order when several directives on same node', () => {
|
it('should call all hooks in correct order when several directives on same node', () => {
|
||||||
|
@ -116,774 +116,6 @@ describe('lifecycles', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('onChanges', () => {
|
|
||||||
let events: ({type: string, name: string, [key: string]: any})[];
|
|
||||||
|
|
||||||
beforeEach(() => { events = []; });
|
|
||||||
|
|
||||||
/**
|
|
||||||
* <div>
|
|
||||||
* <ng-content/>
|
|
||||||
* </div>
|
|
||||||
*/
|
|
||||||
const Comp = createOnChangesComponent('comp', (rf: RenderFlags, ctx: any) => {
|
|
||||||
if (rf & RenderFlags.Create) {
|
|
||||||
ΔprojectionDef();
|
|
||||||
ΔelementStart(0, 'div');
|
|
||||||
{ Δprojection(1); }
|
|
||||||
ΔelementEnd();
|
|
||||||
}
|
|
||||||
}, 2);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* <comp [val1]="a" [publicVal2]="b"/>
|
|
||||||
*/
|
|
||||||
const Parent = createOnChangesComponent('parent', (rf: RenderFlags, ctx: any) => {
|
|
||||||
if (rf & RenderFlags.Create) {
|
|
||||||
Δelement(0, 'comp');
|
|
||||||
}
|
|
||||||
if (rf & RenderFlags.Update) {
|
|
||||||
ΔelementProperty(0, 'val1', Δbind(ctx.a));
|
|
||||||
ΔelementProperty(0, 'publicVal2', Δbind(ctx.b));
|
|
||||||
}
|
|
||||||
}, 1, 2, [Comp]);
|
|
||||||
|
|
||||||
const ProjectedComp = createOnChangesComponent('projected', (rf: RenderFlags, ctx: any) => {
|
|
||||||
if (rf & RenderFlags.Create) {
|
|
||||||
Δtext(0, 'content');
|
|
||||||
}
|
|
||||||
}, 1);
|
|
||||||
|
|
||||||
|
|
||||||
function createOnChangesComponent(
|
|
||||||
name: string, template: ComponentTemplate<any>, consts: number = 0, vars: number = 0,
|
|
||||||
directives: any[] = []) {
|
|
||||||
return class Component {
|
|
||||||
// @Input() val1: string;
|
|
||||||
// @Input('publicVal2') val2: string;
|
|
||||||
a: string = 'wasVal1BeforeMinification';
|
|
||||||
b: string = 'wasVal2BeforeMinification';
|
|
||||||
ngOnChanges(changes: SimpleChanges) {
|
|
||||||
if (changes.a && this.a !== changes.a.currentValue) {
|
|
||||||
throw Error(
|
|
||||||
`SimpleChanges invalid expected this.a ${this.a} to equal currentValue ${changes.a.currentValue}`);
|
|
||||||
}
|
|
||||||
if (changes.b && this.b !== changes.b.currentValue) {
|
|
||||||
throw Error(
|
|
||||||
`SimpleChanges invalid expected this.b ${this.b} to equal currentValue ${changes.b.currentValue}`);
|
|
||||||
}
|
|
||||||
events.push({type: 'onChanges', name: 'comp - ' + name, changes});
|
|
||||||
}
|
|
||||||
|
|
||||||
static ngComponentDef = ΔdefineComponent({
|
|
||||||
type: Component,
|
|
||||||
selectors: [[name]],
|
|
||||||
factory: () => new Component(),
|
|
||||||
consts: consts,
|
|
||||||
vars: vars,
|
|
||||||
inputs: {a: 'val1', b: ['publicVal2', 'val2']}, template,
|
|
||||||
directives: directives,
|
|
||||||
features: [ΔNgOnChangesFeature()],
|
|
||||||
});
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
class Directive {
|
|
||||||
// @Input() val1: string;
|
|
||||||
// @Input('publicVal2') val2: string;
|
|
||||||
a: string = 'wasVal1BeforeMinification';
|
|
||||||
b: string = 'wasVal2BeforeMinification';
|
|
||||||
ngOnChanges(changes: SimpleChanges) {
|
|
||||||
events.push({type: 'onChanges', name: 'dir - dir', changes});
|
|
||||||
}
|
|
||||||
|
|
||||||
static ngDirectiveDef = ΔdefineDirective({
|
|
||||||
type: Directive,
|
|
||||||
selectors: [['', 'dir', '']],
|
|
||||||
factory: () => new Directive(),
|
|
||||||
inputs: {a: 'val1', b: ['publicVal2', 'val2']},
|
|
||||||
features: [ΔNgOnChangesFeature()],
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const defs = [Comp, Parent, Directive, ProjectedComp];
|
|
||||||
|
|
||||||
it('should call onChanges method after inputs are set in creation and update mode', () => {
|
|
||||||
/** <comp [val1]="val1" [publicVal2]="val2"></comp> */
|
|
||||||
const App = createComponent('app', function(rf: RenderFlags, ctx: any) {
|
|
||||||
if (rf & RenderFlags.Create) {
|
|
||||||
Δelement(0, 'comp');
|
|
||||||
}
|
|
||||||
if (rf & RenderFlags.Update) {
|
|
||||||
ΔelementProperty(0, 'val1', Δbind(ctx.val1));
|
|
||||||
ΔelementProperty(0, 'publicVal2', Δbind(ctx.val2));
|
|
||||||
}
|
|
||||||
}, 1, 2, defs);
|
|
||||||
|
|
||||||
// First changes happen here.
|
|
||||||
const fixture = new ComponentFixture(App);
|
|
||||||
|
|
||||||
events = [];
|
|
||||||
fixture.component.val1 = '1';
|
|
||||||
fixture.component.val2 = 'a';
|
|
||||||
fixture.update();
|
|
||||||
expect(events).toEqual([{
|
|
||||||
type: 'onChanges',
|
|
||||||
name: 'comp - comp',
|
|
||||||
changes: {
|
|
||||||
'val1': new SimpleChange(
|
|
||||||
undefined, '1', false), // we cleared `events` above, this is the second change
|
|
||||||
'val2': new SimpleChange(undefined, 'a', false),
|
|
||||||
}
|
|
||||||
}]);
|
|
||||||
|
|
||||||
events = [];
|
|
||||||
fixture.component.val1 = '2';
|
|
||||||
fixture.component.val2 = 'b';
|
|
||||||
fixture.update();
|
|
||||||
expect(events).toEqual([{
|
|
||||||
type: 'onChanges',
|
|
||||||
name: 'comp - comp',
|
|
||||||
changes: {
|
|
||||||
'val1': new SimpleChange('1', '2', false),
|
|
||||||
'val2': new SimpleChange('a', 'b', false),
|
|
||||||
}
|
|
||||||
}]);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should call parent onChanges before child onChanges', () => {
|
|
||||||
/**
|
|
||||||
* <parent></parent>
|
|
||||||
* parent temp: <comp></comp>
|
|
||||||
*/
|
|
||||||
const App = createComponent('app', function(rf: RenderFlags, ctx: any) {
|
|
||||||
if (rf & RenderFlags.Create) {
|
|
||||||
Δelement(0, 'parent');
|
|
||||||
}
|
|
||||||
if (rf & RenderFlags.Update) {
|
|
||||||
ΔelementProperty(0, 'val1', Δbind(ctx.val1));
|
|
||||||
ΔelementProperty(0, 'publicVal2', Δbind(ctx.val2));
|
|
||||||
}
|
|
||||||
}, 1, 2, defs);
|
|
||||||
|
|
||||||
const fixture = new ComponentFixture(App);
|
|
||||||
|
|
||||||
// We're clearing events after the first change here
|
|
||||||
events = [];
|
|
||||||
fixture.component.val1 = '1';
|
|
||||||
fixture.component.val2 = 'a';
|
|
||||||
fixture.update();
|
|
||||||
|
|
||||||
expect(events).toEqual([
|
|
||||||
{
|
|
||||||
type: 'onChanges',
|
|
||||||
name: 'comp - parent',
|
|
||||||
changes: {
|
|
||||||
'val1': new SimpleChange(undefined, '1', false),
|
|
||||||
'val2': new SimpleChange(undefined, 'a', false),
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'onChanges',
|
|
||||||
name: 'comp - comp',
|
|
||||||
changes: {
|
|
||||||
'val1': new SimpleChange(undefined, '1', false),
|
|
||||||
'val2': new SimpleChange(undefined, 'a', false),
|
|
||||||
}
|
|
||||||
},
|
|
||||||
]);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should call all parent onChanges across view before calling children onChanges', () => {
|
|
||||||
/**
|
|
||||||
* <parent [val1]="1"></parent>
|
|
||||||
* <parent [val1]="2"></parent>
|
|
||||||
*/
|
|
||||||
|
|
||||||
const App = createComponent('app', function(rf: RenderFlags, ctx: any) {
|
|
||||||
if (rf & RenderFlags.Create) {
|
|
||||||
Δelement(0, 'parent');
|
|
||||||
Δelement(1, 'parent');
|
|
||||||
}
|
|
||||||
if (rf & RenderFlags.Update) {
|
|
||||||
ΔelementProperty(0, 'val1', Δbind(1));
|
|
||||||
ΔelementProperty(0, 'publicVal2', Δbind(1));
|
|
||||||
Δselect(1);
|
|
||||||
ΔelementProperty(1, 'val1', Δbind(2));
|
|
||||||
ΔelementProperty(1, 'publicVal2', Δbind(2));
|
|
||||||
}
|
|
||||||
}, 2, 4, defs);
|
|
||||||
|
|
||||||
const fixture = new ComponentFixture(App);
|
|
||||||
expect(events).toEqual([
|
|
||||||
{
|
|
||||||
type: 'onChanges',
|
|
||||||
name: 'comp - parent',
|
|
||||||
changes: {
|
|
||||||
'val1': new SimpleChange(undefined, 1, true),
|
|
||||||
'val2': new SimpleChange(undefined, 1, true),
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'onChanges',
|
|
||||||
name: 'comp - parent',
|
|
||||||
changes: {
|
|
||||||
'val1': new SimpleChange(undefined, 2, true),
|
|
||||||
'val2': new SimpleChange(undefined, 2, true),
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'onChanges',
|
|
||||||
name: 'comp - comp',
|
|
||||||
changes: {
|
|
||||||
'val1': new SimpleChange(undefined, 1, true),
|
|
||||||
'val2': new SimpleChange(undefined, 1, true),
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'onChanges',
|
|
||||||
name: 'comp - comp',
|
|
||||||
changes: {
|
|
||||||
'val1': new SimpleChange(undefined, 2, true),
|
|
||||||
'val2': new SimpleChange(undefined, 2, true),
|
|
||||||
}
|
|
||||||
},
|
|
||||||
]);
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
it('should call onChanges every time a new view is created (if block)', () => {
|
|
||||||
/**
|
|
||||||
* % if (condition) {
|
|
||||||
* <comp></comp>
|
|
||||||
* % }
|
|
||||||
*/
|
|
||||||
|
|
||||||
const App = createComponent('app', function(rf: RenderFlags, ctx: any) {
|
|
||||||
if (rf & RenderFlags.Create) {
|
|
||||||
Δcontainer(0);
|
|
||||||
}
|
|
||||||
if (rf & RenderFlags.Update) {
|
|
||||||
ΔcontainerRefreshStart(0);
|
|
||||||
{
|
|
||||||
if (ctx.condition) {
|
|
||||||
let rf1 = ΔembeddedViewStart(0, 1, 2);
|
|
||||||
if (rf1 & RenderFlags.Create) {
|
|
||||||
Δelement(0, 'comp');
|
|
||||||
}
|
|
||||||
if (rf1 & RenderFlags.Update) {
|
|
||||||
ΔelementProperty(0, 'val1', Δbind(1));
|
|
||||||
ΔelementProperty(0, 'publicVal2', Δbind(1));
|
|
||||||
}
|
|
||||||
ΔembeddedViewEnd();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ΔcontainerRefreshEnd();
|
|
||||||
}
|
|
||||||
}, 1, 0, defs);
|
|
||||||
|
|
||||||
const fixture = new ComponentFixture(App);
|
|
||||||
|
|
||||||
// Show the `comp` component, causing it to initialize. (first change is true)
|
|
||||||
fixture.component.condition = true;
|
|
||||||
fixture.update();
|
|
||||||
expect(events).toEqual([{
|
|
||||||
type: 'onChanges',
|
|
||||||
name: 'comp - comp',
|
|
||||||
changes: {
|
|
||||||
'val1': new SimpleChange(undefined, 1, true),
|
|
||||||
'val2': new SimpleChange(undefined, 1, true),
|
|
||||||
}
|
|
||||||
}]);
|
|
||||||
|
|
||||||
// Hide the `comp` component, no onChanges should fire
|
|
||||||
fixture.component.condition = false;
|
|
||||||
fixture.update();
|
|
||||||
expect(events).toEqual([{
|
|
||||||
type: 'onChanges',
|
|
||||||
name: 'comp - comp',
|
|
||||||
changes: {
|
|
||||||
'val1': new SimpleChange(undefined, 1, true),
|
|
||||||
'val2': new SimpleChange(undefined, 1, true),
|
|
||||||
}
|
|
||||||
}]);
|
|
||||||
|
|
||||||
// Show the `comp` component, it initializes again. (first change is true)
|
|
||||||
fixture.component.condition = true;
|
|
||||||
fixture.update();
|
|
||||||
expect(events).toEqual([
|
|
||||||
{
|
|
||||||
type: 'onChanges',
|
|
||||||
name: 'comp - comp',
|
|
||||||
changes: {
|
|
||||||
'val1': new SimpleChange(undefined, 1, true),
|
|
||||||
'val2': new SimpleChange(undefined, 1, true),
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'onChanges',
|
|
||||||
name: 'comp - comp',
|
|
||||||
changes: {
|
|
||||||
'val1': new SimpleChange(undefined, 1, true),
|
|
||||||
'val2': new SimpleChange(undefined, 1, true),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should call onChanges in hosts before their content children', () => {
|
|
||||||
/**
|
|
||||||
* <comp>
|
|
||||||
* <projected></projected>
|
|
||||||
* </comp>
|
|
||||||
*/
|
|
||||||
const App = createComponent('app', function(rf: RenderFlags, ctx: any) {
|
|
||||||
if (rf & RenderFlags.Create) {
|
|
||||||
ΔelementStart(0, 'comp');
|
|
||||||
{ ΔelementStart(1, 'projected'); }
|
|
||||||
ΔelementEnd();
|
|
||||||
}
|
|
||||||
if (rf & RenderFlags.Update) {
|
|
||||||
ΔelementProperty(0, 'val1', Δbind(1));
|
|
||||||
ΔelementProperty(0, 'publicVal2', Δbind(1));
|
|
||||||
Δselect(1);
|
|
||||||
ΔelementProperty(1, 'val1', Δbind(2));
|
|
||||||
ΔelementProperty(1, 'publicVal2', Δbind(2));
|
|
||||||
}
|
|
||||||
}, 2, 4, defs);
|
|
||||||
|
|
||||||
const fixture = new ComponentFixture(App);
|
|
||||||
expect(events).toEqual([
|
|
||||||
{
|
|
||||||
type: 'onChanges',
|
|
||||||
name: 'comp - comp',
|
|
||||||
changes: {
|
|
||||||
'val1': new SimpleChange(undefined, 1, true),
|
|
||||||
'val2': new SimpleChange(undefined, 1, true),
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'onChanges',
|
|
||||||
name: 'comp - projected',
|
|
||||||
changes: {
|
|
||||||
'val1': new SimpleChange(undefined, 2, true),
|
|
||||||
'val2': new SimpleChange(undefined, 2, true),
|
|
||||||
}
|
|
||||||
},
|
|
||||||
]);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should call onChanges in host and its content children before next host', () => {
|
|
||||||
/**
|
|
||||||
* <comp [val1]="1" [publicVal2]="1">
|
|
||||||
* <projected [val1]="2" [publicVal2]="2"></projected>
|
|
||||||
* </comp>
|
|
||||||
* <comp [val1]="3" [publicVal2]="3">
|
|
||||||
* <projected [val1]="4" [publicVal2]="4"></projected>
|
|
||||||
* </comp>
|
|
||||||
*/
|
|
||||||
const App = createComponent('app', function(rf: RenderFlags, ctx: any) {
|
|
||||||
if (rf & RenderFlags.Create) {
|
|
||||||
ΔelementStart(0, 'comp');
|
|
||||||
{ ΔelementStart(1, 'projected'); }
|
|
||||||
ΔelementEnd();
|
|
||||||
ΔelementStart(2, 'comp');
|
|
||||||
{ ΔelementStart(3, 'projected'); }
|
|
||||||
ΔelementEnd();
|
|
||||||
}
|
|
||||||
if (rf & RenderFlags.Update) {
|
|
||||||
ΔelementProperty(0, 'val1', Δbind(1));
|
|
||||||
ΔelementProperty(0, 'publicVal2', Δbind(1));
|
|
||||||
Δselect(1);
|
|
||||||
ΔelementProperty(1, 'val1', Δbind(2));
|
|
||||||
ΔelementProperty(1, 'publicVal2', Δbind(2));
|
|
||||||
Δselect(2);
|
|
||||||
ΔelementProperty(2, 'val1', Δbind(3));
|
|
||||||
ΔelementProperty(2, 'publicVal2', Δbind(3));
|
|
||||||
Δselect(3);
|
|
||||||
ΔelementProperty(3, 'val1', Δbind(4));
|
|
||||||
ΔelementProperty(3, 'publicVal2', Δbind(4));
|
|
||||||
}
|
|
||||||
}, 4, 8, defs);
|
|
||||||
|
|
||||||
const fixture = new ComponentFixture(App);
|
|
||||||
expect(events).toEqual([
|
|
||||||
{
|
|
||||||
type: 'onChanges',
|
|
||||||
name: 'comp - comp',
|
|
||||||
changes: {
|
|
||||||
'val1': new SimpleChange(undefined, 1, true),
|
|
||||||
'val2': new SimpleChange(undefined, 1, true),
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'onChanges',
|
|
||||||
name: 'comp - projected',
|
|
||||||
changes: {
|
|
||||||
'val1': new SimpleChange(undefined, 2, true),
|
|
||||||
'val2': new SimpleChange(undefined, 2, true),
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'onChanges',
|
|
||||||
name: 'comp - comp',
|
|
||||||
changes: {
|
|
||||||
'val1': new SimpleChange(undefined, 3, true),
|
|
||||||
'val2': new SimpleChange(undefined, 3, true),
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'onChanges',
|
|
||||||
name: 'comp - projected',
|
|
||||||
changes: {
|
|
||||||
'val1': new SimpleChange(undefined, 4, true),
|
|
||||||
'val2': new SimpleChange(undefined, 4, true),
|
|
||||||
}
|
|
||||||
},
|
|
||||||
]);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should be called on directives after component', () => {
|
|
||||||
/**
|
|
||||||
* <comp dir [val1]="1" [publicVal2]="1"></comp>
|
|
||||||
*/
|
|
||||||
const App = createComponent('app', function(rf: RenderFlags, ctx: any) {
|
|
||||||
if (rf & RenderFlags.Create) {
|
|
||||||
Δelement(0, 'comp', ['dir', '']);
|
|
||||||
}
|
|
||||||
if (rf & RenderFlags.Update) {
|
|
||||||
ΔelementProperty(0, 'val1', Δbind(1));
|
|
||||||
ΔelementProperty(0, 'publicVal2', Δbind(1));
|
|
||||||
}
|
|
||||||
}, 1, 2, defs);
|
|
||||||
|
|
||||||
const fixture = new ComponentFixture(App);
|
|
||||||
expect(events).toEqual([
|
|
||||||
{
|
|
||||||
type: 'onChanges',
|
|
||||||
name: 'comp - comp',
|
|
||||||
changes: {
|
|
||||||
'val1': new SimpleChange(undefined, 1, true),
|
|
||||||
'val2': new SimpleChange(undefined, 1, true),
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'onChanges',
|
|
||||||
name: 'dir - dir',
|
|
||||||
changes: {
|
|
||||||
'val1': new SimpleChange(undefined, 1, true),
|
|
||||||
'val2': new SimpleChange(undefined, 1, true),
|
|
||||||
}
|
|
||||||
},
|
|
||||||
]);
|
|
||||||
|
|
||||||
// Update causes no changes to be fired, since the bindings didn't change.
|
|
||||||
events = [];
|
|
||||||
fixture.update();
|
|
||||||
expect(events).toEqual([]);
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should be called on directives on an element', () => {
|
|
||||||
/**
|
|
||||||
* <div dir [val]="1" [publicVal2]="1"></div>
|
|
||||||
*/
|
|
||||||
const App = createComponent('app', function(rf: RenderFlags, ctx: any) {
|
|
||||||
if (rf & RenderFlags.Create) {
|
|
||||||
Δelement(0, 'div', ['dir', '']);
|
|
||||||
}
|
|
||||||
if (rf & RenderFlags.Update) {
|
|
||||||
ΔelementProperty(0, 'val1', Δbind(1));
|
|
||||||
ΔelementProperty(0, 'publicVal2', Δbind(1));
|
|
||||||
}
|
|
||||||
}, 1, 2, defs);
|
|
||||||
|
|
||||||
const fixture = new ComponentFixture(App);
|
|
||||||
expect(events).toEqual([{
|
|
||||||
type: 'onChanges',
|
|
||||||
name: 'dir - dir',
|
|
||||||
changes: {
|
|
||||||
'val1': new SimpleChange(undefined, 1, true),
|
|
||||||
'val2': new SimpleChange(undefined, 1, true),
|
|
||||||
}
|
|
||||||
}]);
|
|
||||||
|
|
||||||
events = [];
|
|
||||||
fixture.update();
|
|
||||||
expect(events).toEqual([]);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should call onChanges properly in for loop', () => {
|
|
||||||
/**
|
|
||||||
* <comp [val1]="1" [publicVal2]="1"></comp>
|
|
||||||
* % for (let j = 2; j < 5; j++) {
|
|
||||||
* <comp [val1]="j" [publicVal2]="j"></comp>
|
|
||||||
* % }
|
|
||||||
* <comp [val1]="5" [publicVal2]="5"></comp>
|
|
||||||
*/
|
|
||||||
|
|
||||||
const App = createComponent('app', function(rf: RenderFlags, ctx: any) {
|
|
||||||
if (rf & RenderFlags.Create) {
|
|
||||||
Δelement(0, 'comp');
|
|
||||||
Δcontainer(1);
|
|
||||||
Δelement(2, 'comp');
|
|
||||||
}
|
|
||||||
if (rf & RenderFlags.Update) {
|
|
||||||
ΔelementProperty(0, 'val1', Δbind(1));
|
|
||||||
ΔelementProperty(0, 'publicVal2', Δbind(1));
|
|
||||||
Δselect(2);
|
|
||||||
ΔelementProperty(2, 'val1', Δbind(5));
|
|
||||||
ΔelementProperty(2, 'publicVal2', Δbind(5));
|
|
||||||
ΔcontainerRefreshStart(1);
|
|
||||||
{
|
|
||||||
for (let j = 2; j < 5; j++) {
|
|
||||||
let rf1 = ΔembeddedViewStart(0, 1, 2);
|
|
||||||
if (rf1 & RenderFlags.Create) {
|
|
||||||
Δelement(0, 'comp');
|
|
||||||
}
|
|
||||||
if (rf1 & RenderFlags.Update) {
|
|
||||||
ΔelementProperty(0, 'val1', Δbind(j));
|
|
||||||
ΔelementProperty(0, 'publicVal2', Δbind(j));
|
|
||||||
}
|
|
||||||
ΔembeddedViewEnd();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ΔcontainerRefreshEnd();
|
|
||||||
}
|
|
||||||
}, 3, 4, defs);
|
|
||||||
|
|
||||||
const fixture = new ComponentFixture(App);
|
|
||||||
|
|
||||||
// onChanges is called top to bottom, so top level comps (1 and 5) are called
|
|
||||||
// before the comps inside the for loop's embedded view (2, 3, and 4)
|
|
||||||
expect(events).toEqual([
|
|
||||||
{
|
|
||||||
type: 'onChanges',
|
|
||||||
name: 'comp - comp',
|
|
||||||
changes: {
|
|
||||||
'val1': new SimpleChange(undefined, 1, true),
|
|
||||||
'val2': new SimpleChange(undefined, 1, true),
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'onChanges',
|
|
||||||
name: 'comp - comp',
|
|
||||||
changes: {
|
|
||||||
'val1': new SimpleChange(undefined, 5, true),
|
|
||||||
'val2': new SimpleChange(undefined, 5, true),
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'onChanges',
|
|
||||||
name: 'comp - comp',
|
|
||||||
changes: {
|
|
||||||
'val1': new SimpleChange(undefined, 2, true),
|
|
||||||
'val2': new SimpleChange(undefined, 2, true),
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'onChanges',
|
|
||||||
name: 'comp - comp',
|
|
||||||
changes: {
|
|
||||||
'val1': new SimpleChange(undefined, 3, true),
|
|
||||||
'val2': new SimpleChange(undefined, 3, true),
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'onChanges',
|
|
||||||
name: 'comp - comp',
|
|
||||||
changes: {
|
|
||||||
'val1': new SimpleChange(undefined, 4, true),
|
|
||||||
'val2': new SimpleChange(undefined, 4, true),
|
|
||||||
}
|
|
||||||
},
|
|
||||||
]);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should call onChanges properly in for loop with children', () => {
|
|
||||||
/**
|
|
||||||
* <parent [val1]="1" [publicVal2]="1"></parent>
|
|
||||||
* % for (let j = 2; j < 5; j++) {
|
|
||||||
* <parent [val1]="j" [publicVal2]="j"></parent>
|
|
||||||
* % }
|
|
||||||
* <parent [val1]="5" [publicVal2]="5"></parent>
|
|
||||||
*/
|
|
||||||
|
|
||||||
const App = createComponent('app', function(rf: RenderFlags, ctx: any) {
|
|
||||||
if (rf & RenderFlags.Create) {
|
|
||||||
Δelement(0, 'parent');
|
|
||||||
Δcontainer(1);
|
|
||||||
Δelement(2, 'parent');
|
|
||||||
}
|
|
||||||
if (rf & RenderFlags.Update) {
|
|
||||||
ΔelementProperty(0, 'val1', Δbind(1));
|
|
||||||
ΔelementProperty(0, 'publicVal2', Δbind(1));
|
|
||||||
Δselect(2);
|
|
||||||
ΔelementProperty(2, 'val1', Δbind(5));
|
|
||||||
ΔelementProperty(2, 'publicVal2', Δbind(5));
|
|
||||||
ΔcontainerRefreshStart(1);
|
|
||||||
{
|
|
||||||
for (let j = 2; j < 5; j++) {
|
|
||||||
let rf1 = ΔembeddedViewStart(0, 1, 2);
|
|
||||||
if (rf1 & RenderFlags.Create) {
|
|
||||||
Δelement(0, 'parent');
|
|
||||||
}
|
|
||||||
if (rf1 & RenderFlags.Update) {
|
|
||||||
ΔelementProperty(0, 'val1', Δbind(j));
|
|
||||||
ΔelementProperty(0, 'publicVal2', Δbind(j));
|
|
||||||
}
|
|
||||||
ΔembeddedViewEnd();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ΔcontainerRefreshEnd();
|
|
||||||
}
|
|
||||||
}, 3, 4, defs);
|
|
||||||
|
|
||||||
const fixture = new ComponentFixture(App);
|
|
||||||
|
|
||||||
// onChanges is called top to bottom, so top level comps (1 and 5) are called
|
|
||||||
// before the comps inside the for loop's embedded view (2, 3, and 4)
|
|
||||||
expect(events).toEqual([
|
|
||||||
{
|
|
||||||
type: 'onChanges',
|
|
||||||
name: 'comp - parent',
|
|
||||||
changes: {
|
|
||||||
'val1': new SimpleChange(undefined, 1, true),
|
|
||||||
'val2': new SimpleChange(undefined, 1, true),
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'onChanges',
|
|
||||||
name: 'comp - parent',
|
|
||||||
changes: {
|
|
||||||
'val1': new SimpleChange(undefined, 5, true),
|
|
||||||
'val2': new SimpleChange(undefined, 5, true),
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'onChanges',
|
|
||||||
name: 'comp - parent',
|
|
||||||
changes: {
|
|
||||||
'val1': new SimpleChange(undefined, 2, true),
|
|
||||||
'val2': new SimpleChange(undefined, 2, true),
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'onChanges',
|
|
||||||
name: 'comp - comp',
|
|
||||||
changes: {
|
|
||||||
'val1': new SimpleChange(undefined, 2, true),
|
|
||||||
'val2': new SimpleChange(undefined, 2, true),
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'onChanges',
|
|
||||||
name: 'comp - parent',
|
|
||||||
changes: {
|
|
||||||
'val1': new SimpleChange(undefined, 3, true),
|
|
||||||
'val2': new SimpleChange(undefined, 3, true),
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'onChanges',
|
|
||||||
name: 'comp - comp',
|
|
||||||
changes: {
|
|
||||||
'val1': new SimpleChange(undefined, 3, true),
|
|
||||||
'val2': new SimpleChange(undefined, 3, true),
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'onChanges',
|
|
||||||
name: 'comp - parent',
|
|
||||||
changes: {
|
|
||||||
'val1': new SimpleChange(undefined, 4, true),
|
|
||||||
'val2': new SimpleChange(undefined, 4, true),
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'onChanges',
|
|
||||||
name: 'comp - comp',
|
|
||||||
changes: {
|
|
||||||
'val1': new SimpleChange(undefined, 4, true),
|
|
||||||
'val2': new SimpleChange(undefined, 4, true),
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'onChanges',
|
|
||||||
name: 'comp - comp',
|
|
||||||
changes: {
|
|
||||||
'val1': new SimpleChange(undefined, 1, true),
|
|
||||||
'val2': new SimpleChange(undefined, 1, true),
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'onChanges',
|
|
||||||
name: 'comp - comp',
|
|
||||||
changes: {
|
|
||||||
'val1': new SimpleChange(undefined, 5, true),
|
|
||||||
'val2': new SimpleChange(undefined, 5, true),
|
|
||||||
}
|
|
||||||
},
|
|
||||||
]);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should not call onChanges if props are set directly', () => {
|
|
||||||
let events: SimpleChanges[] = [];
|
|
||||||
let compInstance: MyComp;
|
|
||||||
class MyComp {
|
|
||||||
value = 0;
|
|
||||||
|
|
||||||
ngOnChanges(changes: SimpleChanges) { events.push(changes); }
|
|
||||||
|
|
||||||
static ngComponentDef = ΔdefineComponent({
|
|
||||||
type: MyComp,
|
|
||||||
factory: () => {
|
|
||||||
// Capture the instance so we can test setting the property directly
|
|
||||||
compInstance = new MyComp();
|
|
||||||
return compInstance;
|
|
||||||
},
|
|
||||||
template: (rf: RenderFlags, ctx: any) => {
|
|
||||||
if (rf & RenderFlags.Create) {
|
|
||||||
Δelement(0, 'div');
|
|
||||||
}
|
|
||||||
if (rf & RenderFlags.Update) {
|
|
||||||
ΔelementProperty(0, 'id', Δbind(ctx.a));
|
|
||||||
}
|
|
||||||
},
|
|
||||||
selectors: [['my-comp']],
|
|
||||||
inputs: {
|
|
||||||
value: 'value',
|
|
||||||
},
|
|
||||||
consts: 1,
|
|
||||||
vars: 1,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* <my-comp [value]="1"></my-comp>
|
|
||||||
*/
|
|
||||||
|
|
||||||
const App = createComponent('app', function(rf: RenderFlags, ctx: any) {
|
|
||||||
if (rf & RenderFlags.Create) {
|
|
||||||
Δelement(0, 'my-comp');
|
|
||||||
}
|
|
||||||
if (rf & RenderFlags.Update) {
|
|
||||||
ΔelementProperty(0, 'value', Δbind(1));
|
|
||||||
}
|
|
||||||
}, 1, 1, [MyComp]);
|
|
||||||
|
|
||||||
const fixture = new ComponentFixture(App);
|
|
||||||
events = [];
|
|
||||||
|
|
||||||
// Try setting the property directly
|
|
||||||
compInstance !.value = 2;
|
|
||||||
|
|
||||||
fixture.update();
|
|
||||||
expect(events).toEqual([]);
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('hook order', () => {
|
describe('hook order', () => {
|
||||||
let events: string[];
|
let events: string[];
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user