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 {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 {By} from '@angular/platform-browser';
|
||||
import {onlyInIvy} from '@angular/private/testing';
|
||||
|
||||
describe('ngOnChanges', () => {
|
||||
describe('onChanges', () => {
|
||||
it('should correctly support updating one Input among many', () => {
|
||||
let log: string[] = [];
|
||||
|
||||
|
@ -59,6 +60,930 @@ describe('ngOnChanges', () => {
|
|||
fixture.detectChanges();
|
||||
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', () => {
|
||||
|
|
|
@ -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', () => {
|
||||
let events: string[];
|
||||
|
||||
|
|
Loading…
Reference in New Issue