@ -6,18 +6,17 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {COMPILER_OPTIONS, Component, NgModule, ViewEncapsulation, destroyPlatform} from '@angular/core';
|
||||
import {COMPILER_OPTIONS, Component, destroyPlatform, NgModule, ViewEncapsulation} from '@angular/core';
|
||||
import {BrowserModule} from '@angular/platform-browser';
|
||||
import {platformBrowserDynamic} from '@angular/platform-browser-dynamic';
|
||||
import {onlyInIvy, withBody} from '@angular/private/testing';
|
||||
|
||||
describe('bootstrap', () => {
|
||||
|
||||
beforeEach(destroyPlatform);
|
||||
afterEach(destroyPlatform);
|
||||
|
||||
it('should bootstrap using #id selector',
|
||||
withBody('<div>before|</div><button id="my-app"></button>', async() => {
|
||||
withBody('<div>before|</div><button id="my-app"></button>', async () => {
|
||||
try {
|
||||
const ngModuleRef = await platformBrowserDynamic().bootstrapModule(IdSelectorAppModule);
|
||||
expect(document.body.textContent).toEqual('before|works!');
|
||||
@ -28,7 +27,7 @@ describe('bootstrap', () => {
|
||||
}));
|
||||
|
||||
it('should bootstrap using one of selectors from the list',
|
||||
withBody('<div>before|</div><div class="bar"></div>', async() => {
|
||||
withBody('<div>before|</div><div class="bar"></div>', async () => {
|
||||
try {
|
||||
const ngModuleRef =
|
||||
await platformBrowserDynamic().bootstrapModule(MultipleSelectorsAppModule);
|
||||
@ -66,7 +65,7 @@ describe('bootstrap', () => {
|
||||
}
|
||||
|
||||
it('should use ViewEncapsulation.Emulated as default',
|
||||
withBody('<my-app></my-app>', async() => {
|
||||
withBody('<my-app></my-app>', async () => {
|
||||
const TestModule = createComponentAndModule();
|
||||
|
||||
const ngModuleRef = await platformBrowserDynamic().bootstrapModule(TestModule);
|
||||
@ -75,7 +74,7 @@ describe('bootstrap', () => {
|
||||
}));
|
||||
|
||||
it('should allow setting defaultEncapsulation using bootstrap option',
|
||||
withBody('<my-app></my-app>', async() => {
|
||||
withBody('<my-app></my-app>', async () => {
|
||||
const TestModule = createComponentAndModule();
|
||||
|
||||
const ngModuleRef = await platformBrowserDynamic().bootstrapModule(
|
||||
@ -86,7 +85,7 @@ describe('bootstrap', () => {
|
||||
}));
|
||||
|
||||
it('should allow setting defaultEncapsulation using compiler option',
|
||||
withBody('<my-app></my-app>', async() => {
|
||||
withBody('<my-app></my-app>', async () => {
|
||||
const TestModule = createComponentAndModule();
|
||||
|
||||
const ngModuleRef = await platformBrowserDynamic([{
|
||||
@ -100,7 +99,7 @@ describe('bootstrap', () => {
|
||||
}));
|
||||
|
||||
it('should prefer encapsulation on component over bootstrap option',
|
||||
withBody('<my-app></my-app>', async() => {
|
||||
withBody('<my-app></my-app>', async () => {
|
||||
const TestModule = createComponentAndModule({encapsulation: ViewEncapsulation.Emulated});
|
||||
|
||||
const ngModuleRef = await platformBrowserDynamic().bootstrapModule(
|
||||
@ -110,7 +109,7 @@ describe('bootstrap', () => {
|
||||
}));
|
||||
|
||||
it('should use preserveWhitespaces: false as default',
|
||||
withBody('<my-app></my-app>', async() => {
|
||||
withBody('<my-app></my-app>', async () => {
|
||||
const TestModule = createComponentAndModule();
|
||||
|
||||
const ngModuleRef = await platformBrowserDynamic().bootstrapModule(TestModule);
|
||||
@ -119,7 +118,7 @@ describe('bootstrap', () => {
|
||||
}));
|
||||
|
||||
it('should allow setting preserveWhitespaces using bootstrap option',
|
||||
withBody('<my-app></my-app>', async() => {
|
||||
withBody('<my-app></my-app>', async () => {
|
||||
const TestModule = createComponentAndModule();
|
||||
|
||||
const ngModuleRef = await platformBrowserDynamic().bootstrapModule(
|
||||
@ -129,7 +128,7 @@ describe('bootstrap', () => {
|
||||
}));
|
||||
|
||||
it('should allow setting preserveWhitespaces using compiler option',
|
||||
withBody('<my-app></my-app>', async() => {
|
||||
withBody('<my-app></my-app>', async () => {
|
||||
const TestModule = createComponentAndModule();
|
||||
|
||||
const ngModuleRef =
|
||||
@ -141,7 +140,7 @@ describe('bootstrap', () => {
|
||||
}));
|
||||
|
||||
it('should prefer preserveWhitespaces on component over bootstrap option',
|
||||
withBody('<my-app></my-app>', async() => {
|
||||
withBody('<my-app></my-app>', async () => {
|
||||
const TestModule = createComponentAndModule({preserveWhitespaces: false});
|
||||
|
||||
const ngModuleRef = await platformBrowserDynamic().bootstrapModule(
|
||||
@ -151,10 +150,12 @@ describe('bootstrap', () => {
|
||||
}));
|
||||
|
||||
onlyInIvy('options cannot be changed in Ivy').describe('changing bootstrap options', () => {
|
||||
beforeEach(() => { spyOn(console, 'error'); });
|
||||
beforeEach(() => {
|
||||
spyOn(console, 'error');
|
||||
});
|
||||
|
||||
it('should log an error when changing defaultEncapsulation bootstrap options',
|
||||
withBody('<my-app></my-app>', async() => {
|
||||
withBody('<my-app></my-app>', async () => {
|
||||
const TestModule = createComponentAndModule();
|
||||
const platformRef = platformBrowserDynamic();
|
||||
|
||||
@ -175,7 +176,7 @@ describe('bootstrap', () => {
|
||||
}));
|
||||
|
||||
it('should log an error when changing preserveWhitespaces bootstrap options',
|
||||
withBody('<my-app></my-app>', async() => {
|
||||
withBody('<my-app></my-app>', async () => {
|
||||
const TestModule = createComponentAndModule();
|
||||
const platformRef = platformBrowserDynamic();
|
||||
|
||||
@ -196,7 +197,7 @@ describe('bootstrap', () => {
|
||||
}));
|
||||
|
||||
it('should log an error when changing defaultEncapsulation to its default',
|
||||
withBody('<my-app></my-app>', async() => {
|
||||
withBody('<my-app></my-app>', async () => {
|
||||
const TestModule = createComponentAndModule();
|
||||
const platformRef = platformBrowserDynamic();
|
||||
|
||||
@ -215,7 +216,7 @@ describe('bootstrap', () => {
|
||||
}));
|
||||
|
||||
it('should log an error when changing preserveWhitespaces to its default',
|
||||
withBody('<my-app></my-app>', async() => {
|
||||
withBody('<my-app></my-app>', async () => {
|
||||
const TestModule = createComponentAndModule();
|
||||
const platformRef = platformBrowserDynamic();
|
||||
|
||||
@ -234,7 +235,7 @@ describe('bootstrap', () => {
|
||||
}));
|
||||
|
||||
it('should not log an error when passing identical bootstrap options',
|
||||
withBody('<my-app></my-app>', async() => {
|
||||
withBody('<my-app></my-app>', async () => {
|
||||
const TestModule = createComponentAndModule();
|
||||
const platformRef = platformBrowserDynamic();
|
||||
|
||||
|
@ -16,16 +16,16 @@ import {ivyEnabled} from '@angular/private/testing';
|
||||
import {BehaviorSubject} from 'rxjs';
|
||||
|
||||
describe('change detection', () => {
|
||||
|
||||
describe('embedded views', () => {
|
||||
|
||||
@Directive({selector: '[viewManipulation]', exportAs: 'vm'})
|
||||
class ViewManipulation {
|
||||
constructor(
|
||||
private _tplRef: TemplateRef<{}>, public vcRef: ViewContainerRef,
|
||||
private _appRef: ApplicationRef) {}
|
||||
|
||||
insertIntoVcRef() { return this.vcRef.createEmbeddedView(this._tplRef); }
|
||||
insertIntoVcRef() {
|
||||
return this.vcRef.createEmbeddedView(this._tplRef);
|
||||
}
|
||||
|
||||
insertIntoAppRef(): EmbeddedViewRef<{}> {
|
||||
const viewRef = this._tplRef.createEmbeddedView({});
|
||||
@ -80,7 +80,9 @@ describe('change detection', () => {
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
})
|
||||
class App {
|
||||
increment(counter: 'componentView'|'embeddedView') { counters[counter]++; }
|
||||
increment(counter: 'componentView'|'embeddedView') {
|
||||
counters[counter]++;
|
||||
}
|
||||
noop() {}
|
||||
}
|
||||
|
||||
@ -129,7 +131,9 @@ describe('change detection', () => {
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
})
|
||||
class DynamicComp {
|
||||
increment() { counter++; }
|
||||
increment() {
|
||||
counter++;
|
||||
}
|
||||
noop() {}
|
||||
}
|
||||
|
||||
@ -170,13 +174,10 @@ describe('change detection', () => {
|
||||
|
||||
expect(counter).toBe(3);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('markForCheck', () => {
|
||||
|
||||
it('should mark OnPush ancestor of dynamically created component views as dirty', () => {
|
||||
|
||||
@Component({
|
||||
selector: `test-cmpt`,
|
||||
template: `{{counter}}|<ng-template #vc></ng-template>`,
|
||||
@ -184,7 +185,7 @@ describe('change detection', () => {
|
||||
})
|
||||
class TestCmpt {
|
||||
counter = 0;
|
||||
@ViewChild('vc', {read: ViewContainerRef}) vcRef !: ViewContainerRef;
|
||||
@ViewChild('vc', {read: ViewContainerRef}) vcRef!: ViewContainerRef;
|
||||
|
||||
constructor(private _cfr: ComponentFactoryResolver) {}
|
||||
|
||||
@ -252,11 +253,13 @@ describe('change detection', () => {
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
private _input !: number;
|
||||
private _input!: number;
|
||||
|
||||
constructor(private cdr: ChangeDetectorRef) {}
|
||||
|
||||
get input() { return this._input; }
|
||||
get input() {
|
||||
return this._input;
|
||||
}
|
||||
|
||||
set input(value: number) {
|
||||
this._input = value;
|
||||
@ -286,18 +289,19 @@ describe('change detection', () => {
|
||||
template: `{{ doCheckCount }} - {{ name }} <button (click)="onClick()"></button>`
|
||||
})
|
||||
class MyComponent implements DoCheck {
|
||||
@Input()
|
||||
name = 'Nancy';
|
||||
@Input() name = 'Nancy';
|
||||
doCheckCount = 0;
|
||||
|
||||
ngDoCheck(): void { this.doCheckCount++; }
|
||||
ngDoCheck(): void {
|
||||
this.doCheckCount++;
|
||||
}
|
||||
|
||||
onClick() {}
|
||||
}
|
||||
|
||||
@Component({selector: 'my-app', template: '<my-comp [name]="name"></my-comp>'})
|
||||
class MyApp {
|
||||
@ViewChild(MyComponent) comp !: MyComponent;
|
||||
@ViewChild(MyComponent) comp!: MyComponent;
|
||||
name: string = 'Nancy';
|
||||
}
|
||||
|
||||
@ -369,7 +373,7 @@ describe('change detection', () => {
|
||||
expect(fixture.componentInstance.comp.doCheckCount).toEqual(1);
|
||||
expect(fixture.nativeElement.textContent.trim()).toEqual('1 - Nancy');
|
||||
|
||||
const button = fixture.nativeElement.querySelector('button') !;
|
||||
const button = fixture.nativeElement.querySelector('button')!;
|
||||
button.click();
|
||||
|
||||
// No ticks should have been scheduled.
|
||||
@ -389,7 +393,7 @@ describe('change detection', () => {
|
||||
template: '<my-comp></my-comp><button id="parent" (click)="noop()"></button>'
|
||||
})
|
||||
class ButtonParent {
|
||||
@ViewChild(MyComponent) comp !: MyComponent;
|
||||
@ViewChild(MyComponent) comp!: MyComponent;
|
||||
noop() {}
|
||||
}
|
||||
|
||||
@ -415,16 +419,18 @@ describe('change detection', () => {
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
})
|
||||
class ButtonParent implements DoCheck {
|
||||
@ViewChild(MyComponent) comp !: MyComponent;
|
||||
@ViewChild(MyComponent) comp!: MyComponent;
|
||||
noop() {}
|
||||
|
||||
doCheckCount = 0;
|
||||
ngDoCheck(): void { this.doCheckCount++; }
|
||||
ngDoCheck(): void {
|
||||
this.doCheckCount++;
|
||||
}
|
||||
}
|
||||
|
||||
@Component({selector: 'my-button-app', template: '<button-parent></button-parent>'})
|
||||
class MyButtonApp {
|
||||
@ViewChild(ButtonParent) parent !: ButtonParent;
|
||||
@ViewChild(ButtonParent) parent!: ButtonParent;
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule({declarations: [MyButtonApp, MyComponent, ButtonParent]});
|
||||
@ -456,7 +462,6 @@ describe('change detection', () => {
|
||||
expect(comp.doCheckCount).toEqual(2);
|
||||
expect(fixture.nativeElement.textContent.trim()).toEqual('3 - 2 - Nancy');
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('ChangeDetectorRef', () => {
|
||||
@ -472,18 +477,22 @@ describe('change detection', () => {
|
||||
|
||||
constructor(public cdr: ChangeDetectorRef) {}
|
||||
|
||||
ngDoCheck() { this.doCheckCount++; }
|
||||
ngDoCheck() {
|
||||
this.doCheckCount++;
|
||||
}
|
||||
}
|
||||
|
||||
@Component({selector: 'parent-comp', template: `{{ doCheckCount}} - <my-comp></my-comp>`})
|
||||
class ParentComp implements DoCheck {
|
||||
@ViewChild(MyComp) myComp !: MyComp;
|
||||
@ViewChild(MyComp) myComp!: MyComp;
|
||||
|
||||
doCheckCount = 0;
|
||||
|
||||
constructor(public cdr: ChangeDetectorRef) {}
|
||||
|
||||
ngDoCheck() { this.doCheckCount++; }
|
||||
ngDoCheck() {
|
||||
this.doCheckCount++;
|
||||
}
|
||||
}
|
||||
|
||||
@Directive({selector: '[dir]'})
|
||||
@ -562,8 +571,8 @@ describe('change detection', () => {
|
||||
it('should check component view when called by directive on component node', () => {
|
||||
@Component({template: '<my-comp dir></my-comp>'})
|
||||
class MyApp {
|
||||
@ViewChild(MyComp) myComp !: MyComp;
|
||||
@ViewChild(Dir) dir !: Dir;
|
||||
@ViewChild(MyComp) myComp!: MyComp;
|
||||
@ViewChild(Dir) dir!: Dir;
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule({declarations: [MyComp, Dir, MyApp]});
|
||||
@ -580,8 +589,8 @@ describe('change detection', () => {
|
||||
it('should check host component when called by directive on element node', () => {
|
||||
@Component({template: '{{ value }}<div dir></div>'})
|
||||
class MyApp {
|
||||
@ViewChild(MyComp) myComp !: MyComp;
|
||||
@ViewChild(Dir) dir !: Dir;
|
||||
@ViewChild(MyComp) myComp!: MyComp;
|
||||
@ViewChild(Dir) dir!: Dir;
|
||||
value = '';
|
||||
}
|
||||
|
||||
@ -601,7 +610,7 @@ describe('change detection', () => {
|
||||
it('should check the host component when called from EmbeddedViewRef', () => {
|
||||
@Component({template: '{{ name }}<div *ngIf="showing" dir></div>'})
|
||||
class MyApp {
|
||||
@ViewChild(Dir) dir !: Dir;
|
||||
@ViewChild(Dir) dir!: Dir;
|
||||
showing = true;
|
||||
name = 'Amelia';
|
||||
}
|
||||
@ -642,21 +651,30 @@ describe('change detection', () => {
|
||||
@Component({template: '<child-comp [inp]="true"></child-comp>'})
|
||||
class ParentComp {
|
||||
constructor(public cdr: ChangeDetectorRef) {}
|
||||
triggerChangeDetection() { this.cdr.detectChanges(); }
|
||||
triggerChangeDetection() {
|
||||
this.cdr.detectChanges();
|
||||
}
|
||||
}
|
||||
|
||||
@Component({template: '{{inp}}', selector: 'child-comp'})
|
||||
class ChildComp {
|
||||
@Input()
|
||||
inp: any = '';
|
||||
@Input() inp: any = '';
|
||||
|
||||
count = 0;
|
||||
constructor(public parentComp: ParentComp) {}
|
||||
|
||||
ngOnInit() { this.check('OnInit'); }
|
||||
ngAfterContentInit() { this.check('AfterContentInit'); }
|
||||
ngAfterViewInit() { this.check('AfterViewInit'); }
|
||||
ngOnChanges() { this.check('OnChanges'); }
|
||||
ngOnInit() {
|
||||
this.check('OnInit');
|
||||
}
|
||||
ngAfterContentInit() {
|
||||
this.check('AfterContentInit');
|
||||
}
|
||||
ngAfterViewInit() {
|
||||
this.check('AfterViewInit');
|
||||
}
|
||||
ngOnChanges() {
|
||||
this.check('OnChanges');
|
||||
}
|
||||
|
||||
check(h: string) {
|
||||
if (h === hook) {
|
||||
@ -704,8 +722,7 @@ describe('change detection', () => {
|
||||
`
|
||||
})
|
||||
class App {
|
||||
@ViewChildren('ref')
|
||||
ref !: QueryList<any>;
|
||||
@ViewChildren('ref') ref!: QueryList<any>;
|
||||
|
||||
visible = false;
|
||||
|
||||
@ -737,13 +754,14 @@ describe('change detection', () => {
|
||||
describe('dynamic views', () => {
|
||||
@Component({selector: 'structural-comp', template: '{{ value }}'})
|
||||
class StructuralComp {
|
||||
@Input()
|
||||
tmp !: TemplateRef<any>;
|
||||
@Input() tmp!: TemplateRef<any>;
|
||||
value = 'one';
|
||||
|
||||
constructor(public vcr: ViewContainerRef) {}
|
||||
|
||||
create() { return this.vcr.createEmbeddedView(this.tmp, {ctx: this}); }
|
||||
create() {
|
||||
return this.vcr.createEmbeddedView(this.tmp, {ctx: this});
|
||||
}
|
||||
}
|
||||
|
||||
it('should support ViewRef.detectChanges()', () => {
|
||||
@ -752,7 +770,7 @@ describe('change detection', () => {
|
||||
'<ng-template #foo let-ctx="ctx">{{ ctx.value }}</ng-template><structural-comp [tmp]="foo"></structural-comp>'
|
||||
})
|
||||
class App {
|
||||
@ViewChild(StructuralComp) structuralComp !: StructuralComp;
|
||||
@ViewChild(StructuralComp) structuralComp!: StructuralComp;
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule({declarations: [App, StructuralComp]});
|
||||
@ -781,7 +799,7 @@ describe('change detection', () => {
|
||||
template: '<ng-template #foo>Template text</ng-template><structural-comp [tmp]="foo">'
|
||||
})
|
||||
class App {
|
||||
@ViewChild(StructuralComp) structuralComp !: StructuralComp;
|
||||
@ViewChild(StructuralComp) structuralComp!: StructuralComp;
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule({declarations: [App, StructuralComp]});
|
||||
@ -794,9 +812,7 @@ describe('change detection', () => {
|
||||
viewRef.detectChanges();
|
||||
expect(fixture.nativeElement.textContent).toEqual('oneTemplate text');
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('attach/detach', () => {
|
||||
@ -807,12 +823,14 @@ describe('change detection', () => {
|
||||
|
||||
constructor(public cdr: ChangeDetectorRef) {}
|
||||
|
||||
ngDoCheck() { this.doCheckCount++; }
|
||||
ngDoCheck() {
|
||||
this.doCheckCount++;
|
||||
}
|
||||
}
|
||||
|
||||
@Component({template: '<detached-comp></detached-comp>'})
|
||||
class MyApp {
|
||||
@ViewChild(DetachedComp) comp !: DetachedComp;
|
||||
@ViewChild(DetachedComp) comp!: DetachedComp;
|
||||
|
||||
constructor(public cdr: ChangeDetectorRef) {}
|
||||
}
|
||||
@ -908,22 +926,20 @@ describe('change detection', () => {
|
||||
});
|
||||
|
||||
it('should detach OnPush components properly', () => {
|
||||
|
||||
@Component({
|
||||
selector: 'on-push-comp',
|
||||
template: '{{ value }}',
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
})
|
||||
class OnPushComp {
|
||||
@Input()
|
||||
value !: string;
|
||||
@Input() value!: string;
|
||||
|
||||
constructor(public cdr: ChangeDetectorRef) {}
|
||||
}
|
||||
|
||||
@Component({template: '<on-push-comp [value]="value"></on-push-comp>'})
|
||||
class OnPushApp {
|
||||
@ViewChild(OnPushComp) onPushComp !: OnPushComp;
|
||||
@ViewChild(OnPushComp) onPushComp!: OnPushComp;
|
||||
value = '';
|
||||
}
|
||||
|
||||
@ -946,7 +962,6 @@ describe('change detection', () => {
|
||||
fixture.detectChanges();
|
||||
expect(fixture.nativeElement.textContent).toEqual('two');
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('markForCheck()', () => {
|
||||
@ -962,7 +977,9 @@ describe('change detection', () => {
|
||||
|
||||
constructor(public cdr: ChangeDetectorRef) {}
|
||||
|
||||
ngDoCheck() { this.doCheckCount++; }
|
||||
ngDoCheck() {
|
||||
this.doCheckCount++;
|
||||
}
|
||||
}
|
||||
|
||||
@Component({
|
||||
@ -970,7 +987,7 @@ describe('change detection', () => {
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
})
|
||||
class OnPushParent {
|
||||
@ViewChild(OnPushComp) comp !: OnPushComp;
|
||||
@ViewChild(OnPushComp) comp!: OnPushComp;
|
||||
value = 'one';
|
||||
}
|
||||
|
||||
@ -1023,7 +1040,6 @@ describe('change detection', () => {
|
||||
fixture.componentInstance.comp.cdr.markForCheck();
|
||||
fixture.detectChanges();
|
||||
expect(fixture.nativeElement.textContent).toEqual('two - one');
|
||||
|
||||
});
|
||||
|
||||
it('should ensure OnPush components in embedded views are checked', () => {
|
||||
@ -1032,7 +1048,7 @@ describe('change detection', () => {
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
})
|
||||
class EmbeddedViewParent {
|
||||
@ViewChild(OnPushComp) comp !: OnPushComp;
|
||||
@ViewChild(OnPushComp) comp!: OnPushComp;
|
||||
value = 'one';
|
||||
showing = true;
|
||||
}
|
||||
@ -1112,13 +1128,21 @@ describe('change detection', () => {
|
||||
contentCheckCount = 0;
|
||||
viewCheckCount = 0;
|
||||
|
||||
ngDoCheck() { this.doCheckCount++; }
|
||||
ngDoCheck() {
|
||||
this.doCheckCount++;
|
||||
}
|
||||
|
||||
ngAfterContentChecked() { this.contentCheckCount++; }
|
||||
ngAfterContentChecked() {
|
||||
this.contentCheckCount++;
|
||||
}
|
||||
|
||||
ngAfterViewChecked() { this.viewCheckCount++; }
|
||||
ngAfterViewChecked() {
|
||||
this.viewCheckCount++;
|
||||
}
|
||||
|
||||
constructor(public cdr: ChangeDetectorRef) { comp = this; }
|
||||
constructor(public cdr: ChangeDetectorRef) {
|
||||
comp = this;
|
||||
}
|
||||
}
|
||||
|
||||
@Component({template: '{{ value }} - <no-changes-comp></no-changes-comp>'})
|
||||
@ -1131,7 +1155,9 @@ describe('change detection', () => {
|
||||
// Custom error handler that just rethrows all the errors from the
|
||||
// view, rather than logging them out. Used to keep our logs clean.
|
||||
class RethrowErrorHandler extends ErrorHandler {
|
||||
handleError(error: any) { throw error; }
|
||||
handleError(error: any) {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
it('should throw if bindings in current view have changed', () => {
|
||||
@ -1141,7 +1167,9 @@ describe('change detection', () => {
|
||||
});
|
||||
const fixture = TestBed.createComponent(NoChangesComp);
|
||||
|
||||
expect(() => { fixture.componentInstance.cdr.checkNoChanges(); })
|
||||
expect(() => {
|
||||
fixture.componentInstance.cdr.checkNoChanges();
|
||||
})
|
||||
.toThrowError(
|
||||
/ExpressionChangedAfterItHasBeenCheckedError: .+ Previous value: '.*undefined'. Current value: '.*1'/gi);
|
||||
});
|
||||
@ -1196,9 +1224,7 @@ describe('change detection', () => {
|
||||
expect(comp.contentCheckCount).toEqual(1);
|
||||
expect(comp.viewCheckCount).toEqual(1);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('transplanted views', () => {
|
||||
@ -1216,13 +1242,20 @@ describe('change detection', () => {
|
||||
</div>
|
||||
`
|
||||
})
|
||||
class InsertComp implements DoCheck,
|
||||
AfterViewChecked {
|
||||
get template(): TemplateRef<any> { return declareComp.myTmpl; }
|
||||
class InsertComp implements DoCheck, AfterViewChecked {
|
||||
get template(): TemplateRef<any> {
|
||||
return declareComp.myTmpl;
|
||||
}
|
||||
greeting: string = 'Hello';
|
||||
constructor(public changeDetectorRef: ChangeDetectorRef) { insertComp = this; }
|
||||
ngDoCheck(): void { logValue = 'Insert'; }
|
||||
ngAfterViewChecked(): void { logValue = null; }
|
||||
constructor(public changeDetectorRef: ChangeDetectorRef) {
|
||||
insertComp = this;
|
||||
}
|
||||
ngDoCheck(): void {
|
||||
logValue = 'Insert';
|
||||
}
|
||||
ngAfterViewChecked(): void {
|
||||
logValue = null;
|
||||
}
|
||||
}
|
||||
|
||||
@Component({
|
||||
@ -1234,20 +1267,24 @@ describe('change detection', () => {
|
||||
</ng-template>
|
||||
`
|
||||
})
|
||||
class DeclareComp implements DoCheck,
|
||||
AfterViewChecked {
|
||||
@ViewChild('myTmpl')
|
||||
myTmpl !: TemplateRef<any>;
|
||||
class DeclareComp implements DoCheck, AfterViewChecked {
|
||||
@ViewChild('myTmpl') myTmpl!: TemplateRef<any>;
|
||||
name: string = 'world';
|
||||
constructor() { declareComp = this; }
|
||||
ngDoCheck(): void { logValue = 'Declare'; }
|
||||
constructor() {
|
||||
declareComp = this;
|
||||
}
|
||||
ngDoCheck(): void {
|
||||
logValue = 'Declare';
|
||||
}
|
||||
logName() {
|
||||
// This will log when the embedded view gets CD. The `logValue` will show if the CD was from
|
||||
// `Insert` or from `Declare` component.
|
||||
log.push(logValue !);
|
||||
log.push(logValue!);
|
||||
return this.name;
|
||||
}
|
||||
ngAfterViewChecked(): void { logValue = null; }
|
||||
ngAfterViewChecked(): void {
|
||||
logValue = null;
|
||||
}
|
||||
}
|
||||
|
||||
@Component({
|
||||
@ -1259,15 +1296,17 @@ describe('change detection', () => {
|
||||
class AppComp {
|
||||
showDeclare: boolean = true;
|
||||
showInsert: boolean = true;
|
||||
constructor() { appComp = this; }
|
||||
constructor() {
|
||||
appComp = this;
|
||||
}
|
||||
}
|
||||
|
||||
let log !: string[];
|
||||
let logValue !: string | null;
|
||||
let fixture !: ComponentFixture<AppComp>;
|
||||
let appComp !: AppComp;
|
||||
let insertComp !: InsertComp;
|
||||
let declareComp !: DeclareComp;
|
||||
let log!: string[];
|
||||
let logValue!: string|null;
|
||||
let fixture!: ComponentFixture<AppComp>;
|
||||
let appComp!: AppComp;
|
||||
let insertComp!: InsertComp;
|
||||
let declareComp!: DeclareComp;
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({
|
||||
@ -1376,16 +1415,16 @@ describe('change detection', () => {
|
||||
class OnPushComp {
|
||||
text = 'initial';
|
||||
|
||||
constructor(private _cdRef: ChangeDetectorRef){}
|
||||
constructor(private _cdRef: ChangeDetectorRef) {}
|
||||
|
||||
[hookName]() {
|
||||
[hookName]() {
|
||||
this._cdRef.markForCheck();
|
||||
}
|
||||
}
|
||||
|
||||
@Component({template: `<on-push-comp></on-push-comp>`})
|
||||
class TestApp {
|
||||
@ViewChild(OnPushComp) onPushComp !: OnPushComp;
|
||||
@ViewChild(OnPushComp) onPushComp!: OnPushComp;
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule(
|
||||
@ -1419,16 +1458,16 @@ describe('change detection', () => {
|
||||
class OnPushComp {
|
||||
text = 'initial';
|
||||
|
||||
constructor(private _cdRef: ChangeDetectorRef){}
|
||||
constructor(private _cdRef: ChangeDetectorRef) {}
|
||||
|
||||
[hookName]() {
|
||||
[hookName]() {
|
||||
this._cdRef.markForCheck();
|
||||
}
|
||||
}
|
||||
|
||||
@Component({template: `<on-push-comp></on-push-comp>`})
|
||||
class TestApp {
|
||||
@ViewChild(OnPushComp) onPushComp !: OnPushComp;
|
||||
@ViewChild(OnPushComp) onPushComp!: OnPushComp;
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule(
|
||||
@ -1478,7 +1517,9 @@ describe('change detection', () => {
|
||||
return fixture;
|
||||
}
|
||||
|
||||
function initWithTemplate(template: string) { return initComponent({template}); }
|
||||
function initWithTemplate(template: string) {
|
||||
return initComponent({template});
|
||||
}
|
||||
function initWithHostBindings(bindings: {[key: string]: string}) {
|
||||
return initComponent({host: bindings});
|
||||
}
|
||||
@ -1612,6 +1653,6 @@ describe('change detection', () => {
|
||||
});
|
||||
});
|
||||
|
||||
function trim(text: string | null): string {
|
||||
function trim(text: string|null): string {
|
||||
return text ? text.replace(/[\s\n]+/gm, ' ').trim() : '';
|
||||
}
|
||||
|
@ -11,7 +11,6 @@ import {TestBed} from '@angular/core/testing';
|
||||
import {By} from '@angular/platform-browser';
|
||||
|
||||
describe('@angular/common integration', () => {
|
||||
|
||||
describe('NgForOf', () => {
|
||||
@Directive({selector: '[dir]'})
|
||||
class MyDirective {
|
||||
@ -244,7 +243,9 @@ describe('@angular/common integration', () => {
|
||||
name = 'app';
|
||||
events: string[] = [];
|
||||
|
||||
onClick(value: string, name: string) { this.events.push(value, name); }
|
||||
onClick(value: string, name: string) {
|
||||
this.events.push(value, name);
|
||||
}
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule({declarations: [MultiLevelWithListenerComponent]});
|
||||
@ -485,7 +486,6 @@ describe('@angular/common integration', () => {
|
||||
});
|
||||
|
||||
describe('NgTemplateOutlet', () => {
|
||||
|
||||
it('should create and remove embedded views', () => {
|
||||
@Component({
|
||||
selector: 'app-multi',
|
||||
|
@ -28,7 +28,9 @@ describe('component', () => {
|
||||
providers: [{provide: testToken, useExisting: ParentWithOnDestroy}]
|
||||
})
|
||||
class ParentWithOnDestroy {
|
||||
ngOnDestroy() { destroyCalls++; }
|
||||
ngOnDestroy() {
|
||||
destroyCalls++;
|
||||
}
|
||||
}
|
||||
|
||||
@Component({selector: 'child', template: ''})
|
||||
@ -75,7 +77,7 @@ describe('component', () => {
|
||||
entryComponents: [OtherComponent]
|
||||
})
|
||||
class TestComponent {
|
||||
@ViewChild('vc', {read: ViewContainerRef, static: true}) vcref !: ViewContainerRef;
|
||||
@ViewChild('vc', {read: ViewContainerRef, static: true}) vcref!: ViewContainerRef;
|
||||
|
||||
constructor(private _cfr: ComponentFactoryResolver) {}
|
||||
|
||||
@ -152,7 +154,8 @@ describe('component', () => {
|
||||
expect(match).toBeDefined();
|
||||
expect(match.length).toEqual(2);
|
||||
expect(html).toMatch(
|
||||
`<leaf ${match[0].replace('_nghost', '_ngcontent')}="" ${match[1]}=""><span ${match[1].replace('_nghost', '_ngcontent')}="">bar</span></leaf></div>`);
|
||||
`<leaf ${match[0].replace('_nghost', '_ngcontent')}="" ${match[1]}=""><span ${
|
||||
match[1].replace('_nghost', '_ngcontent')}="">bar</span></leaf></div>`);
|
||||
});
|
||||
});
|
||||
|
||||
@ -162,7 +165,9 @@ describe('component', () => {
|
||||
|
||||
@Component({selector: 'comp-with-destroy', template: ``})
|
||||
class ComponentWithOnDestroy implements OnDestroy {
|
||||
ngOnDestroy() { wasOnDestroyCalled = true; }
|
||||
ngOnDestroy() {
|
||||
wasOnDestroyCalled = true;
|
||||
}
|
||||
}
|
||||
|
||||
// This test asserts that the view tree is set up correctly based on the knowledge that this
|
||||
@ -244,7 +249,7 @@ describe('component', () => {
|
||||
encapsulation: ViewEncapsulation.Emulated,
|
||||
})
|
||||
class Parent {
|
||||
@ViewChild(Child) childInstance !: Child;
|
||||
@ViewChild(Child) childInstance!: Child;
|
||||
constructor(public renderer: Renderer2) {}
|
||||
}
|
||||
|
||||
@ -266,7 +271,9 @@ describe('component', () => {
|
||||
})
|
||||
class CompA {
|
||||
@Input() a: string = '';
|
||||
ngDoCheck() { log.push('CompA:ngDoCheck'); }
|
||||
ngDoCheck() {
|
||||
log.push('CompA:ngDoCheck');
|
||||
}
|
||||
}
|
||||
|
||||
@Component({
|
||||
@ -275,7 +282,9 @@ describe('component', () => {
|
||||
})
|
||||
class CompB {
|
||||
@Input() b: string = '';
|
||||
ngDoCheck() { log.push('CompB:ngDoCheck'); }
|
||||
ngDoCheck() {
|
||||
log.push('CompB:ngDoCheck');
|
||||
}
|
||||
}
|
||||
|
||||
@Component({template: `<span></span>`})
|
||||
@ -435,14 +444,14 @@ describe('component', () => {
|
||||
fixture.detectChanges();
|
||||
|
||||
// Create an instance of DynamicComponent and provide host element *reference*
|
||||
let targetEl = document.getElementById('dynamic-comp-root-a') !;
|
||||
let targetEl = document.getElementById('dynamic-comp-root-a')!;
|
||||
fixture.componentInstance.createDynamicComponent(targetEl);
|
||||
fixture.detectChanges();
|
||||
expect(targetEl.innerHTML).not.toContain('Existing content in slot A');
|
||||
expect(targetEl.innerHTML).toContain('DynamicComponent Content');
|
||||
|
||||
// Create an instance of DynamicComponent and provide host element *selector*
|
||||
targetEl = document.getElementById('dynamic-comp-root-b') !;
|
||||
targetEl = document.getElementById('dynamic-comp-root-b')!;
|
||||
fixture.componentInstance.createDynamicComponent('#dynamic-comp-root-b');
|
||||
fixture.detectChanges();
|
||||
expect(targetEl.innerHTML).not.toContain('Existing content in slot B');
|
||||
@ -453,7 +462,8 @@ describe('component', () => {
|
||||
() => runTestWithRenderer([{provide: RendererFactory2, useClass: DomRendererFactory2}]));
|
||||
|
||||
onlyInIvy('Renderer3 is supported only in Ivy')
|
||||
.it('with Renderer3', () => runTestWithRenderer(
|
||||
[{provide: RendererFactory2, useValue: domRendererFactory3}]));
|
||||
.it('with Renderer3',
|
||||
() =>
|
||||
runTestWithRenderer([{provide: RendererFactory2, useValue: domRendererFactory3}]));
|
||||
});
|
||||
});
|
||||
|
@ -14,7 +14,6 @@ import {By} from '@angular/platform-browser';
|
||||
import {expect} from '@angular/platform-browser/testing/src/matchers';
|
||||
|
||||
describe('projection', () => {
|
||||
|
||||
function getElementHtml(element: HTMLElement) {
|
||||
return element.innerHTML.replace(/<!--(\W|\w)*?-->/g, '')
|
||||
.replace(/\sng-reflect-\S*="[^"]*"/g, '');
|
||||
@ -328,11 +327,13 @@ describe('projection', () => {
|
||||
|
||||
@Directive({selector: '[trigger]'})
|
||||
class Trigger {
|
||||
@Input() trigger !: Comp;
|
||||
@Input() trigger!: Comp;
|
||||
|
||||
constructor(public vcr: ViewContainerRef) {}
|
||||
|
||||
open() { this.vcr.createEmbeddedView(this.trigger.template); }
|
||||
open() {
|
||||
this.vcr.createEmbeddedView(this.trigger.template);
|
||||
}
|
||||
}
|
||||
|
||||
@Component({
|
||||
@ -861,7 +862,6 @@ describe('projection', () => {
|
||||
|
||||
expect(getElementHtml(fixture.nativeElement))
|
||||
.toEqual('<child><span title="Some title">Has title</span></child>');
|
||||
|
||||
});
|
||||
|
||||
it('should match selectors against projected containers', () => {
|
||||
@ -934,7 +934,9 @@ describe('projection', () => {
|
||||
it('should project content if the change detector has been detached', () => {
|
||||
@Component({selector: 'my-comp', template: '<ng-content></ng-content>'})
|
||||
class MyComp {
|
||||
constructor(changeDetectorRef: ChangeDetectorRef) { changeDetectorRef.detach(); }
|
||||
constructor(changeDetectorRef: ChangeDetectorRef) {
|
||||
changeDetectorRef.detach();
|
||||
}
|
||||
}
|
||||
|
||||
@Component({
|
||||
@ -1108,7 +1110,9 @@ describe('projection', () => {
|
||||
|
||||
@Directive({selector: 'div'})
|
||||
class DivDirective {
|
||||
constructor() { divDirectives++; }
|
||||
constructor() {
|
||||
divDirectives++;
|
||||
}
|
||||
}
|
||||
|
||||
@Component({
|
||||
@ -1135,7 +1139,9 @@ describe('projection', () => {
|
||||
|
||||
@Directive({selector: '[x]'})
|
||||
class XDirective {
|
||||
constructor() { xDirectives++; }
|
||||
constructor() {
|
||||
xDirectives++;
|
||||
}
|
||||
}
|
||||
|
||||
@Component({
|
||||
@ -1162,7 +1168,9 @@ describe('projection', () => {
|
||||
|
||||
@Directive({selector: '.x'})
|
||||
class XDirective {
|
||||
constructor() { xDirectives++; }
|
||||
constructor() {
|
||||
xDirectives++;
|
||||
}
|
||||
}
|
||||
|
||||
@Component({
|
||||
@ -1199,7 +1207,9 @@ describe('projection', () => {
|
||||
{id: 2, name: 'two'},
|
||||
{id: 3, name: 'three'},
|
||||
];
|
||||
getItemId(item: {id: number}) { return item.id; }
|
||||
getItemId(item: {id: number}) {
|
||||
return item.id;
|
||||
}
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule({declarations: [SelectedNgContentComp, SelectorMainComp]});
|
||||
@ -1265,7 +1275,9 @@ describe('projection', () => {
|
||||
|
||||
@Directive({selector: '[x]'})
|
||||
class XDirective {
|
||||
constructor() { xDirectives++; }
|
||||
constructor() {
|
||||
xDirectives++;
|
||||
}
|
||||
}
|
||||
|
||||
@Component({
|
||||
@ -1293,7 +1305,9 @@ describe('projection', () => {
|
||||
|
||||
@Directive({selector: '.x'})
|
||||
class XDirective {
|
||||
constructor() { xDirectives++; }
|
||||
constructor() {
|
||||
xDirectives++;
|
||||
}
|
||||
}
|
||||
|
||||
@Component({
|
||||
|
@ -6,7 +6,7 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {Component, NgModule, ɵɵCopyDefinitionFeature as CopyDefinitionFeature, ɵɵInheritDefinitionFeature as InheritDefinitionFeature, ɵɵdefineComponent as defineComponent} from '@angular/core';
|
||||
import {Component, NgModule, ɵɵCopyDefinitionFeature as CopyDefinitionFeature, ɵɵdefineComponent as defineComponent, ɵɵInheritDefinitionFeature as InheritDefinitionFeature} from '@angular/core';
|
||||
import {TestBed} from '@angular/core/testing';
|
||||
import {onlyInIvy} from '@angular/private/testing';
|
||||
|
||||
@ -14,20 +14,22 @@ describe('Ivy CopyDefinitionFeature', () => {
|
||||
onlyInIvy('this feature is not required in View Engine')
|
||||
.it('should copy the template function of a component definition from parent to child',
|
||||
() => {
|
||||
|
||||
// It would be nice if the base component could be JIT compiled. However, this creates
|
||||
// a getter for ɵcmp which precludes adding a static definition of that field for the
|
||||
// child class.
|
||||
// TODO(alxhub): see if there's a cleaner way to do this.
|
||||
class BaseComponent {
|
||||
name !: string;
|
||||
name!: string;
|
||||
static ɵcmp = defineComponent({
|
||||
type: BaseComponent,
|
||||
selectors: [['some-cmp']],
|
||||
decls: 0,
|
||||
vars: 0,
|
||||
inputs: {name: 'name'},
|
||||
template: function BaseComponent_Template(rf, ctx) { ctx.rendered = true; },
|
||||
template:
|
||||
function BaseComponent_Template(rf, ctx) {
|
||||
ctx.rendered = true;
|
||||
},
|
||||
encapsulation: 2
|
||||
});
|
||||
static ɵfac = function BaseComponent_Factory(t: any) {
|
||||
|
@ -14,9 +14,7 @@ import {expect} from '@angular/platform-browser/testing/src/matchers';
|
||||
import {onlyInIvy} from '@angular/private/testing';
|
||||
|
||||
describe('Debug Representation', () => {
|
||||
|
||||
onlyInIvy('Ivy specific').it('should generate a human readable version', () => {
|
||||
|
||||
@Component({selector: 'my-comp', template: '<div id="123">Hello World</div>'})
|
||||
class MyComponent {
|
||||
}
|
||||
@ -25,11 +23,11 @@ describe('Debug Representation', () => {
|
||||
const fixture = TestBed.createComponent(MyComponent);
|
||||
fixture.detectChanges();
|
||||
|
||||
const hostView = toDebug(getLContext(fixture.componentInstance) !.lView);
|
||||
const hostView = toDebug(getLContext(fixture.componentInstance)!.lView);
|
||||
expect(hostView.host).toEqual(null);
|
||||
const myCompView = hostView.childViews[0] as LViewDebug;
|
||||
expect(myCompView.host).toContain('<div id="123">Hello World</div>');
|
||||
expect(myCompView.nodes ![0].html).toEqual('<div id="123">');
|
||||
expect(myCompView.nodes ![0].nodes ![0].html).toEqual('Hello World');
|
||||
expect(myCompView.nodes![0].html).toEqual('<div id="123">');
|
||||
expect(myCompView.nodes![0].nodes![0].html).toEqual('Hello World');
|
||||
});
|
||||
});
|
||||
|
@ -7,7 +7,7 @@
|
||||
*/
|
||||
|
||||
import {CommonModule} from '@angular/common';
|
||||
import {Attribute, ChangeDetectorRef, Component, ComponentFactoryResolver, ComponentRef, Directive, ElementRef, EventEmitter, Host, HostBinding, INJECTOR, Inject, Injectable, InjectionToken, Injector, Input, LOCALE_ID, ModuleWithProviders, NgModule, NgZone, Optional, Output, Pipe, PipeTransform, Self, SkipSelf, TemplateRef, ViewChild, ViewContainerRef, forwardRef, ɵDEFAULT_LOCALE_ID as DEFAULT_LOCALE_ID} from '@angular/core';
|
||||
import {Attribute, ChangeDetectorRef, Component, ComponentFactoryResolver, ComponentRef, Directive, ElementRef, EventEmitter, forwardRef, Host, HostBinding, Inject, Injectable, InjectionToken, INJECTOR, Injector, Input, LOCALE_ID, ModuleWithProviders, NgModule, NgZone, Optional, Output, Pipe, PipeTransform, Self, SkipSelf, TemplateRef, ViewChild, ViewContainerRef, ɵDEFAULT_LOCALE_ID as DEFAULT_LOCALE_ID} from '@angular/core';
|
||||
import {ɵINJECTOR_SCOPE} from '@angular/core/src/core';
|
||||
import {ViewRef} from '@angular/core/src/render3/view_ref';
|
||||
import {TestBed} from '@angular/core/testing';
|
||||
@ -60,14 +60,15 @@ describe('di', () => {
|
||||
});
|
||||
|
||||
describe('directive injection', () => {
|
||||
|
||||
let log: string[] = [];
|
||||
|
||||
@Directive({selector: '[dirB]', exportAs: 'dirB'})
|
||||
class DirectiveB {
|
||||
@Input() value = 'DirB';
|
||||
|
||||
constructor() { log.push(this.value); }
|
||||
constructor() {
|
||||
log.push(this.value);
|
||||
}
|
||||
}
|
||||
|
||||
beforeEach(() => log = []);
|
||||
@ -82,7 +83,9 @@ describe('di', () => {
|
||||
class DirectiveC {
|
||||
value: string;
|
||||
|
||||
constructor(dirA: DirectiveA, dirB: DirectiveB) { this.value = dirA.value + dirB.value; }
|
||||
constructor(dirA: DirectiveA, dirB: DirectiveB) {
|
||||
this.value = dirA.value + dirB.value;
|
||||
}
|
||||
}
|
||||
|
||||
@Component({
|
||||
@ -108,7 +111,9 @@ describe('di', () => {
|
||||
class DirectiveA {
|
||||
value = 'dirA';
|
||||
|
||||
constructor(dirB: DirectiveB) { log.push(`DirA (dep: ${dirB.value})`); }
|
||||
constructor(dirB: DirectiveB) {
|
||||
log.push(`DirA (dep: ${dirB.value})`);
|
||||
}
|
||||
}
|
||||
|
||||
@Component({template: '<div dirA dirB></div>'})
|
||||
@ -127,7 +132,9 @@ describe('di', () => {
|
||||
class DirectiveA {
|
||||
value = 'dirA';
|
||||
|
||||
constructor(dirB: DirectiveB) { log.push(`DirA (dep: ${dirB.value})`); }
|
||||
constructor(dirB: DirectiveB) {
|
||||
log.push(`DirA (dep: ${dirB.value})`);
|
||||
}
|
||||
}
|
||||
|
||||
// - dirB is know to the node injectors
|
||||
@ -150,7 +157,9 @@ describe('di', () => {
|
||||
it('should instantiate injected directives before components', () => {
|
||||
@Component({selector: 'my-comp', template: ''})
|
||||
class MyComp {
|
||||
constructor(dirB: DirectiveB) { log.push(`Comp (dep: ${dirB.value})`); }
|
||||
constructor(dirB: DirectiveB) {
|
||||
log.push(`Comp (dep: ${dirB.value})`);
|
||||
}
|
||||
}
|
||||
|
||||
@Component({template: '<my-comp dirB></my-comp>'})
|
||||
@ -167,7 +176,9 @@ describe('di', () => {
|
||||
it('should inject directives in the correct order in a for loop', () => {
|
||||
@Directive({selector: '[dirA]'})
|
||||
class DirectiveA {
|
||||
constructor(dir: DirectiveB) { log.push(`DirA (dep: ${dir.value})`); }
|
||||
constructor(dir: DirectiveB) {
|
||||
log.push(`DirA (dep: ${dir.value})`);
|
||||
}
|
||||
}
|
||||
|
||||
@Component({template: '<div dirA dirB *ngFor="let i of array"></div>'})
|
||||
@ -188,14 +199,18 @@ describe('di', () => {
|
||||
class DirectiveA {
|
||||
value = 'DirA';
|
||||
|
||||
constructor() { log.push(this.value); }
|
||||
constructor() {
|
||||
log.push(this.value);
|
||||
}
|
||||
}
|
||||
|
||||
@Directive({selector: '[dirC]'})
|
||||
class DirectiveC {
|
||||
value = 'DirC';
|
||||
|
||||
constructor() { log.push(this.value); }
|
||||
constructor() {
|
||||
log.push(this.value);
|
||||
}
|
||||
}
|
||||
|
||||
@Directive({selector: '[dirB]'})
|
||||
@ -221,26 +236,34 @@ describe('di', () => {
|
||||
class DirectiveC {
|
||||
value = 'DirC';
|
||||
|
||||
constructor(dirB: DirectiveB) { log.push(`DirC (dep: ${dirB.value})`); }
|
||||
constructor(dirB: DirectiveB) {
|
||||
log.push(`DirC (dep: ${dirB.value})`);
|
||||
}
|
||||
}
|
||||
|
||||
@Directive({selector: '[dirA]'})
|
||||
class DirectiveA {
|
||||
value = 'DirA';
|
||||
|
||||
constructor(dirC: DirectiveC) { log.push(`DirA (dep: ${dirC.value})`); }
|
||||
constructor(dirC: DirectiveC) {
|
||||
log.push(`DirA (dep: ${dirC.value})`);
|
||||
}
|
||||
}
|
||||
|
||||
@Directive({selector: '[dirD]'})
|
||||
class DirectiveD {
|
||||
value = 'DirD';
|
||||
|
||||
constructor(dirA: DirectiveA) { log.push(`DirD (dep: ${dirA.value})`); }
|
||||
constructor(dirA: DirectiveA) {
|
||||
log.push(`DirD (dep: ${dirA.value})`);
|
||||
}
|
||||
}
|
||||
|
||||
@Component({selector: 'my-comp', template: ''})
|
||||
class MyComp {
|
||||
constructor(dirD: DirectiveD) { log.push(`Comp (dep: ${dirD.value})`); }
|
||||
constructor(dirD: DirectiveD) {
|
||||
log.push(`Comp (dep: ${dirD.value})`);
|
||||
}
|
||||
}
|
||||
|
||||
@Component({template: '<my-comp dirA dirB dirC dirD></my-comp>'})
|
||||
@ -291,7 +314,9 @@ describe('di', () => {
|
||||
|
||||
@Directive({selector: '[dirA]'})
|
||||
class DirectiveA {
|
||||
constructor(dirB: DirectiveB) { log.push(`DirA (dep: DirB - ${dirB.count})`); }
|
||||
constructor(dirB: DirectiveB) {
|
||||
log.push(`DirA (dep: DirB - ${dirB.count})`);
|
||||
}
|
||||
}
|
||||
|
||||
@Component({selector: 'my-comp', template: '<div dirA dirB></div>'})
|
||||
@ -310,7 +335,6 @@ describe('di', () => {
|
||||
});
|
||||
|
||||
describe('dependencies in parent views', () => {
|
||||
|
||||
@Directive({selector: '[dirA]', exportAs: 'dirA'})
|
||||
class DirectiveA {
|
||||
injector: Injector;
|
||||
@ -385,11 +409,13 @@ describe('di', () => {
|
||||
it('should find dependencies in declaration tree of ng-template (not insertion tree)', () => {
|
||||
@Directive({selector: '[structuralDir]'})
|
||||
class StructuralDirective {
|
||||
@Input() tmp !: TemplateRef<any>;
|
||||
@Input() tmp!: TemplateRef<any>;
|
||||
|
||||
constructor(public vcr: ViewContainerRef) {}
|
||||
|
||||
create() { this.vcr.createEmbeddedView(this.tmp); }
|
||||
create() {
|
||||
this.vcr.createEmbeddedView(this.tmp);
|
||||
}
|
||||
}
|
||||
|
||||
@Component({
|
||||
@ -405,7 +431,7 @@ describe('di', () => {
|
||||
</div>`
|
||||
})
|
||||
class MyComp {
|
||||
@ViewChild(StructuralDirective) structuralDir !: StructuralDirective;
|
||||
@ViewChild(StructuralDirective) structuralDir!: StructuralDirective;
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule(
|
||||
@ -449,8 +475,8 @@ describe('di', () => {
|
||||
</div>`
|
||||
})
|
||||
class MyApp {
|
||||
@ViewChild(HostBindingDirective) hostBindingDir !: HostBindingDirective;
|
||||
@ViewChild(DirectiveA) dirA !: DirectiveA;
|
||||
@ViewChild(HostBindingDirective) hostBindingDir!: HostBindingDirective;
|
||||
@ViewChild(DirectiveA) dirA!: DirectiveA;
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule(
|
||||
@ -501,15 +527,19 @@ describe('di', () => {
|
||||
})
|
||||
class MyApp {
|
||||
@ViewChild('childOrigin', {read: ViewContainerRef, static: true})
|
||||
childOrigin !: ViewContainerRef;
|
||||
childOrigin!: ViewContainerRef;
|
||||
@ViewChild('childOriginWithDirB', {read: ViewContainerRef, static: true})
|
||||
childOriginWithDirB !: ViewContainerRef;
|
||||
childOriginWithDirB!: ViewContainerRef;
|
||||
childFactory = this.resolver.resolveComponentFactory(Child);
|
||||
|
||||
constructor(readonly resolver: ComponentFactoryResolver, readonly injector: Injector) {}
|
||||
|
||||
addChild() { return this.childOrigin.createComponent(this.childFactory); }
|
||||
addChildWithDirB() { return this.childOriginWithDirB.createComponent(this.childFactory); }
|
||||
addChild() {
|
||||
return this.childOrigin.createComponent(this.childFactory);
|
||||
}
|
||||
addChildWithDirB() {
|
||||
return this.childOriginWithDirB.createComponent(this.childFactory);
|
||||
}
|
||||
}
|
||||
|
||||
const fixture =
|
||||
@ -604,24 +634,21 @@ describe('di', () => {
|
||||
});
|
||||
|
||||
describe('flags', () => {
|
||||
|
||||
@Directive({selector: '[dirB]'})
|
||||
class DirectiveB {
|
||||
@Input('dirB') value = '';
|
||||
}
|
||||
|
||||
describe('Optional', () => {
|
||||
|
||||
@Directive({selector: '[dirA]'})
|
||||
class DirectiveA {
|
||||
constructor(@Optional() public dirB: DirectiveB) {}
|
||||
}
|
||||
|
||||
it('should not throw if dependency is @Optional (module injector)', () => {
|
||||
|
||||
@Component({template: '<div dirA></div>'})
|
||||
class MyComp {
|
||||
@ViewChild(DirectiveA) dirA !: DirectiveA;
|
||||
@ViewChild(DirectiveA) dirA!: DirectiveA;
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule({declarations: [DirectiveA, DirectiveB, MyComp]});
|
||||
@ -633,7 +660,6 @@ describe('di', () => {
|
||||
});
|
||||
|
||||
it('should return null if @Optional dependency has @Self flag', () => {
|
||||
|
||||
@Directive({selector: '[dirC]'})
|
||||
class DirectiveC {
|
||||
constructor(@Optional() @Self() public dirB: DirectiveB) {}
|
||||
@ -641,7 +667,7 @@ describe('di', () => {
|
||||
|
||||
@Component({template: '<div dirC></div>'})
|
||||
class MyComp {
|
||||
@ViewChild(DirectiveC) dirC !: DirectiveC;
|
||||
@ViewChild(DirectiveC) dirC!: DirectiveC;
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule({declarations: [DirectiveC, MyComp]});
|
||||
@ -653,7 +679,6 @@ describe('di', () => {
|
||||
});
|
||||
|
||||
it('should not throw if dependency is @Optional but defined elsewhere', () => {
|
||||
|
||||
@Directive({selector: '[dirC]'})
|
||||
class DirectiveC {
|
||||
constructor(@Optional() public dirB: DirectiveB) {}
|
||||
@ -661,7 +686,7 @@ describe('di', () => {
|
||||
|
||||
@Component({template: '<div dirB></div><div dirC></div>'})
|
||||
class MyComp {
|
||||
@ViewChild(DirectiveC) dirC !: DirectiveC;
|
||||
@ViewChild(DirectiveC) dirC!: DirectiveC;
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule({declarations: [DirectiveB, DirectiveC, MyComp]});
|
||||
@ -674,7 +699,6 @@ describe('di', () => {
|
||||
});
|
||||
|
||||
it('should skip the current node with @SkipSelf', () => {
|
||||
|
||||
@Directive({selector: '[dirA]'})
|
||||
class DirectiveA {
|
||||
constructor(@SkipSelf() public dirB: DirectiveB) {}
|
||||
@ -682,12 +706,12 @@ describe('di', () => {
|
||||
|
||||
@Component({selector: 'my-comp', template: '<div dirA dirB="self"></div>'})
|
||||
class MyComp {
|
||||
@ViewChild(DirectiveA) dirA !: DirectiveA;
|
||||
@ViewChild(DirectiveA) dirA!: DirectiveA;
|
||||
}
|
||||
|
||||
@Component({template: '<my-comp dirB="parent"></my-comp>'})
|
||||
class MyApp {
|
||||
@ViewChild(MyComp) myComp !: MyComp;
|
||||
@ViewChild(MyComp) myComp!: MyComp;
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule({declarations: [DirectiveA, DirectiveB, MyComp, MyApp]});
|
||||
@ -700,7 +724,6 @@ describe('di', () => {
|
||||
|
||||
onlyInIvy('Ivy has different error message when dependency is not found')
|
||||
.it('should check only the current node with @Self', () => {
|
||||
|
||||
@Directive({selector: '[dirA]'})
|
||||
class DirectiveA {
|
||||
constructor(@Self() public dirB: DirectiveB) {}
|
||||
@ -732,12 +755,12 @@ describe('di', () => {
|
||||
viewProviders: [{provide: String, useValue: 'Foo'}]
|
||||
})
|
||||
class MyComp {
|
||||
@ViewChild(DirectiveString) dirString !: DirectiveString;
|
||||
@ViewChild(DirectiveString) dirString!: DirectiveString;
|
||||
}
|
||||
|
||||
@Component({template: '<my-comp></my-comp>'})
|
||||
class MyApp {
|
||||
@ViewChild(MyComp) myComp !: MyComp;
|
||||
@ViewChild(MyComp) myComp!: MyComp;
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule({declarations: [DirectiveString, MyComp, MyApp]});
|
||||
@ -756,12 +779,12 @@ describe('di', () => {
|
||||
|
||||
@Component({selector: 'my-comp', template: '<div dirComp></div>'})
|
||||
class MyComp {
|
||||
@ViewChild(DirectiveComp) dirComp !: DirectiveComp;
|
||||
@ViewChild(DirectiveComp) dirComp!: DirectiveComp;
|
||||
}
|
||||
|
||||
@Component({template: '<my-comp></my-comp>'})
|
||||
class MyApp {
|
||||
@ViewChild(MyComp) myComp !: MyComp;
|
||||
@ViewChild(MyComp) myComp!: MyComp;
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule({declarations: [DirectiveComp, MyComp, MyApp]});
|
||||
@ -820,7 +843,7 @@ describe('di', () => {
|
||||
|
||||
@Component({template: '<my-comp dirB></my-comp>'})
|
||||
class MyApp {
|
||||
@ViewChild(MyComp) myComp !: MyComp;
|
||||
@ViewChild(MyComp) myComp!: MyComp;
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule(
|
||||
@ -837,8 +860,8 @@ describe('di', () => {
|
||||
@Component({template: '<div dirB><div *ngIf="showing" dirA></div></div>'})
|
||||
class MyApp {
|
||||
showing = false;
|
||||
@ViewChild(DirectiveA) dirA !: DirectiveA;
|
||||
@ViewChild(DirectiveB) dirB !: DirectiveB;
|
||||
@ViewChild(DirectiveA) dirA!: DirectiveA;
|
||||
@ViewChild(DirectiveB) dirB!: DirectiveB;
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule({declarations: [DirectiveA, DirectiveB, MyApp]});
|
||||
@ -885,7 +908,9 @@ describe('di', () => {
|
||||
providers: [{provide: ControlContainer, useExisting: GroupDirective}]
|
||||
})
|
||||
class GroupDirective {
|
||||
constructor() { controlContainers.push(this); }
|
||||
constructor() {
|
||||
controlContainers.push(this);
|
||||
}
|
||||
}
|
||||
|
||||
@Directive({selector: '[control]'})
|
||||
@ -919,7 +944,7 @@ describe('di', () => {
|
||||
const fixture = TestBed.createComponent(MyApp);
|
||||
expect(fixture.nativeElement.innerHTML)
|
||||
.toBe('<div group=""><my-comp><input control=""></my-comp></div>');
|
||||
expect(controlContainers).toEqual([injectedControlContainer !]);
|
||||
expect(controlContainers).toEqual([injectedControlContainer!]);
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -958,7 +983,6 @@ describe('di', () => {
|
||||
});
|
||||
|
||||
describe('service injection', () => {
|
||||
|
||||
it('should create instance even when no injector present', () => {
|
||||
@Injectable({providedIn: 'root'})
|
||||
class MyService {
|
||||
@ -1063,7 +1087,6 @@ describe('di', () => {
|
||||
expect(fixture.componentInstance.myService instanceof MyService).toBe(true);
|
||||
expect(fixture.componentInstance.myOtherService instanceof MyOtherService).toBe(true);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('service injection with useClass', () => {
|
||||
@ -1075,7 +1098,9 @@ describe('di', () => {
|
||||
@Injectable({providedIn: 'root'})
|
||||
class BarService {
|
||||
constructor(public dep: BarServiceDep) {}
|
||||
getMessage() { return 'bar'; }
|
||||
getMessage() {
|
||||
return 'bar';
|
||||
}
|
||||
}
|
||||
|
||||
@Injectable({providedIn: 'root'})
|
||||
@ -1086,7 +1111,9 @@ describe('di', () => {
|
||||
@Injectable({providedIn: 'root', useClass: BarService})
|
||||
class FooService {
|
||||
constructor(public dep: FooServiceDep) {}
|
||||
getMessage() { return 'foo'; }
|
||||
getMessage() {
|
||||
return 'foo';
|
||||
}
|
||||
}
|
||||
|
||||
it('should use @Injectable useClass config when token is not provided', () => {
|
||||
@ -1094,18 +1121,20 @@ describe('di', () => {
|
||||
|
||||
@Component({template: ''})
|
||||
class App {
|
||||
constructor(service: FooService) { provider = service; }
|
||||
constructor(service: FooService) {
|
||||
provider = service;
|
||||
}
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule({declarations: [App]});
|
||||
const fixture = TestBed.createComponent(App);
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(provider !.getMessage()).toBe('bar');
|
||||
expect(provider!.getMessage()).toBe('bar');
|
||||
|
||||
// ViewEngine incorrectly uses the original class DI config, instead of the one from useClass.
|
||||
if (ivyEnabled) {
|
||||
expect(provider !.dep.name).toBe('BarServiceDep');
|
||||
expect(provider!.dep.name).toBe('BarServiceDep');
|
||||
}
|
||||
});
|
||||
|
||||
@ -1115,7 +1144,9 @@ describe('di', () => {
|
||||
|
||||
@Component({template: ''})
|
||||
class App {
|
||||
constructor(service: FooService) { provider = service; }
|
||||
constructor(service: FooService) {
|
||||
provider = service;
|
||||
}
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule(
|
||||
@ -1123,7 +1154,7 @@ describe('di', () => {
|
||||
const fixture = TestBed.createComponent(App);
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(provider !.getMessage()).toBe('foo');
|
||||
expect(provider!.getMessage()).toBe('foo');
|
||||
});
|
||||
|
||||
|
||||
@ -1144,13 +1175,13 @@ describe('di', () => {
|
||||
const fixture = TestBed.createComponent(App);
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(directProvider !.getMessage()).toBe('bar');
|
||||
expect(overriddenProvider !.getMessage()).toBe('foo');
|
||||
expect(directProvider!.getMessage()).toBe('bar');
|
||||
expect(overriddenProvider!.getMessage()).toBe('foo');
|
||||
|
||||
// ViewEngine incorrectly uses the original class DI config, instead of the one from useClass.
|
||||
if (ivyEnabled) {
|
||||
expect(directProvider !.dep.name).toBe('BarServiceDep');
|
||||
expect(overriddenProvider !.dep.name).toBe('FooServiceDep');
|
||||
expect(directProvider!.dep.name).toBe('BarServiceDep');
|
||||
expect(overriddenProvider!.dep.name).toBe('FooServiceDep');
|
||||
}
|
||||
});
|
||||
|
||||
@ -1160,20 +1191,21 @@ describe('di', () => {
|
||||
|
||||
@Component({template: ''})
|
||||
class App {
|
||||
constructor(service: FooService) { provider = service; }
|
||||
constructor(service: FooService) {
|
||||
provider = service;
|
||||
}
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule({declarations: [App], providers: [FooService]});
|
||||
const fixture = TestBed.createComponent(App);
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(provider !.getMessage()).toBe('foo');
|
||||
expect(provider !.dep.name).toBe('FooServiceDep');
|
||||
expect(provider!.getMessage()).toBe('foo');
|
||||
expect(provider!.dep.name).toBe('FooServiceDep');
|
||||
});
|
||||
});
|
||||
|
||||
describe('inject', () => {
|
||||
|
||||
it('should inject from parent view', () => {
|
||||
@Directive({selector: '[parentDir]'})
|
||||
class ParentDirective {
|
||||
@ -1182,7 +1214,9 @@ describe('di', () => {
|
||||
@Directive({selector: '[childDir]', exportAs: 'childDir'})
|
||||
class ChildDirective {
|
||||
value: string;
|
||||
constructor(public parent: ParentDirective) { this.value = parent.constructor.name; }
|
||||
constructor(public parent: ParentDirective) {
|
||||
this.value = parent.constructor.name;
|
||||
}
|
||||
}
|
||||
|
||||
@Directive({selector: '[child2Dir]', exportAs: 'child2Dir'})
|
||||
@ -1214,9 +1248,7 @@ describe('di', () => {
|
||||
});
|
||||
|
||||
describe('Special tokens', () => {
|
||||
|
||||
describe('Injector', () => {
|
||||
|
||||
it('should inject the injector', () => {
|
||||
@Directive({selector: '[injectorDir]'})
|
||||
class InjectorDir {
|
||||
@ -1230,8 +1262,8 @@ describe('di', () => {
|
||||
|
||||
@Component({template: '<div injectorDir otherInjectorDir></div>'})
|
||||
class MyComp {
|
||||
@ViewChild(InjectorDir) injectorDir !: InjectorDir;
|
||||
@ViewChild(OtherInjectorDir) otherInjectorDir !: OtherInjectorDir;
|
||||
@ViewChild(InjectorDir) injectorDir!: InjectorDir;
|
||||
@ViewChild(OtherInjectorDir) otherInjectorDir!: OtherInjectorDir;
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule({declarations: [InjectorDir, OtherInjectorDir, MyComp]});
|
||||
@ -1256,7 +1288,7 @@ describe('di', () => {
|
||||
|
||||
@Component({template: '<div injectorDir></div>'})
|
||||
class MyComp {
|
||||
@ViewChild(InjectorDir) injectorDir !: InjectorDir;
|
||||
@ViewChild(InjectorDir) injectorDir!: InjectorDir;
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule({declarations: [InjectorDir, MyComp]});
|
||||
@ -1273,7 +1305,6 @@ describe('di', () => {
|
||||
});
|
||||
|
||||
describe('ElementRef', () => {
|
||||
|
||||
it('should create directive with ElementRef dependencies', () => {
|
||||
@Directive({selector: '[dir]'})
|
||||
class MyDir {
|
||||
@ -1293,8 +1324,8 @@ describe('di', () => {
|
||||
|
||||
@Component({template: '<div dir otherDir></div>'})
|
||||
class MyComp {
|
||||
@ViewChild(MyDir) directive !: MyDir;
|
||||
@ViewChild(MyOtherDir) otherDirective !: MyOtherDir;
|
||||
@ViewChild(MyDir) directive!: MyDir;
|
||||
@ViewChild(MyOtherDir) otherDirective!: MyOtherDir;
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule({declarations: [MyDir, MyOtherDir, MyComp]});
|
||||
@ -1325,7 +1356,7 @@ describe('di', () => {
|
||||
|
||||
@Component({template: '<ng-template dir></ng-template>'})
|
||||
class MyComp {
|
||||
@ViewChild(MyDir) directive !: MyDir;
|
||||
@ViewChild(MyDir) directive!: MyDir;
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule({declarations: [MyDir, MyComp]});
|
||||
@ -1346,7 +1377,9 @@ describe('di', () => {
|
||||
constructor(protected zone: NgZone) {
|
||||
this.subject = new BehaviorSubject<any>(1);
|
||||
// trigger change detection
|
||||
zone.run(() => { this.subject.next(2); });
|
||||
zone.run(() => {
|
||||
this.subject.next(2);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -1360,7 +1393,7 @@ describe('di', () => {
|
||||
template: `<div id="test-id" dir></div>`,
|
||||
})
|
||||
class ChildComp {
|
||||
@ViewChild(DirectiveA) directive !: DirectiveA;
|
||||
@ViewChild(DirectiveA) directive!: DirectiveA;
|
||||
}
|
||||
|
||||
@Component({
|
||||
@ -1368,7 +1401,7 @@ describe('di', () => {
|
||||
template: '...',
|
||||
})
|
||||
class RootComp {
|
||||
public childCompRef !: ComponentRef<ChildComp>;
|
||||
public childCompRef!: ComponentRef<ChildComp>;
|
||||
|
||||
constructor(
|
||||
public factoryResolver: ComponentFactoryResolver, public vcr: ViewContainerRef) {}
|
||||
@ -1404,7 +1437,6 @@ describe('di', () => {
|
||||
});
|
||||
|
||||
describe('TemplateRef', () => {
|
||||
|
||||
@Directive({selector: '[dir]', exportAs: 'dir'})
|
||||
class MyDir {
|
||||
value: string;
|
||||
@ -1426,8 +1458,8 @@ describe('di', () => {
|
||||
template: '<ng-template dir otherDir #dir="dir" #otherDir="otherDir"></ng-template>'
|
||||
})
|
||||
class MyComp {
|
||||
@ViewChild(MyDir) directive !: MyDir;
|
||||
@ViewChild(MyOtherDir) otherDirective !: MyOtherDir;
|
||||
@ViewChild(MyDir) directive!: MyDir;
|
||||
@ViewChild(MyOtherDir) otherDirective!: MyOtherDir;
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule({declarations: [MyDir, MyOtherDir, MyComp]});
|
||||
@ -1470,7 +1502,7 @@ describe('di', () => {
|
||||
}
|
||||
@Component({template: '<div optionalDir></div>'})
|
||||
class MyComp {
|
||||
@ViewChild(OptionalDir) directive !: OptionalDir;
|
||||
@ViewChild(OptionalDir) directive!: OptionalDir;
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule({declarations: [OptionalDir, MyComp]});
|
||||
@ -1499,8 +1531,8 @@ describe('di', () => {
|
||||
}
|
||||
@Component({template: '<div dir otherDir #dir="dir" #otherDir="otherDir"></div>'})
|
||||
class MyComp {
|
||||
@ViewChild(MyDir) directive !: MyDir;
|
||||
@ViewChild(MyOtherDir) otherDirective !: MyOtherDir;
|
||||
@ViewChild(MyDir) directive!: MyDir;
|
||||
@ViewChild(MyOtherDir) otherDirective!: MyOtherDir;
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule({declarations: [MyDir, MyOtherDir, MyComp]});
|
||||
@ -1524,12 +1556,13 @@ describe('di', () => {
|
||||
template: `<ng-template #tmpl>Test</ng-template>`,
|
||||
})
|
||||
class Root {
|
||||
@ViewChild(TemplateRef, {static: true})
|
||||
tmpl !: TemplateRef<any>;
|
||||
@ViewChild(TemplateRef, {static: true}) tmpl!: TemplateRef<any>;
|
||||
|
||||
constructor(public vcr: ViewContainerRef, public vcr2: ViewContainerRef) {}
|
||||
|
||||
ngOnInit(): void { this.vcr.createEmbeddedView(this.tmpl); }
|
||||
ngOnInit(): void {
|
||||
this.vcr.createEmbeddedView(this.tmpl);
|
||||
}
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule({
|
||||
@ -1551,11 +1584,12 @@ describe('di', () => {
|
||||
});
|
||||
|
||||
describe('ChangeDetectorRef', () => {
|
||||
|
||||
@Directive({selector: '[dir]', exportAs: 'dir'})
|
||||
class MyDir {
|
||||
value: string;
|
||||
constructor(public cdr: ChangeDetectorRef) { this.value = (cdr.constructor as any).name; }
|
||||
constructor(public cdr: ChangeDetectorRef) {
|
||||
this.value = (cdr.constructor as any).name;
|
||||
}
|
||||
}
|
||||
@Directive({selector: '[otherDir]', exportAs: 'otherDir'})
|
||||
class MyOtherDir {
|
||||
@ -1571,9 +1605,13 @@ describe('di', () => {
|
||||
|
||||
@Pipe({name: 'pipe'})
|
||||
class MyPipe implements PipeTransform {
|
||||
constructor(public cdr: ChangeDetectorRef) { pipeInstance = this; }
|
||||
constructor(public cdr: ChangeDetectorRef) {
|
||||
pipeInstance = this;
|
||||
}
|
||||
|
||||
transform(value: any): any { return value; }
|
||||
transform(value: any): any {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
@Component({
|
||||
@ -1589,29 +1627,29 @@ describe('di', () => {
|
||||
TestBed.configureTestingModule({declarations: [MyApp, MyPipe], imports: [CommonModule]});
|
||||
const fixture = TestBed.createComponent(MyApp);
|
||||
fixture.detectChanges();
|
||||
expect((pipeInstance !.cdr as ViewRef<MyApp>).context).toBe(fixture.componentInstance);
|
||||
expect((pipeInstance!.cdr as ViewRef<MyApp>).context).toBe(fixture.componentInstance);
|
||||
});
|
||||
|
||||
it('should inject current component ChangeDetectorRef into directives on the same node as components',
|
||||
() => {
|
||||
@Component({selector: 'my-app', template: '<my-comp dir otherDir #dir="dir"></my-comp>'})
|
||||
class MyApp {
|
||||
@ViewChild(MyComp) component !: MyComp;
|
||||
@ViewChild(MyDir) directive !: MyDir;
|
||||
@ViewChild(MyOtherDir) otherDirective !: MyOtherDir;
|
||||
@ViewChild(MyComp) component!: MyComp;
|
||||
@ViewChild(MyDir) directive!: MyDir;
|
||||
@ViewChild(MyOtherDir) otherDirective!: MyOtherDir;
|
||||
}
|
||||
TestBed.configureTestingModule({declarations: [MyApp, MyComp, MyDir, MyOtherDir]});
|
||||
const fixture = TestBed.createComponent(MyApp);
|
||||
fixture.detectChanges();
|
||||
const app = fixture.componentInstance;
|
||||
const comp = fixture.componentInstance.component;
|
||||
expect((comp !.cdr as ViewRef<MyComp>).context).toBe(comp);
|
||||
expect((comp!.cdr as ViewRef<MyComp>).context).toBe(comp);
|
||||
// ChangeDetectorRef is the token, ViewRef has historically been the constructor
|
||||
expect(app.directive.value).toContain('ViewRef');
|
||||
|
||||
// Each ChangeDetectorRef instance should be unique
|
||||
expect(app.directive !.cdr).not.toBe(comp !.cdr);
|
||||
expect(app.directive !.cdr).not.toBe(app.otherDirective !.cdr);
|
||||
expect(app.directive!.cdr).not.toBe(comp!.cdr);
|
||||
expect(app.directive!.cdr).not.toBe(app.otherDirective!.cdr);
|
||||
});
|
||||
|
||||
it('should inject host component ChangeDetectorRef into directives on normal elements',
|
||||
@ -1619,20 +1657,20 @@ describe('di', () => {
|
||||
@Component({selector: 'my-comp', template: '<div dir otherDir #dir="dir"></div>'})
|
||||
class MyComp {
|
||||
constructor(public cdr: ChangeDetectorRef) {}
|
||||
@ViewChild(MyDir) directive !: MyDir;
|
||||
@ViewChild(MyOtherDir) otherDirective !: MyOtherDir;
|
||||
@ViewChild(MyDir) directive!: MyDir;
|
||||
@ViewChild(MyOtherDir) otherDirective!: MyOtherDir;
|
||||
}
|
||||
TestBed.configureTestingModule({declarations: [MyComp, MyDir, MyOtherDir]});
|
||||
const fixture = TestBed.createComponent(MyComp);
|
||||
fixture.detectChanges();
|
||||
const comp = fixture.componentInstance;
|
||||
expect((comp !.cdr as ViewRef<MyComp>).context).toBe(comp);
|
||||
expect((comp!.cdr as ViewRef<MyComp>).context).toBe(comp);
|
||||
// ChangeDetectorRef is the token, ViewRef has historically been the constructor
|
||||
expect(comp.directive.value).toContain('ViewRef');
|
||||
|
||||
// Each ChangeDetectorRef instance should be unique
|
||||
expect(comp.directive !.cdr).not.toBe(comp.cdr);
|
||||
expect(comp.directive !.cdr).not.toBe(comp.otherDirective !.cdr);
|
||||
expect(comp.directive!.cdr).not.toBe(comp.cdr);
|
||||
expect(comp.directive!.cdr).not.toBe(comp.otherDirective!.cdr);
|
||||
});
|
||||
|
||||
it('should inject host component ChangeDetectorRef into directives in a component\'s ContentChildren',
|
||||
@ -1646,22 +1684,22 @@ describe('di', () => {
|
||||
})
|
||||
class MyApp {
|
||||
constructor(public cdr: ChangeDetectorRef) {}
|
||||
@ViewChild(MyComp) component !: MyComp;
|
||||
@ViewChild(MyDir) directive !: MyDir;
|
||||
@ViewChild(MyOtherDir) otherDirective !: MyOtherDir;
|
||||
@ViewChild(MyComp) component!: MyComp;
|
||||
@ViewChild(MyDir) directive!: MyDir;
|
||||
@ViewChild(MyOtherDir) otherDirective!: MyOtherDir;
|
||||
}
|
||||
TestBed.configureTestingModule({declarations: [MyApp, MyComp, MyDir, MyOtherDir]});
|
||||
const fixture = TestBed.createComponent(MyApp);
|
||||
fixture.detectChanges();
|
||||
const app = fixture.componentInstance;
|
||||
expect((app !.cdr as ViewRef<MyApp>).context).toBe(app);
|
||||
expect((app!.cdr as ViewRef<MyApp>).context).toBe(app);
|
||||
const comp = fixture.componentInstance.component;
|
||||
// ChangeDetectorRef is the token, ViewRef has historically been the constructor
|
||||
expect(app.directive.value).toContain('ViewRef');
|
||||
|
||||
// Each ChangeDetectorRef instance should be unique
|
||||
expect(app.directive !.cdr).not.toBe(comp.cdr);
|
||||
expect(app.directive !.cdr).not.toBe(app.otherDirective !.cdr);
|
||||
expect(app.directive!.cdr).not.toBe(comp.cdr);
|
||||
expect(app.directive!.cdr).not.toBe(app.otherDirective!.cdr);
|
||||
});
|
||||
|
||||
it('should inject host component ChangeDetectorRef into directives in embedded views', () => {
|
||||
@ -1674,21 +1712,21 @@ describe('di', () => {
|
||||
class MyComp {
|
||||
showing = true;
|
||||
constructor(public cdr: ChangeDetectorRef) {}
|
||||
@ViewChild(MyDir) directive !: MyDir;
|
||||
@ViewChild(MyOtherDir) otherDirective !: MyOtherDir;
|
||||
@ViewChild(MyDir) directive!: MyDir;
|
||||
@ViewChild(MyOtherDir) otherDirective!: MyOtherDir;
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule({declarations: [MyComp, MyDir, MyOtherDir]});
|
||||
const fixture = TestBed.createComponent(MyComp);
|
||||
fixture.detectChanges();
|
||||
const comp = fixture.componentInstance;
|
||||
expect((comp !.cdr as ViewRef<MyComp>).context).toBe(comp);
|
||||
expect((comp!.cdr as ViewRef<MyComp>).context).toBe(comp);
|
||||
// ChangeDetectorRef is the token, ViewRef has historically been the constructor
|
||||
expect(comp.directive.value).toContain('ViewRef');
|
||||
|
||||
// Each ChangeDetectorRef instance should be unique
|
||||
expect(comp.directive !.cdr).not.toBe(comp.cdr);
|
||||
expect(comp.directive !.cdr).not.toBe(comp.otherDirective !.cdr);
|
||||
expect(comp.directive!.cdr).not.toBe(comp.cdr);
|
||||
expect(comp.directive!.cdr).not.toBe(comp.otherDirective!.cdr);
|
||||
});
|
||||
|
||||
it('should inject host component ChangeDetectorRef into directives on containers', () => {
|
||||
@ -1697,21 +1735,21 @@ describe('di', () => {
|
||||
class MyComp {
|
||||
showing = true;
|
||||
constructor(public cdr: ChangeDetectorRef) {}
|
||||
@ViewChild(MyDir) directive !: MyDir;
|
||||
@ViewChild(MyOtherDir) otherDirective !: MyOtherDir;
|
||||
@ViewChild(MyDir) directive!: MyDir;
|
||||
@ViewChild(MyOtherDir) otherDirective!: MyOtherDir;
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule({declarations: [MyComp, MyDir, MyOtherDir]});
|
||||
const fixture = TestBed.createComponent(MyComp);
|
||||
fixture.detectChanges();
|
||||
const comp = fixture.componentInstance;
|
||||
expect((comp !.cdr as ViewRef<MyComp>).context).toBe(comp);
|
||||
expect((comp!.cdr as ViewRef<MyComp>).context).toBe(comp);
|
||||
// ChangeDetectorRef is the token, ViewRef has historically been the constructor
|
||||
expect(comp.directive.value).toContain('ViewRef');
|
||||
|
||||
// Each ChangeDetectorRef instance should be unique
|
||||
expect(comp.directive !.cdr).not.toBe(comp.cdr);
|
||||
expect(comp.directive !.cdr).not.toBe(comp.otherDirective !.cdr);
|
||||
expect(comp.directive!.cdr).not.toBe(comp.cdr);
|
||||
expect(comp.directive!.cdr).not.toBe(comp.otherDirective!.cdr);
|
||||
});
|
||||
|
||||
it('should inject host component ChangeDetectorRef into directives on ng-container', () => {
|
||||
@ -1719,7 +1757,9 @@ describe('di', () => {
|
||||
|
||||
@Directive({selector: '[getCDR]'})
|
||||
class MyDirective {
|
||||
constructor(public cdr: ChangeDetectorRef) { dirInstance = this; }
|
||||
constructor(public cdr: ChangeDetectorRef) {
|
||||
dirInstance = this;
|
||||
}
|
||||
}
|
||||
|
||||
@Component({
|
||||
@ -1733,7 +1773,7 @@ describe('di', () => {
|
||||
TestBed.configureTestingModule({declarations: [MyApp, MyDirective]});
|
||||
const fixture = TestBed.createComponent(MyApp);
|
||||
fixture.detectChanges();
|
||||
expect((dirInstance !.cdr as ViewRef<MyApp>).context).toBe(fixture.componentInstance);
|
||||
expect((dirInstance!.cdr as ViewRef<MyApp>).context).toBe(fixture.componentInstance);
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -1747,7 +1787,7 @@ describe('di', () => {
|
||||
|
||||
@Component({template: '<div injectorDir></div>'})
|
||||
class MyComp {
|
||||
@ViewChild(InjectorDir) injectorDirInstance !: InjectorDir;
|
||||
@ViewChild(InjectorDir) injectorDirInstance!: InjectorDir;
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule({declarations: [InjectorDir, MyComp]});
|
||||
@ -1840,7 +1880,7 @@ describe('di', () => {
|
||||
providers: [{provide: LOCALE_ID, useValue: 'en-GB'}]
|
||||
})
|
||||
class MyComp {
|
||||
@ViewChild(MyDir) myDir !: MyDir;
|
||||
@ViewChild(MyDir) myDir!: MyDir;
|
||||
constructor(@Inject(LOCALE_ID) public localeId: string) {}
|
||||
}
|
||||
|
||||
@ -1851,7 +1891,6 @@ describe('di', () => {
|
||||
});
|
||||
|
||||
describe('@Attribute', () => {
|
||||
|
||||
it('should inject attributes', () => {
|
||||
@Directive({selector: '[dir]'})
|
||||
class MyDir {
|
||||
@ -1862,7 +1901,7 @@ describe('di', () => {
|
||||
|
||||
@Component({template: '<div dir exist="existValue" other="ignore"></div>'})
|
||||
class MyComp {
|
||||
@ViewChild(MyDir) directiveInstance !: MyDir;
|
||||
@ViewChild(MyDir) directiveInstance!: MyDir;
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule({declarations: [MyDir, MyComp]});
|
||||
@ -1886,7 +1925,7 @@ describe('di', () => {
|
||||
@Component(
|
||||
{template: '<ng-template dir="initial" exist="existValue" other="ignore"></ng-template>'})
|
||||
class MyComp {
|
||||
@ViewChild(MyDir) directiveInstance !: MyDir;
|
||||
@ViewChild(MyDir) directiveInstance!: MyDir;
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule({declarations: [MyDir, MyComp]});
|
||||
@ -1911,7 +1950,7 @@ describe('di', () => {
|
||||
template: '<ng-container dir="initial" exist="existValue" other="ignore"></ng-container>'
|
||||
})
|
||||
class MyComp {
|
||||
@ViewChild(MyDir) directiveInstance !: MyDir;
|
||||
@ViewChild(MyDir) directiveInstance!: MyDir;
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule({declarations: [MyDir, MyComp]});
|
||||
@ -1938,7 +1977,7 @@ describe('di', () => {
|
||||
'<div dir style="margin: 1px; color: red;" class="hello there" other-attr="value"></div>'
|
||||
})
|
||||
class MyComp {
|
||||
@ViewChild(MyDir) directiveInstance !: MyDir;
|
||||
@ViewChild(MyDir) directiveInstance!: MyDir;
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule({declarations: [MyDir, MyComp]});
|
||||
@ -1966,7 +2005,7 @@ describe('di', () => {
|
||||
template: '<div dir exist="existValue" svg:exist="testExistValue" other="otherValue"></div>'
|
||||
})
|
||||
class MyComp {
|
||||
@ViewChild(MyDir) directiveInstance !: MyDir;
|
||||
@ViewChild(MyDir) directiveInstance!: MyDir;
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule({declarations: [MyDir, MyComp]});
|
||||
@ -1983,7 +2022,7 @@ describe('di', () => {
|
||||
it('should not inject attributes representing bindings and outputs', () => {
|
||||
@Directive({selector: '[dir]'})
|
||||
class MyDir {
|
||||
@Input() binding !: string;
|
||||
@Input() binding!: string;
|
||||
@Output() output = new EventEmitter();
|
||||
constructor(
|
||||
@Attribute('exist') public exist: string,
|
||||
@ -1997,7 +2036,7 @@ describe('di', () => {
|
||||
'<div dir exist="existValue" [binding]="bindingValue" (output)="outputValue" other="otherValue" ignore="ignoreValue"></div>'
|
||||
})
|
||||
class MyComp {
|
||||
@ViewChild(MyDir) directiveInstance !: MyDir;
|
||||
@ViewChild(MyDir) directiveInstance!: MyDir;
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule({declarations: [MyDir, MyComp]});
|
||||
@ -2016,13 +2055,17 @@ describe('di', () => {
|
||||
it('should support dependencies in Pipes used inside ICUs', () => {
|
||||
@Injectable()
|
||||
class MyService {
|
||||
transform(value: string): string { return `${value} (transformed)`; }
|
||||
transform(value: string): string {
|
||||
return `${value} (transformed)`;
|
||||
}
|
||||
}
|
||||
|
||||
@Pipe({name: 'somePipe'})
|
||||
class MyPipe {
|
||||
constructor(private service: MyService) {}
|
||||
transform(value: any): any { return this.service.transform(value); }
|
||||
transform(value: any): any {
|
||||
return this.service.transform(value);
|
||||
}
|
||||
}
|
||||
|
||||
@Component({
|
||||
@ -2051,13 +2094,17 @@ describe('di', () => {
|
||||
it('should support dependencies in Pipes used inside i18n blocks', () => {
|
||||
@Injectable()
|
||||
class MyService {
|
||||
transform(value: string): string { return `${value} (transformed)`; }
|
||||
transform(value: string): string {
|
||||
return `${value} (transformed)`;
|
||||
}
|
||||
}
|
||||
|
||||
@Pipe({name: 'somePipe'})
|
||||
class MyPipe {
|
||||
constructor(private service: MyService) {}
|
||||
transform(value: any): any { return this.service.transform(value); }
|
||||
transform(value: any): any {
|
||||
return this.service.transform(value);
|
||||
}
|
||||
}
|
||||
|
||||
@Component({
|
||||
@ -2071,10 +2118,12 @@ describe('di', () => {
|
||||
class MyComp {
|
||||
count = '2';
|
||||
|
||||
@ViewChild('target', {read: ViewContainerRef}) target !: ViewContainerRef;
|
||||
@ViewChild('source', {read: TemplateRef}) source !: TemplateRef<any>;
|
||||
@ViewChild('target', {read: ViewContainerRef}) target!: ViewContainerRef;
|
||||
@ViewChild('source', {read: TemplateRef}) source!: TemplateRef<any>;
|
||||
|
||||
create() { this.target.createEmbeddedView(this.source); }
|
||||
create() {
|
||||
this.target.createEmbeddedView(this.source);
|
||||
}
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule({
|
||||
@ -2109,8 +2158,8 @@ describe('di', () => {
|
||||
|
||||
@Component({template: `<div dirA> <child></child> </div>`})
|
||||
class App {
|
||||
@ViewChild(DirA) dirA !: DirA;
|
||||
@ViewChild(Child) child !: Child;
|
||||
@ViewChild(DirA) dirA!: DirA;
|
||||
@ViewChild(Child) child!: Child;
|
||||
}
|
||||
|
||||
const fixture = TestBed.configureTestingModule({declarations: [DirA, DirB, App, Child]})
|
||||
|
@ -13,9 +13,7 @@ import {TestBed} from '@angular/core/testing';
|
||||
import {By} from '@angular/platform-browser';
|
||||
|
||||
describe('directives', () => {
|
||||
|
||||
describe('matching', () => {
|
||||
|
||||
@Directive({selector: 'ng-template[test]'})
|
||||
class TestDirective {
|
||||
constructor(public templateRef: TemplateRef<any>) {}
|
||||
@ -241,7 +239,9 @@ describe('directives', () => {
|
||||
|
||||
@Directive({selector: '[dir]'})
|
||||
class MyDir {
|
||||
ngOnInit() { calls.push('MyDir.ngOnInit'); }
|
||||
ngOnInit() {
|
||||
calls.push('MyDir.ngOnInit');
|
||||
}
|
||||
}
|
||||
|
||||
@Component({
|
||||
@ -266,7 +266,9 @@ describe('directives', () => {
|
||||
@Directive({selector: 'svg[dir]'})
|
||||
class MyDir {
|
||||
constructor(private el: ElementRef) {}
|
||||
ngOnInit() { calls.push(`MyDir.ngOnInit: ${this.el.nativeElement.tagName}`); }
|
||||
ngOnInit() {
|
||||
calls.push(`MyDir.ngOnInit: ${this.el.nativeElement.tagName}`);
|
||||
}
|
||||
}
|
||||
|
||||
@Component({
|
||||
@ -289,7 +291,9 @@ describe('directives', () => {
|
||||
@Directive({selector: 'text[dir]'})
|
||||
class MyDir {
|
||||
constructor(private el: ElementRef) {}
|
||||
ngOnInit() { calls.push(`MyDir.ngOnInit: ${this.el.nativeElement.tagName}`); }
|
||||
ngOnInit() {
|
||||
calls.push(`MyDir.ngOnInit: ${this.el.nativeElement.tagName}`);
|
||||
}
|
||||
}
|
||||
|
||||
@Component({
|
||||
@ -311,13 +315,13 @@ describe('directives', () => {
|
||||
|
||||
@Directive({selector: '[test]'})
|
||||
class MyDir {
|
||||
constructor() { logs.push('MyDir.contructor'); }
|
||||
constructor() {
|
||||
logs.push('MyDir.contructor');
|
||||
}
|
||||
|
||||
@Input('test')
|
||||
myInput = '';
|
||||
@Input('test') myInput = '';
|
||||
|
||||
@Input('disabled')
|
||||
myInput2 = '';
|
||||
@Input('disabled') myInput2 = '';
|
||||
}
|
||||
|
||||
@Component({
|
||||
@ -338,7 +342,6 @@ describe('directives', () => {
|
||||
|
||||
expect(logs).toEqual(['MyDir.contructor']);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('inputs', () => {
|
||||
@ -346,7 +349,9 @@ describe('directives', () => {
|
||||
let dirInstance: WithInput;
|
||||
@Directive({selector: '[dir]'})
|
||||
class WithInput {
|
||||
constructor() { dirInstance = this; }
|
||||
constructor() {
|
||||
dirInstance = this;
|
||||
}
|
||||
@Input() dir: string = '';
|
||||
}
|
||||
|
||||
@ -362,14 +367,16 @@ describe('directives', () => {
|
||||
const fixture = TestBed.createComponent(TestComp);
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(dirInstance !.dir).toBe('Hello');
|
||||
expect(dirInstance!.dir).toBe('Hello');
|
||||
});
|
||||
|
||||
it('should allow directive inputs (as an interpolated prop) on <ng-template>', () => {
|
||||
let dirInstance: WithInput;
|
||||
@Directive({selector: '[dir]'})
|
||||
class WithInput {
|
||||
constructor() { dirInstance = this; }
|
||||
constructor() {
|
||||
dirInstance = this;
|
||||
}
|
||||
@Input() dir: string = '';
|
||||
}
|
||||
|
||||
@ -385,7 +392,7 @@ describe('directives', () => {
|
||||
const fixture = TestBed.createComponent(TestComp);
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(dirInstance !.dir).toBe('Hello');
|
||||
expect(dirInstance!.dir).toBe('Hello');
|
||||
});
|
||||
|
||||
it('should allow directive inputs (as an interpolated prop) on <ng-template> with structural directives',
|
||||
@ -393,7 +400,9 @@ describe('directives', () => {
|
||||
let dirInstance: WithInput;
|
||||
@Directive({selector: '[dir]'})
|
||||
class WithInput {
|
||||
constructor() { dirInstance = this; }
|
||||
constructor() {
|
||||
dirInstance = this;
|
||||
}
|
||||
@Input() dir: string = '';
|
||||
}
|
||||
|
||||
@ -409,7 +418,7 @@ describe('directives', () => {
|
||||
const fixture = TestBed.createComponent(TestComp);
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(dirInstance !.dir).toBe('Hello');
|
||||
expect(dirInstance!.dir).toBe('Hello');
|
||||
});
|
||||
});
|
||||
|
||||
@ -433,7 +442,7 @@ describe('directives', () => {
|
||||
expect(fixture.componentInstance.testDir).toBeTruthy();
|
||||
expect(fixture.componentInstance.value).toBe(false);
|
||||
|
||||
fixture.componentInstance.testDir !.out.emit();
|
||||
fixture.componentInstance.testDir!.out.emit();
|
||||
fixture.detectChanges();
|
||||
expect(fixture.componentInstance.value).toBe(true);
|
||||
});
|
||||
@ -459,7 +468,6 @@ describe('directives', () => {
|
||||
fixture.detectChanges();
|
||||
expect(fixture.componentInstance.value).toBeTruthy();
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('attribute shadowing behaviors', () => {
|
||||
@ -471,8 +479,7 @@ describe('directives', () => {
|
||||
selector: '[dir-with-title]',
|
||||
})
|
||||
class DirWithTitle {
|
||||
@Input()
|
||||
title = '';
|
||||
@Input() title = '';
|
||||
}
|
||||
|
||||
it('should set both the div attribute and the directive input for `title="value"`', () => {
|
||||
@ -711,8 +718,12 @@ describe('directives', () => {
|
||||
const log: string[] = [];
|
||||
@Directive({selector: '[dir]'})
|
||||
class DirectiveA {
|
||||
constructor() { log.push('DirectiveA.constructor'); }
|
||||
ngOnInit() { log.push('DirectiveA.ngOnInit'); }
|
||||
constructor() {
|
||||
log.push('DirectiveA.constructor');
|
||||
}
|
||||
ngOnInit() {
|
||||
log.push('DirectiveA.ngOnInit');
|
||||
}
|
||||
}
|
||||
|
||||
@NgModule({
|
||||
@ -724,8 +735,12 @@ describe('directives', () => {
|
||||
|
||||
@Directive({selector: '[dir]'})
|
||||
class DirectiveB {
|
||||
constructor() { log.push('DirectiveB.constructor'); }
|
||||
ngOnInit() { log.push('DirectiveB.ngOnInit'); }
|
||||
constructor() {
|
||||
log.push('DirectiveB.constructor');
|
||||
}
|
||||
ngOnInit() {
|
||||
log.push('DirectiveB.ngOnInit');
|
||||
}
|
||||
}
|
||||
|
||||
@Component({
|
||||
@ -752,8 +767,12 @@ describe('directives', () => {
|
||||
const log: string[] = [];
|
||||
@Directive({selector: '[dir]'})
|
||||
class DirectiveA {
|
||||
constructor() { log.push('DirectiveA.constructor'); }
|
||||
ngOnInit() { log.push('DirectiveA.ngOnInit'); }
|
||||
constructor() {
|
||||
log.push('DirectiveA.constructor');
|
||||
}
|
||||
ngOnInit() {
|
||||
log.push('DirectiveA.ngOnInit');
|
||||
}
|
||||
}
|
||||
|
||||
@NgModule({
|
||||
@ -765,8 +784,12 @@ describe('directives', () => {
|
||||
|
||||
@Directive({selector: '[dir]'})
|
||||
class DirectiveB {
|
||||
constructor() { log.push('DirectiveB.constructor'); }
|
||||
ngOnInit() { log.push('DirectiveB.ngOnInit'); }
|
||||
constructor() {
|
||||
log.push('DirectiveB.constructor');
|
||||
}
|
||||
ngOnInit() {
|
||||
log.push('DirectiveB.ngOnInit');
|
||||
}
|
||||
}
|
||||
|
||||
@NgModule({
|
||||
@ -795,6 +818,5 @@ describe('directives', () => {
|
||||
'DirectiveB.ngOnInit'
|
||||
]);
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
|
@ -48,12 +48,16 @@ onlyInIvy('Ivy-specific utilities').describe('discovery utils', () => {
|
||||
@Component(
|
||||
{selector: 'child', template: '<p></p>', providers: [{provide: String, useValue: 'Child'}]})
|
||||
class Child {
|
||||
constructor() { childComponent.push(this); }
|
||||
constructor() {
|
||||
childComponent.push(this);
|
||||
}
|
||||
}
|
||||
|
||||
@Directive({selector: '[dirA]', exportAs: 'dirA'})
|
||||
class DirectiveA {
|
||||
constructor() { dirA.push(this); }
|
||||
constructor() {
|
||||
dirA.push(this);
|
||||
}
|
||||
}
|
||||
|
||||
@Component({
|
||||
@ -69,9 +73,13 @@ onlyInIvy('Ivy-specific utilities').describe('discovery utils', () => {
|
||||
})
|
||||
class MyApp {
|
||||
text: string = 'INIT';
|
||||
constructor() { myApp = this; }
|
||||
constructor() {
|
||||
myApp = this;
|
||||
}
|
||||
|
||||
log(event: any) { log.push(event); }
|
||||
log(event: any) {
|
||||
log.push(event);
|
||||
}
|
||||
}
|
||||
|
||||
describe('getComponent', () => {
|
||||
@ -112,7 +120,7 @@ onlyInIvy('Ivy-specific utilities').describe('discovery utils', () => {
|
||||
});
|
||||
it('should return context from element', () => {
|
||||
expect(getContext<MyApp>(child[0])).toEqual(myApp);
|
||||
expect(getContext<{$implicit: boolean}>(child[2]) !.$implicit).toEqual(true);
|
||||
expect(getContext<{$implicit: boolean}>(child[2])!.$implicit).toEqual(true);
|
||||
expect(getContext<Child>(p[0])).toEqual(childComponent[0]);
|
||||
});
|
||||
});
|
||||
@ -264,7 +272,7 @@ onlyInIvy('Ivy-specific utilities').describe('discovery utils', () => {
|
||||
|
||||
it('should work on templates', () => {
|
||||
const templateComment = Array.from((fixture.nativeElement as HTMLElement).childNodes)
|
||||
.find((node: ChildNode) => node.nodeType === Node.COMMENT_NODE) !;
|
||||
.find((node: ChildNode) => node.nodeType === Node.COMMENT_NODE)!;
|
||||
const lContext = loadLContext(templateComment);
|
||||
expect(lContext).toBeDefined();
|
||||
expect(lContext.native as any).toBe(templateComment);
|
||||
@ -274,7 +282,7 @@ onlyInIvy('Ivy-specific utilities').describe('discovery utils', () => {
|
||||
const ngContainerComment = Array.from((fixture.nativeElement as HTMLElement).childNodes)
|
||||
.find(
|
||||
(node: ChildNode) => node.nodeType === Node.COMMENT_NODE &&
|
||||
node.textContent === `ng-container`) !;
|
||||
node.textContent === `ng-container`)!;
|
||||
const lContext = loadLContext(ngContainerComment);
|
||||
expect(lContext).toBeDefined();
|
||||
expect(lContext.native as any).toBe(ngContainerComment);
|
||||
@ -285,7 +293,6 @@ onlyInIvy('Ivy-specific utilities').describe('discovery utils', () => {
|
||||
onlyInIvy('Ivy-specific utilities').describe('discovery utils deprecated', () => {
|
||||
describe('getRootComponents()', () => {
|
||||
it('should return a list of the root components of the application from an element', () => {
|
||||
|
||||
@Component({selector: 'inner-comp', template: '<div></div>'})
|
||||
class InnerComp {
|
||||
}
|
||||
@ -299,13 +306,13 @@ onlyInIvy('Ivy-specific utilities').describe('discovery utils deprecated', () =>
|
||||
fixture.detectChanges();
|
||||
|
||||
const hostElm = fixture.nativeElement;
|
||||
const innerElm = hostElm.querySelector('inner-comp') !;
|
||||
const divElm = hostElm.querySelector('div') !;
|
||||
const innerElm = hostElm.querySelector('inner-comp')!;
|
||||
const divElm = hostElm.querySelector('div')!;
|
||||
const component = fixture.componentInstance;
|
||||
|
||||
expect(getRootComponents(hostElm) !).toEqual([component]);
|
||||
expect(getRootComponents(innerElm) !).toEqual([component]);
|
||||
expect(getRootComponents(divElm) !).toEqual([component]);
|
||||
expect(getRootComponents(hostElm)!).toEqual([component]);
|
||||
expect(getRootComponents(innerElm)!).toEqual([component]);
|
||||
expect(getRootComponents(divElm)!).toEqual([component]);
|
||||
});
|
||||
});
|
||||
|
||||
@ -331,9 +338,9 @@ onlyInIvy('Ivy-specific utilities').describe('discovery utils deprecated', () =>
|
||||
`
|
||||
})
|
||||
class Comp {
|
||||
@ViewChild(MyDir1) myDir1Instance !: MyDir1;
|
||||
@ViewChild(MyDir2) myDir2Instance !: MyDir2;
|
||||
@ViewChild(MyDir3) myDir3Instance !: MyDir3;
|
||||
@ViewChild(MyDir1) myDir1Instance!: MyDir1;
|
||||
@ViewChild(MyDir2) myDir2Instance!: MyDir2;
|
||||
@ViewChild(MyDir3) myDir3Instance!: MyDir3;
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule({declarations: [Comp, MyDir1, MyDir2, MyDir3]});
|
||||
@ -345,19 +352,17 @@ onlyInIvy('Ivy-specific utilities').describe('discovery utils deprecated', () =>
|
||||
|
||||
const elm1 = elements[0];
|
||||
const elm1Dirs = getDirectives(elm1);
|
||||
expect(elm1Dirs).toContain(fixture.componentInstance.myDir1Instance !);
|
||||
expect(elm1Dirs).toContain(fixture.componentInstance.myDir2Instance !);
|
||||
expect(elm1Dirs).toContain(fixture.componentInstance.myDir1Instance!);
|
||||
expect(elm1Dirs).toContain(fixture.componentInstance.myDir2Instance!);
|
||||
|
||||
const elm2 = elements[1];
|
||||
const elm2Dirs = getDirectives(elm2);
|
||||
expect(elm2Dirs).toContain(fixture.componentInstance.myDir3Instance !);
|
||||
expect(elm2Dirs).toContain(fixture.componentInstance.myDir3Instance!);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getInjector', () => {
|
||||
|
||||
it('should return an injector that can return directive instances', () => {
|
||||
|
||||
@Component({template: ''})
|
||||
class Comp {
|
||||
}
|
||||
@ -386,7 +391,6 @@ onlyInIvy('Ivy-specific utilities').describe('discovery utils deprecated', () =>
|
||||
|
||||
describe('getLocalRefs', () => {
|
||||
it('should return a map of local refs for an element', () => {
|
||||
|
||||
@Directive({selector: '[myDir]', exportAs: 'myDir'})
|
||||
class MyDir {
|
||||
}
|
||||
@ -399,7 +403,7 @@ onlyInIvy('Ivy-specific utilities').describe('discovery utils deprecated', () =>
|
||||
const fixture = TestBed.createComponent(Comp);
|
||||
fixture.detectChanges();
|
||||
|
||||
const divEl = fixture.nativeElement.querySelector('div') !;
|
||||
const divEl = fixture.nativeElement.querySelector('div')!;
|
||||
const localRefs = getLocalRefs(divEl);
|
||||
|
||||
expect(localRefs.elRef.tagName.toLowerCase()).toBe('div');
|
||||
@ -416,7 +420,7 @@ onlyInIvy('Ivy-specific utilities').describe('discovery utils deprecated', () =>
|
||||
const fixture = TestBed.createComponent(Comp);
|
||||
fixture.detectChanges();
|
||||
|
||||
const divEl = fixture.nativeElement.querySelector('div') !;
|
||||
const divEl = fixture.nativeElement.querySelector('div')!;
|
||||
const localRefs = getLocalRefs(divEl);
|
||||
|
||||
expect(localRefs.elRef.tagName.toLowerCase()).toBe('div');
|
||||
@ -439,11 +443,11 @@ onlyInIvy('Ivy-specific utilities').describe('discovery utils deprecated', () =>
|
||||
const fixture = TestBed.createComponent(Comp);
|
||||
fixture.detectChanges();
|
||||
|
||||
const parent = fixture.nativeElement.querySelector('.parent') !;
|
||||
const child = fixture.nativeElement.querySelector('.child') !;
|
||||
const parent = fixture.nativeElement.querySelector('.parent')!;
|
||||
const child = fixture.nativeElement.querySelector('.child')!;
|
||||
|
||||
const parentDebug = getDebugNode(parent) !;
|
||||
const childDebug = getDebugNode(child) !;
|
||||
const parentDebug = getDebugNode(parent)!;
|
||||
const childDebug = getDebugNode(child)!;
|
||||
|
||||
expect(parentDebug.native).toBe(parent);
|
||||
expect(childDebug.native).toBe(child);
|
||||
@ -472,8 +476,8 @@ onlyInIvy('Ivy-specific utilities').describe('discovery utils deprecated', () =>
|
||||
const fixture = TestBed.createComponent(Comp);
|
||||
fixture.detectChanges();
|
||||
|
||||
const child = fixture.nativeElement.querySelector('child-comp') !;
|
||||
const childDebug = getDebugNode(child) !;
|
||||
const child = fixture.nativeElement.querySelector('child-comp')!;
|
||||
const childDebug = getDebugNode(child)!;
|
||||
|
||||
expect(childDebug.native).toBe(child);
|
||||
expect(getElementStyles(child)).toEqual({
|
||||
|
@ -10,7 +10,6 @@ import {Component, Input} from '@angular/core';
|
||||
import {TestBed} from '@angular/core/testing';
|
||||
|
||||
describe('embedded views', () => {
|
||||
|
||||
it('should correctly resolve the implicit receiver in expressions', () => {
|
||||
const items: string[] = [];
|
||||
|
||||
@ -27,7 +26,9 @@ describe('embedded views', () => {
|
||||
})
|
||||
class TestCmp {
|
||||
item: string = 'CmpItem';
|
||||
addItem() { items.push(this.item); }
|
||||
addItem() {
|
||||
items.push(this.item);
|
||||
}
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule({declarations: [ChildCmp, TestCmp]});
|
||||
@ -36,8 +37,8 @@ describe('embedded views', () => {
|
||||
|
||||
const childCmp: ChildCmp = fixture.debugElement.children[0].componentInstance;
|
||||
|
||||
childCmp.addItemFn !();
|
||||
childCmp.addItemFn !();
|
||||
childCmp.addItemFn!();
|
||||
childCmp.addItemFn!();
|
||||
|
||||
expect(items).toEqual(['CmpItem', 'CmpItem']);
|
||||
});
|
||||
@ -71,5 +72,4 @@ describe('embedded views', () => {
|
||||
|
||||
expect(fixture.nativeElement.textContent).toBe('HelloHello');
|
||||
});
|
||||
|
||||
});
|
||||
|
@ -247,7 +247,13 @@ class DirWithCompInput {
|
||||
class DirToReferenceWithPreOrderHooks implements OnInit, OnChanges, DoCheck {
|
||||
@Input() in : any = null;
|
||||
name = 'Drew';
|
||||
ngOnChanges(changes: SimpleChanges) { this.name += '!'; }
|
||||
ngOnInit() { this.name += '?'; }
|
||||
ngDoCheck() { this.name += '@'; }
|
||||
ngOnChanges(changes: SimpleChanges) {
|
||||
this.name += '!';
|
||||
}
|
||||
ngOnInit() {
|
||||
this.name += '?';
|
||||
}
|
||||
ngDoCheck() {
|
||||
this.name += '@';
|
||||
}
|
||||
}
|
||||
|
@ -121,10 +121,9 @@ describe('host bindings', () => {
|
||||
class ParentCmp {
|
||||
private _prop = '';
|
||||
|
||||
@ViewChild('template', {read: ViewContainerRef})
|
||||
vcr: ViewContainerRef = null !;
|
||||
@ViewChild('template', {read: ViewContainerRef}) vcr: ViewContainerRef = null!;
|
||||
|
||||
private child: ComponentRef<ChildCmp> = null !;
|
||||
private child: ComponentRef<ChildCmp> = null!;
|
||||
|
||||
@Input()
|
||||
set prop(value: string) {
|
||||
@ -137,10 +136,11 @@ describe('host bindings', () => {
|
||||
}
|
||||
}
|
||||
|
||||
get prop() { return this._prop; }
|
||||
get prop() {
|
||||
return this._prop;
|
||||
}
|
||||
|
||||
@Input()
|
||||
prop2 = 0;
|
||||
@Input() prop2 = 0;
|
||||
|
||||
ngAfterViewInit() {
|
||||
const factory = this.componentFactoryResolver.resolveComponentFactory(ChildCmp);
|
||||
@ -212,7 +212,6 @@ describe('host bindings', () => {
|
||||
onlyInIvy('[style.prop] and [class.name] prioritization is a new feature')
|
||||
.it('should prioritize styling present in the order of directive hostBinding evaluation, but consider sub-classed directive styling to be the most important',
|
||||
() => {
|
||||
|
||||
@Component({template: '<div child-dir sibling-dir></div>'})
|
||||
class MyApp {
|
||||
}
|
||||
@ -220,37 +219,55 @@ describe('host bindings', () => {
|
||||
@Directive({selector: '[parent-dir]'})
|
||||
class ParentDir {
|
||||
@HostBinding('style.width')
|
||||
get width1() { return '100px'; }
|
||||
get width1() {
|
||||
return '100px';
|
||||
}
|
||||
|
||||
@HostBinding('style.height')
|
||||
get height1() { return '100px'; }
|
||||
get height1() {
|
||||
return '100px';
|
||||
}
|
||||
|
||||
@HostBinding('style.color')
|
||||
get color1() { return 'red'; }
|
||||
get color1() {
|
||||
return 'red';
|
||||
}
|
||||
}
|
||||
|
||||
@Directive({selector: '[child-dir]'})
|
||||
class ChildDir extends ParentDir {
|
||||
@HostBinding('style.width')
|
||||
get width2() { return '200px'; }
|
||||
get width2() {
|
||||
return '200px';
|
||||
}
|
||||
|
||||
@HostBinding('style.height')
|
||||
get height2() { return '200px'; }
|
||||
get height2() {
|
||||
return '200px';
|
||||
}
|
||||
}
|
||||
|
||||
@Directive({selector: '[sibling-dir]'})
|
||||
class SiblingDir {
|
||||
@HostBinding('style.width')
|
||||
get width3() { return '300px'; }
|
||||
get width3() {
|
||||
return '300px';
|
||||
}
|
||||
|
||||
@HostBinding('style.height')
|
||||
get height3() { return '300px'; }
|
||||
get height3() {
|
||||
return '300px';
|
||||
}
|
||||
|
||||
@HostBinding('style.opacity')
|
||||
get opacity3() { return '0.5'; }
|
||||
get opacity3() {
|
||||
return '0.5';
|
||||
}
|
||||
|
||||
@HostBinding('style.color')
|
||||
get color1() { return 'blue'; }
|
||||
get color1() {
|
||||
return 'blue';
|
||||
}
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule(
|
||||
@ -295,25 +312,22 @@ describe('host bindings', () => {
|
||||
fixture.detectChanges();
|
||||
}).not.toThrow();
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
@Directive({selector: '[hostBindingDir]'})
|
||||
class HostBindingDir {
|
||||
@HostBinding()
|
||||
id = 'foo';
|
||||
@HostBinding() id = 'foo';
|
||||
}
|
||||
|
||||
it('should support host bindings in directives', () => {
|
||||
@Directive({selector: '[dir]'})
|
||||
class Dir {
|
||||
@HostBinding('className')
|
||||
klass = 'foo';
|
||||
@HostBinding('className') klass = 'foo';
|
||||
}
|
||||
|
||||
@Component({template: '<span dir></span>'})
|
||||
class App {
|
||||
@ViewChild(Dir) directiveInstance !: Dir;
|
||||
@ViewChild(Dir) directiveInstance!: Dir;
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule({declarations: [App, Dir]});
|
||||
@ -333,8 +347,7 @@ describe('host bindings', () => {
|
||||
it('should support host bindings on root component', () => {
|
||||
@Component({template: ''})
|
||||
class HostBindingComp {
|
||||
@HostBinding()
|
||||
title = 'my-title';
|
||||
@HostBinding() title = 'my-title';
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule({declarations: [HostBindingComp]});
|
||||
@ -365,8 +378,7 @@ describe('host bindings', () => {
|
||||
class App {
|
||||
constructor(public serviceOne: ServiceOne, public serviceTwo: ServiceTwo) {}
|
||||
|
||||
@HostBinding()
|
||||
title = 'my-title';
|
||||
@HostBinding() title = 'my-title';
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule({declarations: [App]});
|
||||
@ -390,8 +402,7 @@ describe('host bindings', () => {
|
||||
|
||||
@Component({selector: 'host-title-comp', template: ''})
|
||||
class HostTitleComp {
|
||||
@HostBinding()
|
||||
title = 'my-title';
|
||||
@HostBinding() title = 'my-title';
|
||||
}
|
||||
|
||||
@Component({
|
||||
@ -402,7 +413,7 @@ describe('host bindings', () => {
|
||||
`
|
||||
})
|
||||
class App {
|
||||
@ViewChild(HostBindingDir) hostBindingDir !: HostBindingDir;
|
||||
@ViewChild(HostBindingDir) hostBindingDir!: HostBindingDir;
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule({declarations: [App, SomeDir, HostTitleComp, HostBindingDir]});
|
||||
@ -415,7 +426,7 @@ describe('host bindings', () => {
|
||||
expect(hostBindingDiv.id).toEqual('foo');
|
||||
expect(hostTitleComp.title).toEqual('my-title');
|
||||
|
||||
fixture.componentInstance.hostBindingDir !.id = 'bar';
|
||||
fixture.componentInstance.hostBindingDir!.id = 'bar';
|
||||
fixture.detectChanges();
|
||||
expect(hostBindingDiv.id).toEqual('bar');
|
||||
});
|
||||
@ -423,8 +434,7 @@ describe('host bindings', () => {
|
||||
it('should support consecutive components with host bindings', () => {
|
||||
@Component({selector: 'host-binding-comp', template: ''})
|
||||
class HostBindingComp {
|
||||
@HostBinding()
|
||||
id = 'blue';
|
||||
@HostBinding() id = 'blue';
|
||||
}
|
||||
|
||||
@Component({
|
||||
@ -434,7 +444,7 @@ describe('host bindings', () => {
|
||||
`
|
||||
})
|
||||
class App {
|
||||
@ViewChildren(HostBindingComp) hostBindingComp !: QueryList<HostBindingComp>;
|
||||
@ViewChildren(HostBindingComp) hostBindingComp!: QueryList<HostBindingComp>;
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule({declarations: [App, HostBindingComp]});
|
||||
@ -470,7 +480,7 @@ describe('host bindings', () => {
|
||||
|
||||
@Component({template: '<div someDir hostBindingDir></div>'})
|
||||
class App {
|
||||
@ViewChild(HostBindingDir) hostBindingDir !: HostBindingDir;
|
||||
@ViewChild(HostBindingDir) hostBindingDir!: HostBindingDir;
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule({declarations: [App, SomeDir, HostBindingDir]});
|
||||
@ -480,7 +490,7 @@ describe('host bindings', () => {
|
||||
const hostBindingDiv = fixture.nativeElement.querySelector('div') as HTMLElement;
|
||||
expect(hostBindingDiv.id).toEqual('foo');
|
||||
|
||||
fixture.componentInstance.hostBindingDir !.id = 'bar';
|
||||
fixture.componentInstance.hostBindingDir!.id = 'bar';
|
||||
fixture.detectChanges();
|
||||
expect(hostBindingDiv.id).toEqual('bar');
|
||||
});
|
||||
@ -490,18 +500,23 @@ describe('host bindings', () => {
|
||||
it('should support host bindings that rely on values from init hooks', () => {
|
||||
@Component({template: '', selector: 'init-hook-comp'})
|
||||
class InitHookComp implements OnInit, OnChanges, DoCheck {
|
||||
@Input()
|
||||
inputValue = '';
|
||||
@Input() inputValue = '';
|
||||
|
||||
changesValue = '';
|
||||
initValue = '';
|
||||
checkValue = '';
|
||||
|
||||
ngOnChanges() { this.changesValue = 'changes'; }
|
||||
ngOnChanges() {
|
||||
this.changesValue = 'changes';
|
||||
}
|
||||
|
||||
ngOnInit() { this.initValue = 'init'; }
|
||||
ngOnInit() {
|
||||
this.initValue = 'init';
|
||||
}
|
||||
|
||||
ngDoCheck() { this.checkValue = 'check'; }
|
||||
ngDoCheck() {
|
||||
this.checkValue = 'check';
|
||||
}
|
||||
|
||||
@HostBinding('title')
|
||||
get value() {
|
||||
@ -529,16 +544,14 @@ describe('host bindings', () => {
|
||||
it('should support host bindings with the same name as inputs', () => {
|
||||
@Directive({selector: '[hostBindingDir]'})
|
||||
class HostBindingInputDir {
|
||||
@Input()
|
||||
disabled = false;
|
||||
@Input() disabled = false;
|
||||
|
||||
@HostBinding('disabled')
|
||||
hostDisabled = false;
|
||||
@HostBinding('disabled') hostDisabled = false;
|
||||
}
|
||||
|
||||
@Component({template: '<input hostBindingDir [disabled]="isDisabled">'})
|
||||
class App {
|
||||
@ViewChild(HostBindingInputDir) hostBindingInputDir !: HostBindingInputDir;
|
||||
@ViewChild(HostBindingInputDir) hostBindingInputDir!: HostBindingInputDir;
|
||||
isDisabled = true;
|
||||
}
|
||||
|
||||
@ -611,14 +624,12 @@ describe('host bindings', () => {
|
||||
it('should support component with host bindings and array literals', () => {
|
||||
@Component({selector: 'host-binding-comp', template: ''})
|
||||
class HostBindingComp {
|
||||
@HostBinding()
|
||||
id = 'my-id';
|
||||
@HostBinding() id = 'my-id';
|
||||
}
|
||||
|
||||
@Component({selector: 'name-comp', template: ''})
|
||||
class NameComp {
|
||||
@Input()
|
||||
names !: string[];
|
||||
@Input() names!: string[];
|
||||
}
|
||||
|
||||
@Component({
|
||||
@ -628,7 +639,7 @@ describe('host bindings', () => {
|
||||
`
|
||||
})
|
||||
class App {
|
||||
@ViewChild(NameComp) nameComp !: NameComp;
|
||||
@ViewChild(NameComp) nameComp!: NameComp;
|
||||
name = '';
|
||||
}
|
||||
|
||||
@ -661,8 +672,7 @@ describe('host bindings', () => {
|
||||
it('should support host bindings that contain array literals', () => {
|
||||
@Component({selector: 'name-comp', template: ''})
|
||||
class NameComp {
|
||||
@Input()
|
||||
names !: string[];
|
||||
@Input() names!: string[];
|
||||
}
|
||||
|
||||
@Component({
|
||||
@ -684,8 +694,8 @@ describe('host bindings', () => {
|
||||
`
|
||||
})
|
||||
class App {
|
||||
@ViewChild(HostBindingComp) hostBindingComp !: HostBindingComp;
|
||||
@ViewChild(NameComp) nameComp !: NameComp;
|
||||
@ViewChild(HostBindingComp) hostBindingComp!: HostBindingComp;
|
||||
@ViewChild(NameComp) nameComp!: NameComp;
|
||||
name = '';
|
||||
otherName = '';
|
||||
}
|
||||
@ -703,11 +713,11 @@ describe('host bindings', () => {
|
||||
expect(hostBindingEl.id).toBe('red,blue');
|
||||
expect(hostBindingEl.dir).toBe('ltr');
|
||||
expect(hostBindingEl.title).toBe('my title,other title');
|
||||
expect(nameComp !.names).toEqual(['Frank', 'Nancy', 'Joe']);
|
||||
expect(nameComp!.names).toEqual(['Frank', 'Nancy', 'Joe']);
|
||||
|
||||
const firstArray = nameComp !.names;
|
||||
const firstArray = nameComp!.names;
|
||||
fixture.detectChanges();
|
||||
expect(firstArray).toBe(nameComp !.names);
|
||||
expect(firstArray).toBe(nameComp!.names);
|
||||
|
||||
hostBindingComp.id = 'green';
|
||||
hostBindingComp.dir = 'rtl';
|
||||
@ -729,7 +739,9 @@ describe('host bindings', () => {
|
||||
@Directive({selector: '[hostListenerDir]'})
|
||||
class HostListenerDir {
|
||||
@HostListener('click')
|
||||
onClick() { events.push('click!'); }
|
||||
onClick() {
|
||||
events.push('click!');
|
||||
}
|
||||
}
|
||||
|
||||
@Component({template: '<button hostListenerDir hostDir>Click</button>'})
|
||||
@ -740,7 +752,7 @@ describe('host bindings', () => {
|
||||
const fixture = TestBed.createComponent(App);
|
||||
fixture.detectChanges();
|
||||
|
||||
const button = fixture.nativeElement.querySelector('button') !;
|
||||
const button = fixture.nativeElement.querySelector('button')!;
|
||||
button.click();
|
||||
expect(events).toEqual(['click!']);
|
||||
expect(button.title).toEqual('my title,other title');
|
||||
@ -759,8 +771,8 @@ describe('host bindings', () => {
|
||||
|
||||
@Component({template: '<host-binding-comp hostDir></host-binding-comp>'})
|
||||
class App {
|
||||
@ViewChild(HostBindingComp) hostBindingComp !: HostBindingComp;
|
||||
@ViewChild(HostBindingDir) hostBindingDir !: HostBindingDir;
|
||||
@ViewChild(HostBindingComp) hostBindingComp!: HostBindingComp;
|
||||
@ViewChild(HostBindingDir) hostBindingDir!: HostBindingDir;
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule({declarations: [App, HostBindingComp, HostBindingDir]});
|
||||
@ -798,7 +810,7 @@ describe('host bindings', () => {
|
||||
|
||||
@Component({template: `<host-binding-comp></host-binding-comp>{{ name }}`})
|
||||
class App {
|
||||
@ViewChild(HostBindingComp) hostBindingComp !: HostBindingComp;
|
||||
@ViewChild(HostBindingComp) hostBindingComp!: HostBindingComp;
|
||||
name = '';
|
||||
}
|
||||
|
||||
@ -872,8 +884,8 @@ describe('host bindings', () => {
|
||||
`
|
||||
})
|
||||
class App {
|
||||
@ViewChild(SubDirective) subDir !: SubDirective;
|
||||
@ViewChild(SuperDirective) superDir !: SuperDirective;
|
||||
@ViewChild(SubDirective) subDir!: SubDirective;
|
||||
@ViewChild(SuperDirective) superDir!: SuperDirective;
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule({declarations: [App, SuperDirective, SubDirective]});
|
||||
@ -927,8 +939,7 @@ describe('host bindings', () => {
|
||||
host: {'[id]': 'foos.length'}
|
||||
})
|
||||
class HostBindingWithContentChildren {
|
||||
@ContentChildren('foo')
|
||||
foos !: QueryList<any>;
|
||||
@ContentChildren('foo') foos!: QueryList<any>;
|
||||
}
|
||||
|
||||
@Component({
|
||||
@ -955,7 +966,9 @@ describe('host bindings', () => {
|
||||
class HostBindingWithContentHooks implements AfterContentInit {
|
||||
myValue = 'initial';
|
||||
|
||||
ngAfterContentInit() { this.myValue = 'after-content'; }
|
||||
ngAfterContentInit() {
|
||||
this.myValue = 'after-content';
|
||||
}
|
||||
}
|
||||
|
||||
@Component({template: '<host-binding-comp></host-binding-comp>'})
|
||||
@ -971,7 +984,6 @@ describe('host bindings', () => {
|
||||
});
|
||||
|
||||
describe('styles', () => {
|
||||
|
||||
it('should bind to host styles', () => {
|
||||
@Component(
|
||||
{selector: 'host-binding-to-styles', host: {'[style.width.px]': 'width'}, template: ''})
|
||||
@ -981,7 +993,7 @@ describe('host bindings', () => {
|
||||
|
||||
@Component({template: '<host-binding-to-styles></host-binding-to-styles>'})
|
||||
class App {
|
||||
@ViewChild(HostBindingToStyles) hostBindingDir !: HostBindingToStyles;
|
||||
@ViewChild(HostBindingToStyles) hostBindingDir!: HostBindingToStyles;
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule({declarations: [App, HostBindingToStyles]});
|
||||
@ -1010,7 +1022,7 @@ describe('host bindings', () => {
|
||||
|
||||
@Component({template: '<div hostStyles containerDir></div>'})
|
||||
class App {
|
||||
@ViewChild(HostBindingToStyles) hostBindingDir !: HostBindingToStyles;
|
||||
@ViewChild(HostBindingToStyles) hostBindingDir!: HostBindingToStyles;
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule({declarations: [App, HostBindingToStyles, ContainerDir]});
|
||||
@ -1044,10 +1056,12 @@ describe('host bindings', () => {
|
||||
});
|
||||
|
||||
describe('sanitization', () => {
|
||||
function identity(value: any) { return value; }
|
||||
function verify(
|
||||
tag: string, prop: string, value: any, expectedSanitizedValue: any, bypassFn: Function,
|
||||
isAttribute: boolean = true, throws: boolean = false) {
|
||||
function identity(value: any) {
|
||||
return value;
|
||||
}
|
||||
function verify(tag: string, prop: string, value: any, expectedSanitizedValue: any,
|
||||
bypassFn: Function, isAttribute: boolean = true,
|
||||
throws: boolean = false) {
|
||||
it(`should sanitize <${tag} ${prop}> ${isAttribute ? 'properties' : 'attributes'}`, () => {
|
||||
@Directive({
|
||||
selector: '[unsafeUrlHostBindingDir]',
|
||||
@ -1061,13 +1075,13 @@ describe('host bindings', () => {
|
||||
|
||||
@Component({template: `<${tag} unsafeUrlHostBindingDir></${tag}>`})
|
||||
class App {
|
||||
@ViewChild(UnsafeDir) unsafeDir !: UnsafeDir;
|
||||
@ViewChild(UnsafeDir) unsafeDir!: UnsafeDir;
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule({declarations: [App, UnsafeDir]});
|
||||
const fixture = TestBed.createComponent(App);
|
||||
fixture.detectChanges();
|
||||
const el = fixture.nativeElement.querySelector(tag) !;
|
||||
const el = fixture.nativeElement.querySelector(tag)!;
|
||||
const current = () => isAttribute ? el.getAttribute(prop) : (el as any)[prop];
|
||||
|
||||
fixture.componentInstance.unsafeDir.value = value;
|
||||
@ -1103,5 +1117,4 @@ describe('host bindings', () => {
|
||||
'<img src="unsafe:javascript:alert(3)">', bypassSanitizationTrustHtml,
|
||||
/* isAttribute */ false);
|
||||
});
|
||||
|
||||
});
|
||||
|
@ -8,16 +8,17 @@
|
||||
// Make the `$localize()` global function available to the compiled templates, and the direct calls
|
||||
// below. This would normally be done inside the application `polyfills.ts` file.
|
||||
import '@angular/localize/init';
|
||||
|
||||
import {CommonModule, registerLocaleData} from '@angular/common';
|
||||
import localeRo from '@angular/common/locales/ro';
|
||||
import {Component, ContentChild, ElementRef, ContentChildren, Directive, HostBinding, Input, LOCALE_ID, QueryList, TemplateRef, Type, ViewChild, ViewContainerRef, Pipe, PipeTransform, NO_ERRORS_SCHEMA} from '@angular/core';
|
||||
import {computeMsgId} from '@angular/compiler';
|
||||
import {Component, ContentChild, ContentChildren, Directive, ElementRef, HostBinding, Input, LOCALE_ID, NO_ERRORS_SCHEMA, Pipe, PipeTransform, QueryList, TemplateRef, Type, ViewChild, ViewContainerRef} from '@angular/core';
|
||||
import {setDelayProjection} from '@angular/core/src/render3/instructions/projection';
|
||||
import {TestBed} from '@angular/core/testing';
|
||||
import {loadTranslations, clearTranslations} from '@angular/localize';
|
||||
import {clearTranslations, loadTranslations} from '@angular/localize';
|
||||
import {By} from '@angular/platform-browser';
|
||||
import {expect} from '@angular/platform-browser/testing/src/matchers';
|
||||
import {onlyInIvy} from '@angular/private/testing';
|
||||
import {computeMsgId} from '@angular/compiler';
|
||||
import {BehaviorSubject} from 'rxjs';
|
||||
|
||||
|
||||
@ -72,7 +73,7 @@ onlyInIvy('Ivy i18n logic').describe('runtime i18n', () => {
|
||||
|
||||
it('should support interpolations with custom interpolation config', () => {
|
||||
loadTranslations({[computeMsgId('Hello {$INTERPOLATION}')]: 'Bonjour {$INTERPOLATION}'});
|
||||
const interpolation = ['{%', '%}'] as[string, string];
|
||||
const interpolation = ['{%', '%}'] as [string, string];
|
||||
TestBed.overrideComponent(AppComp, {set: {interpolation}});
|
||||
const fixture = initWithTemplate(AppComp, `<div i18n>Hello {% name %}</div>`);
|
||||
|
||||
@ -277,7 +278,9 @@ onlyInIvy('Ivy i18n logic').describe('runtime i18n', () => {
|
||||
name = `Angular`;
|
||||
clicks = 0;
|
||||
|
||||
onClick() { this.clicks++; }
|
||||
onClick() {
|
||||
this.clicks++;
|
||||
}
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule({declarations: [ListenerComp]});
|
||||
@ -619,7 +622,7 @@ onlyInIvy('Ivy i18n logic').describe('runtime i18n', () => {
|
||||
[computeMsgId('{VAR_SELECT, select, 10 {ten} other {{INTERPOLATION}}}')]:
|
||||
'{VAR_SELECT, select, 10 {dix} other {{INTERPOLATION}}}'
|
||||
});
|
||||
const interpolation = ['{%', '%}'] as[string, string];
|
||||
const interpolation = ['{%', '%}'] as [string, string];
|
||||
TestBed.overrideComponent(AppComp, {set: {interpolation}});
|
||||
const fixture =
|
||||
initWithTemplate(AppComp, `<div i18n>{count, select, 10 {ten} other {{% name %}}}</div>`);
|
||||
@ -704,7 +707,8 @@ onlyInIvy('Ivy i18n logic').describe('runtime i18n', () => {
|
||||
'{VAR_SELECT, select, 10 {dix} 20 {vingt} other {autre}}'
|
||||
});
|
||||
const fixture = initWithTemplate(
|
||||
AppComp, `
|
||||
AppComp,
|
||||
`
|
||||
<ng-template i18n tplRef>` +
|
||||
`{count, select, 10 {ten} 20 {twenty} other {other}}` +
|
||||
`</ng-template>
|
||||
@ -864,7 +868,9 @@ onlyInIvy('Ivy i18n logic').describe('runtime i18n', () => {
|
||||
private readonly viewContainerRef: ViewContainerRef,
|
||||
private readonly templateRef: TemplateRef<any>) {}
|
||||
|
||||
ngOnInit() { this.viewContainerRef.createEmbeddedView(this.templateRef); }
|
||||
ngOnInit() {
|
||||
this.viewContainerRef.createEmbeddedView(this.templateRef);
|
||||
}
|
||||
}
|
||||
|
||||
@Component({
|
||||
@ -941,7 +947,9 @@ onlyInIvy('Ivy i18n logic').describe('runtime i18n', () => {
|
||||
dir = this;
|
||||
}
|
||||
|
||||
attachEmbeddedView() { this.viewContainerRef.createEmbeddedView(this.templateRef); }
|
||||
attachEmbeddedView() {
|
||||
this.viewContainerRef.createEmbeddedView(this.templateRef);
|
||||
}
|
||||
}
|
||||
|
||||
@Component({
|
||||
@ -983,7 +991,7 @@ onlyInIvy('Ivy i18n logic').describe('runtime i18n', () => {
|
||||
expect(fixture.debugElement.nativeElement.innerHTML)
|
||||
.toBe('<my-cmp><!--container--></my-cmp>');
|
||||
|
||||
dir !.attachEmbeddedView();
|
||||
dir!.attachEmbeddedView();
|
||||
fixture.detectChanges();
|
||||
expect(fixture.debugElement.nativeElement.innerHTML)
|
||||
.toBe(
|
||||
@ -1019,7 +1027,9 @@ onlyInIvy('Ivy i18n logic').describe('runtime i18n', () => {
|
||||
class Comp {
|
||||
type = 'A';
|
||||
visible = true;
|
||||
isVisible() { return true; }
|
||||
isVisible() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule({declarations: [Comp]});
|
||||
@ -1042,7 +1052,7 @@ onlyInIvy('Ivy i18n logic').describe('runtime i18n', () => {
|
||||
'{VAR_SELECT, select, A {A - {PH_A}} ' +
|
||||
'B {B - {PH_B}} other {other - {PH_WITH_SPACES}}}')]:
|
||||
'{VAR_SELECT, select, A {A (translated) - {PH_A}} ' +
|
||||
'B {B (translated) - {PH_B}} other {other (translated) - {PH_WITH_SPACES}}}',
|
||||
'B {B (translated) - {PH_B}} other {other (translated) - {PH_WITH_SPACES}}}',
|
||||
});
|
||||
@Component({
|
||||
selector: 'comp',
|
||||
@ -1326,7 +1336,7 @@ onlyInIvy('Ivy i18n logic').describe('runtime i18n', () => {
|
||||
|
||||
it('with custom interpolation config', () => {
|
||||
loadTranslations({[computeMsgId('Hello {$INTERPOLATION}', 'm')]: 'Bonjour {$INTERPOLATION}'});
|
||||
const interpolation = ['{%', '%}'] as[string, string];
|
||||
const interpolation = ['{%', '%}'] as [string, string];
|
||||
TestBed.overrideComponent(AppComp, {set: {interpolation}});
|
||||
const fixture =
|
||||
initWithTemplate(AppComp, `<div i18n-title="m|d" title="Hello {% name %}"></div>`);
|
||||
@ -1367,7 +1377,9 @@ onlyInIvy('Ivy i18n logic').describe('runtime i18n', () => {
|
||||
})
|
||||
class TitleDir {
|
||||
@Input() title = '';
|
||||
constructor() { titleDirInstances.push(this); }
|
||||
constructor() {
|
||||
titleDirInstances.push(this);
|
||||
}
|
||||
}
|
||||
|
||||
@Component({
|
||||
@ -1397,7 +1409,9 @@ onlyInIvy('Ivy i18n logic').describe('runtime i18n', () => {
|
||||
@Directive({selector: '[title]'})
|
||||
class TitleDir {
|
||||
@Input() title: string = '';
|
||||
constructor(public elRef: ElementRef) { titleDirInstances.push(this); }
|
||||
constructor(public elRef: ElementRef) {
|
||||
titleDirInstances.push(this);
|
||||
}
|
||||
}
|
||||
|
||||
@Component({
|
||||
@ -1429,7 +1443,9 @@ onlyInIvy('Ivy i18n logic').describe('runtime i18n', () => {
|
||||
let dirInstance: WithInput;
|
||||
@Directive({selector: '[dir]'})
|
||||
class WithInput {
|
||||
constructor() { dirInstance = this; }
|
||||
constructor() {
|
||||
dirInstance = this;
|
||||
}
|
||||
@Input() dir: string = '';
|
||||
}
|
||||
|
||||
@ -1445,7 +1461,7 @@ onlyInIvy('Ivy i18n logic').describe('runtime i18n', () => {
|
||||
const fixture = TestBed.createComponent(TestComp);
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(dirInstance !.dir).toBe('Bonjour Angular');
|
||||
expect(dirInstance!.dir).toBe('Bonjour Angular');
|
||||
});
|
||||
|
||||
it('should allow directive inputs (as interpolated props)' +
|
||||
@ -1456,7 +1472,9 @@ onlyInIvy('Ivy i18n logic').describe('runtime i18n', () => {
|
||||
let dirInstance: WithInput;
|
||||
@Directive({selector: '[dir]'})
|
||||
class WithInput {
|
||||
constructor() { dirInstance = this; }
|
||||
constructor() {
|
||||
dirInstance = this;
|
||||
}
|
||||
@Input() dir: string = '';
|
||||
}
|
||||
|
||||
@ -1472,7 +1490,7 @@ onlyInIvy('Ivy i18n logic').describe('runtime i18n', () => {
|
||||
const fixture = TestBed.createComponent(TestComp);
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(dirInstance !.dir).toBe('Bonjour Angular');
|
||||
expect(dirInstance!.dir).toBe('Bonjour Angular');
|
||||
});
|
||||
|
||||
it('should apply i18n attributes during second template pass', () => {
|
||||
@ -1537,7 +1555,7 @@ onlyInIvy('Ivy i18n logic').describe('runtime i18n', () => {
|
||||
template: '{{ messageText }}',
|
||||
})
|
||||
class WelcomeComp {
|
||||
@Input() messageText !: string;
|
||||
@Input() messageText!: string;
|
||||
}
|
||||
|
||||
@Component({
|
||||
@ -1598,10 +1616,11 @@ onlyInIvy('Ivy i18n logic').describe('runtime i18n', () => {
|
||||
|
||||
@Directive({selector: '[test]'})
|
||||
class ClsDir {
|
||||
@HostBinding('className')
|
||||
klass = 'foo';
|
||||
@HostBinding('className') klass = 'foo';
|
||||
|
||||
constructor() { directiveInstances.push(this); }
|
||||
constructor() {
|
||||
directiveInstances.push(this);
|
||||
}
|
||||
}
|
||||
|
||||
@Component({
|
||||
@ -1641,7 +1660,7 @@ onlyInIvy('Ivy i18n logic').describe('runtime i18n', () => {
|
||||
// but IE does not.
|
||||
expect(outerDiv.getAttribute('title')).toBe('début 2 milieu 1 fin');
|
||||
expect(outerDiv.getAttribute('class')).toBe('foo');
|
||||
expect(outerDiv.textContent !.trim()).toBe('traduction: un email');
|
||||
expect(outerDiv.textContent!.trim()).toBe('traduction: un email');
|
||||
expect(innerDiv.getAttribute('class')).toBe('foo');
|
||||
|
||||
directiveInstances.forEach(instance => instance.klass = 'bar');
|
||||
@ -1651,7 +1670,7 @@ onlyInIvy('Ivy i18n logic').describe('runtime i18n', () => {
|
||||
|
||||
expect(outerDiv.getAttribute('title')).toBe('début 3 milieu 2 fin');
|
||||
expect(outerDiv.getAttribute('class')).toBe('bar');
|
||||
expect(outerDiv.textContent !.trim()).toBe('traduction: 2 emails');
|
||||
expect(outerDiv.textContent!.trim()).toBe('traduction: 2 emails');
|
||||
expect(innerDiv.getAttribute('class')).toBe('bar');
|
||||
});
|
||||
|
||||
@ -1660,21 +1679,25 @@ onlyInIvy('Ivy i18n logic').describe('runtime i18n', () => {
|
||||
let calledValue = false;
|
||||
@Component({selector: 'my-comp', template: ''})
|
||||
class MyComp {
|
||||
t !: string;
|
||||
t!: string;
|
||||
@Input()
|
||||
get title() { return this.t; }
|
||||
get title() {
|
||||
return this.t;
|
||||
}
|
||||
set title(title) {
|
||||
calledTitle = true;
|
||||
this.t = title;
|
||||
}
|
||||
|
||||
@Input()
|
||||
get value() { return this.val; }
|
||||
get value() {
|
||||
return this.val;
|
||||
}
|
||||
set value(value: string) {
|
||||
calledValue = true;
|
||||
this.val = value;
|
||||
}
|
||||
val !: string;
|
||||
val!: string;
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule({declarations: [AppComp, MyComp]});
|
||||
@ -2050,7 +2073,6 @@ onlyInIvy('Ivy i18n logic').describe('runtime i18n', () => {
|
||||
});
|
||||
|
||||
it('should display/destroy projected i18n content', () => {
|
||||
|
||||
loadTranslations({
|
||||
[computeMsgId('{VAR_SELECT, select, A {A} B {B} other {other}}')]:
|
||||
'{VAR_SELECT, select, A {A} B {B} other {other}}'
|
||||
@ -2109,7 +2131,7 @@ onlyInIvy('Ivy i18n logic').describe('runtime i18n', () => {
|
||||
@Directive({selector: '[text]', inputs: ['text'], exportAs: 'textDir'})
|
||||
class TextDirective {
|
||||
// TODO(issue/24571): remove '!'.
|
||||
text !: string;
|
||||
text!: string;
|
||||
constructor() {}
|
||||
}
|
||||
|
||||
@ -2119,16 +2141,18 @@ onlyInIvy('Ivy i18n logic').describe('runtime i18n', () => {
|
||||
@ContentChild(TemplateRef, {static: true}) template !: TemplateRef<any>;
|
||||
|
||||
// TODO(issue/24571): remove '!'.
|
||||
@ViewChild('vc', {read: ViewContainerRef, static: true})
|
||||
vc !: ViewContainerRef;
|
||||
@ViewChild('vc', {read: ViewContainerRef, static: true}) vc!: ViewContainerRef;
|
||||
|
||||
// TODO(issue/24571): remove '!'.
|
||||
@ContentChildren(TextDirective, {descendants: true})
|
||||
query !: QueryList<TextDirective>;
|
||||
@ContentChildren(TextDirective, {descendants: true}) query!: QueryList<TextDirective>;
|
||||
|
||||
create() { this.vc.createEmbeddedView(this.template); }
|
||||
create() {
|
||||
this.vc.createEmbeddedView(this.template);
|
||||
}
|
||||
|
||||
destroy() { this.vc.clear(); }
|
||||
destroy() {
|
||||
this.vc.clear();
|
||||
}
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule({declarations: [TextDirective, DivQuery]});
|
||||
@ -2224,7 +2248,9 @@ onlyInIvy('Ivy i18n logic').describe('runtime i18n', () => {
|
||||
@Directive({selector: 'input'})
|
||||
class InputsDir {
|
||||
constructor(private elementRef: ElementRef) {}
|
||||
ngOnInit() { this.elementRef.nativeElement.value = 'value set in Directive.ngOnInit'; }
|
||||
ngOnInit() {
|
||||
this.elementRef.nativeElement.value = 'value set in Directive.ngOnInit';
|
||||
}
|
||||
}
|
||||
|
||||
@Component({
|
||||
@ -2248,7 +2274,9 @@ onlyInIvy('Ivy i18n logic').describe('runtime i18n', () => {
|
||||
@Directive({selector: 'input'})
|
||||
class InputsDir {
|
||||
constructor(private elementRef: ElementRef) {}
|
||||
ngOnInit() { this.elementRef.nativeElement.value = 'value set in Directive.ngOnInit'; }
|
||||
ngOnInit() {
|
||||
this.elementRef.nativeElement.value = 'value set in Directive.ngOnInit';
|
||||
}
|
||||
}
|
||||
|
||||
@Component({
|
||||
@ -2401,12 +2429,16 @@ class AppCompWithWhitespaces {
|
||||
})
|
||||
class DirectiveWithTplRef {
|
||||
constructor(public vcRef: ViewContainerRef, public tplRef: TemplateRef<{}>) {}
|
||||
ngOnInit() { this.vcRef.createEmbeddedView(this.tplRef, {}); }
|
||||
ngOnInit() {
|
||||
this.vcRef.createEmbeddedView(this.tplRef, {});
|
||||
}
|
||||
}
|
||||
|
||||
@Pipe({name: 'uppercase'})
|
||||
class UppercasePipe implements PipeTransform {
|
||||
transform(value: string) { return value.toUpperCase(); }
|
||||
transform(value: string) {
|
||||
return value.toUpperCase();
|
||||
}
|
||||
}
|
||||
|
||||
@Directive({selector: `[dialog]`})
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -16,10 +16,11 @@ import {expect} from '@angular/platform-browser/testing/src/matchers';
|
||||
import {ivyEnabled, onlyInIvy} from '@angular/private/testing';
|
||||
|
||||
describe('acceptance integration tests', () => {
|
||||
function stripHtmlComments(str: string) { return str.replace(/<!--[\s\S]*?-->/g, ''); }
|
||||
function stripHtmlComments(str: string) {
|
||||
return str.replace(/<!--[\s\S]*?-->/g, '');
|
||||
}
|
||||
|
||||
describe('render', () => {
|
||||
|
||||
it('should render basic template', () => {
|
||||
@Component({template: '<span title="Hello">Greetings</span>'})
|
||||
class App {
|
||||
@ -64,12 +65,10 @@ describe('acceptance integration tests', () => {
|
||||
tView: 2,
|
||||
tNode: 4,
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
describe('ng-container', () => {
|
||||
|
||||
it('should insert as a child of a regular element', () => {
|
||||
@Component(
|
||||
{template: '<div>before|<ng-container>Greetings<span></span></ng-container>|after</div>'})
|
||||
@ -108,7 +107,6 @@ describe('acceptance integration tests', () => {
|
||||
});
|
||||
|
||||
it('should add and remove DOM nodes when ng-container is a child of an embedded view', () => {
|
||||
|
||||
@Component({template: '<ng-container *ngIf="render">content</ng-container>'})
|
||||
class App {
|
||||
render = false;
|
||||
@ -131,21 +129,24 @@ describe('acceptance integration tests', () => {
|
||||
// https://stackblitz.com/edit/angular-tfhcz1?file=src%2Fapp%2Fapp.component.ts
|
||||
it('should add and remove DOM nodes when ng-container is a child of a delayed embedded view',
|
||||
() => {
|
||||
|
||||
@Directive({selector: '[testDirective]'})
|
||||
class TestDirective {
|
||||
constructor(private _tplRef: TemplateRef<any>, private _vcRef: ViewContainerRef) {}
|
||||
|
||||
createAndInsert() { this._vcRef.insert(this._tplRef.createEmbeddedView({})); }
|
||||
createAndInsert() {
|
||||
this._vcRef.insert(this._tplRef.createEmbeddedView({}));
|
||||
}
|
||||
|
||||
clear() { this._vcRef.clear(); }
|
||||
clear() {
|
||||
this._vcRef.clear();
|
||||
}
|
||||
}
|
||||
|
||||
@Component({
|
||||
template: '<ng-template testDirective><ng-container>content</ng-container></ng-template>'
|
||||
})
|
||||
class App {
|
||||
@ViewChild(TestDirective, {static: true}) testDirective !: TestDirective;
|
||||
@ViewChild(TestDirective, {static: true}) testDirective!: TestDirective;
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule({declarations: [App, TestDirective]});
|
||||
@ -205,9 +206,13 @@ describe('acceptance integration tests', () => {
|
||||
class TestDirective {
|
||||
constructor(private _tplRef: TemplateRef<any>, private _vcRef: ViewContainerRef) {}
|
||||
|
||||
createAndInsert() { this._vcRef.insert(this._tplRef.createEmbeddedView({})); }
|
||||
createAndInsert() {
|
||||
this._vcRef.insert(this._tplRef.createEmbeddedView({}));
|
||||
}
|
||||
|
||||
clear() { this._vcRef.clear(); }
|
||||
clear() {
|
||||
this._vcRef.clear();
|
||||
}
|
||||
}
|
||||
|
||||
@Component({
|
||||
@ -215,7 +220,7 @@ describe('acceptance integration tests', () => {
|
||||
'<ng-template testDirective><ng-container><ng-container><ng-container>content</ng-container></ng-container></ng-container></ng-template>'
|
||||
})
|
||||
class App {
|
||||
@ViewChild(TestDirective, {static: true}) testDirective !: TestDirective;
|
||||
@ViewChild(TestDirective, {static: true}) testDirective!: TestDirective;
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule({declarations: [App, TestDirective]});
|
||||
@ -245,7 +250,7 @@ describe('acceptance integration tests', () => {
|
||||
|
||||
@Component({template: '<div><ng-container dir></ng-container></div>'})
|
||||
class App {
|
||||
@ViewChild(TestDirective) testDirective !: TestDirective;
|
||||
@ViewChild(TestDirective) testDirective!: TestDirective;
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule({declarations: [App, TestDirective]});
|
||||
@ -260,14 +265,17 @@ describe('acceptance integration tests', () => {
|
||||
it('should support ViewContainerRef when ng-container is at the root of a view', () => {
|
||||
@Directive({selector: '[dir]'})
|
||||
class TestDirective {
|
||||
@Input()
|
||||
contentTpl: TemplateRef<{}>|null = null;
|
||||
@Input() contentTpl: TemplateRef<{}>|null = null;
|
||||
|
||||
constructor(private _vcRef: ViewContainerRef) {}
|
||||
|
||||
insertView() { this._vcRef.createEmbeddedView(this.contentTpl as TemplateRef<{}>); }
|
||||
insertView() {
|
||||
this._vcRef.createEmbeddedView(this.contentTpl as TemplateRef<{}>);
|
||||
}
|
||||
|
||||
clear() { this._vcRef.clear(); }
|
||||
clear() {
|
||||
this._vcRef.clear();
|
||||
}
|
||||
}
|
||||
|
||||
@Component({
|
||||
@ -275,7 +283,7 @@ describe('acceptance integration tests', () => {
|
||||
'<ng-container dir [contentTpl]="content"><ng-template #content>Content</ng-template></ng-container>'
|
||||
})
|
||||
class App {
|
||||
@ViewChild(TestDirective) testDirective !: TestDirective;
|
||||
@ViewChild(TestDirective) testDirective!: TestDirective;
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule({declarations: [App, TestDirective]});
|
||||
@ -298,14 +306,18 @@ describe('acceptance integration tests', () => {
|
||||
class TestDirective {
|
||||
constructor(private _tplRef: TemplateRef<{}>, private _vcRef: ViewContainerRef) {}
|
||||
|
||||
insertView() { this._vcRef.createEmbeddedView(this._tplRef); }
|
||||
insertView() {
|
||||
this._vcRef.createEmbeddedView(this._tplRef);
|
||||
}
|
||||
|
||||
clear() { this._vcRef.clear(); }
|
||||
clear() {
|
||||
this._vcRef.clear();
|
||||
}
|
||||
}
|
||||
|
||||
@Component({template: '<ng-container><ng-template dir>Content</ng-template></ng-container>'})
|
||||
class App {
|
||||
@ViewChild(TestDirective) testDirective !: TestDirective;
|
||||
@ViewChild(TestDirective) testDirective!: TestDirective;
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule({declarations: [App, TestDirective]});
|
||||
@ -334,7 +346,6 @@ describe('acceptance integration tests', () => {
|
||||
|
||||
expect(stripHtmlComments(fixture.nativeElement.innerHTML)).toEqual('<div></div>');
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('text bindings', () => {
|
||||
@ -373,11 +384,12 @@ describe('acceptance integration tests', () => {
|
||||
|
||||
expect(fixture.nativeElement.innerHTML).toEqual('');
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('ngNonBindable handling', () => {
|
||||
function stripNgNonBindable(str: string) { return str.replace(/ ngnonbindable=""/i, ''); }
|
||||
function stripNgNonBindable(str: string) {
|
||||
return str.replace(/ ngnonbindable=""/i, '');
|
||||
}
|
||||
|
||||
it('should keep local ref for host element', () => {
|
||||
@Component({
|
||||
@ -404,7 +416,9 @@ describe('acceptance integration tests', () => {
|
||||
|
||||
@Directive({selector: '[directive]'})
|
||||
class TestDirective implements OnInit {
|
||||
ngOnInit() { directiveInvoked = true; }
|
||||
ngOnInit() {
|
||||
directiveInvoked = true;
|
||||
}
|
||||
}
|
||||
|
||||
@Component({
|
||||
@ -432,7 +446,9 @@ describe('acceptance integration tests', () => {
|
||||
|
||||
@Directive({selector: '[directive]'})
|
||||
class TestDirective implements OnInit {
|
||||
ngOnInit() { directiveInvoked = true; }
|
||||
ngOnInit() {
|
||||
directiveInvoked = true;
|
||||
}
|
||||
}
|
||||
|
||||
@Component({
|
||||
@ -604,14 +620,12 @@ describe('acceptance integration tests', () => {
|
||||
it('should support a component with binding on host element', () => {
|
||||
@Component({selector: 'todo', template: '{{title}}'})
|
||||
class TodoComponentHostBinding {
|
||||
@HostBinding()
|
||||
title = 'one';
|
||||
@HostBinding() title = 'one';
|
||||
}
|
||||
|
||||
@Component({template: '<todo></todo>'})
|
||||
class App {
|
||||
@ViewChild(TodoComponentHostBinding)
|
||||
todoComponentHostBinding !: TodoComponentHostBinding;
|
||||
@ViewChild(TodoComponentHostBinding) todoComponentHostBinding!: TodoComponentHostBinding;
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule({declarations: [App, TodoComponentHostBinding]});
|
||||
@ -658,8 +672,7 @@ describe('acceptance integration tests', () => {
|
||||
it('should support a component with sub-views', () => {
|
||||
@Component({selector: 'comp', template: '<div *ngIf="condition">text</div>'})
|
||||
class MyComp {
|
||||
@Input()
|
||||
condition !: boolean;
|
||||
@Input() condition!: boolean;
|
||||
}
|
||||
|
||||
@Component({template: '<comp [condition]="condition"></comp>'})
|
||||
@ -680,7 +693,6 @@ describe('acceptance integration tests', () => {
|
||||
fixture.detectChanges();
|
||||
expect(stripHtmlComments(compElement.innerHTML)).toEqual('');
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('element bindings', () => {
|
||||
@ -814,7 +826,7 @@ describe('acceptance integration tests', () => {
|
||||
fixture.detectChanges();
|
||||
|
||||
const span: HTMLSpanElement = fixture.nativeElement.querySelector('span');
|
||||
const bold: HTMLElement = span.querySelector('b') !;
|
||||
const bold: HTMLElement = span.querySelector('b')!;
|
||||
|
||||
fixture.componentInstance.title = 'Hello';
|
||||
fixture.detectChanges();
|
||||
@ -840,13 +852,12 @@ describe('acceptance integration tests', () => {
|
||||
it('should support host attribute bindings', () => {
|
||||
@Directive({selector: '[hostBindingDir]'})
|
||||
class HostBindingDir {
|
||||
@HostBinding('attr.aria-label')
|
||||
label = 'some label';
|
||||
@HostBinding('attr.aria-label') label = 'some label';
|
||||
}
|
||||
|
||||
@Component({template: '<div hostBindingDir></div>'})
|
||||
class App {
|
||||
@ViewChild(HostBindingDir) hostBindingDir !: HostBindingDir;
|
||||
@ViewChild(HostBindingDir) hostBindingDir!: HostBindingDir;
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule({declarations: [App, HostBindingDir]});
|
||||
@ -999,12 +1010,13 @@ describe('acceptance integration tests', () => {
|
||||
it('should apply classes properly when nodes have containers', () => {
|
||||
@Component({selector: 'structural-comp', template: 'Comp Content'})
|
||||
class StructuralComp {
|
||||
@Input()
|
||||
tmp !: TemplateRef<any>;
|
||||
@Input() tmp!: TemplateRef<any>;
|
||||
|
||||
constructor(public vcr: ViewContainerRef) {}
|
||||
|
||||
create() { this.vcr.createEmbeddedView(this.tmp); }
|
||||
create() {
|
||||
this.vcr.createEmbeddedView(this.tmp);
|
||||
}
|
||||
}
|
||||
|
||||
@Component({
|
||||
@ -1014,7 +1026,7 @@ describe('acceptance integration tests', () => {
|
||||
`
|
||||
})
|
||||
class App {
|
||||
@ViewChild(StructuralComp) structuralComp !: StructuralComp;
|
||||
@ViewChild(StructuralComp) structuralComp!: StructuralComp;
|
||||
value: any;
|
||||
}
|
||||
|
||||
@ -1040,7 +1052,9 @@ describe('acceptance integration tests', () => {
|
||||
public classesVal: string = '';
|
||||
|
||||
@Input('class')
|
||||
set klass(value: string) { this.classesVal = value; }
|
||||
set klass(value: string) {
|
||||
this.classesVal = value;
|
||||
}
|
||||
}
|
||||
|
||||
@Directive({selector: '[DirWithStyle]'})
|
||||
@ -1048,15 +1062,16 @@ describe('acceptance integration tests', () => {
|
||||
public stylesVal: any = '';
|
||||
|
||||
@Input()
|
||||
set style(value: any) { this.stylesVal = value; }
|
||||
set style(value: any) {
|
||||
this.stylesVal = value;
|
||||
}
|
||||
}
|
||||
|
||||
it('should delegate initial classes to a [class] input binding if present on a directive on the same element',
|
||||
() => {
|
||||
@Component({template: '<div class="apple orange banana" DirWithClass></div>'})
|
||||
class App {
|
||||
@ViewChild(DirWithClassDirective)
|
||||
mockClassDirective !: DirWithClassDirective;
|
||||
@ViewChild(DirWithClassDirective) mockClassDirective!: DirWithClassDirective;
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule({declarations: [App, DirWithClassDirective]});
|
||||
@ -1073,8 +1088,7 @@ describe('acceptance integration tests', () => {
|
||||
() => {
|
||||
@Component({template: '<div style="width: 100px; height: 200px" DirWithStyle></div>'})
|
||||
class App {
|
||||
@ViewChild(DirWithStyleDirective)
|
||||
mockStyleDirective !: DirWithStyleDirective;
|
||||
@ViewChild(DirWithStyleDirective) mockStyleDirective!: DirWithStyleDirective;
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule({declarations: [App, DirWithStyleDirective]});
|
||||
@ -1092,8 +1106,7 @@ describe('acceptance integration tests', () => {
|
||||
() => {
|
||||
@Component({template: '<div DirWithClass [class]="value"></div>'})
|
||||
class App {
|
||||
@ViewChild(DirWithClassDirective)
|
||||
mockClassDirective !: DirWithClassDirective;
|
||||
@ViewChild(DirWithClassDirective) mockClassDirective!: DirWithClassDirective;
|
||||
value = '';
|
||||
}
|
||||
|
||||
@ -1111,9 +1124,8 @@ describe('acceptance integration tests', () => {
|
||||
() => {
|
||||
@Component({template: '<div DirWithStyle [style]="value"></div>'})
|
||||
class App {
|
||||
@ViewChild(DirWithStyleDirective)
|
||||
mockStyleDirective !: DirWithStyleDirective;
|
||||
value !: {[key: string]: string};
|
||||
@ViewChild(DirWithStyleDirective) mockStyleDirective!: DirWithStyleDirective;
|
||||
value!: {[key: string]: string};
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule({declarations: [App, DirWithStyleDirective]});
|
||||
@ -1155,7 +1167,7 @@ describe('acceptance integration tests', () => {
|
||||
fixture.detectChanges();
|
||||
|
||||
const target: HTMLDivElement = fixture.nativeElement.querySelector('div');
|
||||
const classes = target.getAttribute('class') !.split(/\s+/).sort();
|
||||
const classes = target.getAttribute('class')!.split(/\s+/).sort();
|
||||
expect(classes).toEqual(['big', 'golden', 'heavy']);
|
||||
|
||||
expect(target.getAttribute('title')).toEqual('foo');
|
||||
@ -1189,7 +1201,7 @@ describe('acceptance integration tests', () => {
|
||||
})
|
||||
class App {
|
||||
@ViewChild(DirWithSingleStylingBindings)
|
||||
dirInstance !: DirWithSingleStylingBindings;
|
||||
dirInstance!: DirWithSingleStylingBindings;
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule({declarations: [App, DirWithSingleStylingBindings]});
|
||||
@ -1244,8 +1256,8 @@ describe('acceptance integration tests', () => {
|
||||
@Component(
|
||||
{template: '<div Dir1WithStyle Dir2WithStyle [style.width]="width"></div>'})
|
||||
class App {
|
||||
@ViewChild(Dir1WithStyle) dir1Instance !: Dir1WithStyle;
|
||||
@ViewChild(Dir2WithStyle) dir2Instance !: Dir2WithStyle;
|
||||
@ViewChild(Dir1WithStyle) dir1Instance!: Dir1WithStyle;
|
||||
@ViewChild(Dir2WithStyle) dir2Instance!: Dir2WithStyle;
|
||||
width: string|null|undefined = undefined;
|
||||
}
|
||||
|
||||
@ -1309,8 +1321,8 @@ describe('acceptance integration tests', () => {
|
||||
'<div Dir1WithStyling Dir2WithStyling [style]="stylesExp" [class]="classesExp"></div>'
|
||||
})
|
||||
class App {
|
||||
@ViewChild(Dir1WithStyling) dir1Instance !: Dir1WithStyling;
|
||||
@ViewChild(Dir2WithStyling) dir2Instance !: Dir2WithStyling;
|
||||
@ViewChild(Dir1WithStyling) dir1Instance!: Dir1WithStyling;
|
||||
@ViewChild(Dir2WithStyling) dir2Instance!: Dir2WithStyling;
|
||||
stylesExp: any = {};
|
||||
classesExp: any = {};
|
||||
}
|
||||
@ -1321,7 +1333,7 @@ describe('acceptance integration tests', () => {
|
||||
fixture.detectChanges();
|
||||
const {dir1Instance, dir2Instance} = fixture.componentInstance;
|
||||
|
||||
const target = fixture.nativeElement.querySelector('div') !;
|
||||
const target = fixture.nativeElement.querySelector('div')!;
|
||||
expect(target.style.getPropertyValue('width')).toEqual('111px');
|
||||
|
||||
const compInstance = fixture.componentInstance;
|
||||
@ -1393,7 +1405,7 @@ describe('acceptance integration tests', () => {
|
||||
|
||||
TestBed.configureTestingModule({declarations: [App]});
|
||||
const fixture = TestBed.createComponent(App);
|
||||
const target = fixture.nativeElement.querySelector('div') !;
|
||||
const target = fixture.nativeElement.querySelector('div')!;
|
||||
|
||||
expect(target.classList.contains('-fred-36-')).toBeFalsy();
|
||||
|
||||
@ -1510,7 +1522,6 @@ describe('acceptance integration tests', () => {
|
||||
// The ViewEngine error has a typo, whereas the Ivy one fixes it.
|
||||
/^Unexpected value 'SomeModule' imported by the module 'ModuleWithImportedModule'\. Please add (a|an) @NgModule annotation\.$/);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
it('should only call inherited host listeners once', () => {
|
||||
@ -1519,7 +1530,9 @@ describe('acceptance integration tests', () => {
|
||||
@Component({template: ''})
|
||||
class ButtonSuperClass {
|
||||
@HostListener('click')
|
||||
clicked() { clicks++; }
|
||||
clicked() {
|
||||
clicks++;
|
||||
}
|
||||
}
|
||||
|
||||
@Component({selector: 'button[custom-button]', template: ''})
|
||||
@ -1548,7 +1561,7 @@ describe('acceptance integration tests', () => {
|
||||
|
||||
@Component({template: '<div someDir></div>'})
|
||||
class SuperComp {
|
||||
@ViewChildren(SomeDir) dirs !: QueryList<SomeDir>;
|
||||
@ViewChildren(SomeDir) dirs!: QueryList<SomeDir>;
|
||||
}
|
||||
|
||||
@Component({selector: 'button[custom-button]', template: '<div someDir></div>'})
|
||||
@ -1576,7 +1589,9 @@ describe('acceptance integration tests', () => {
|
||||
private _isDestroyed = false;
|
||||
|
||||
@Input()
|
||||
get value() { return this._value; }
|
||||
get value() {
|
||||
return this._value;
|
||||
}
|
||||
set value(newValue: any) {
|
||||
if (this._isDestroyed) {
|
||||
throw Error('Cannot assign to value after destroy.');
|
||||
@ -1586,7 +1601,9 @@ describe('acceptance integration tests', () => {
|
||||
}
|
||||
private _value: any;
|
||||
|
||||
ngOnDestroy() { this._isDestroyed = true; }
|
||||
ngOnDestroy() {
|
||||
this._isDestroyed = true;
|
||||
}
|
||||
}
|
||||
|
||||
@Component({template: '<div no-assign-after-destroy [value]="directiveValue"></div>'})
|
||||
@ -1608,7 +1625,7 @@ describe('acceptance integration tests', () => {
|
||||
@Component(
|
||||
{selector: 'test-component', template: `foo`, host: {'[attr.aria-disabled]': 'true'}})
|
||||
class TestComponent {
|
||||
@ContentChild(TemplateRef, {static: true}) tpl !: TemplateRef<any>;
|
||||
@ContentChild(TemplateRef, {static: true}) tpl!: TemplateRef<any>;
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule({declarations: [TestComponent]});
|
||||
@ -1621,7 +1638,7 @@ describe('acceptance integration tests', () => {
|
||||
|
||||
it('should inherit inputs from undecorated superclasses', () => {
|
||||
class ButtonSuperClass {
|
||||
@Input() isDisabled !: boolean;
|
||||
@Input() isDisabled!: boolean;
|
||||
}
|
||||
|
||||
@Component({selector: 'button[custom-button]', template: ''})
|
||||
@ -1651,7 +1668,9 @@ describe('acceptance integration tests', () => {
|
||||
|
||||
class ButtonSuperClass {
|
||||
@Output() clicked = new EventEmitter<void>();
|
||||
emitClick() { this.clicked.emit(); }
|
||||
emitClick() {
|
||||
this.clicked.emit();
|
||||
}
|
||||
}
|
||||
|
||||
@Component({selector: 'button[custom-button]', template: ''})
|
||||
@ -1660,7 +1679,9 @@ describe('acceptance integration tests', () => {
|
||||
|
||||
@Component({template: '<button custom-button (clicked)="handleClick()"></button>'})
|
||||
class MyApp {
|
||||
handleClick() { clicks++; }
|
||||
handleClick() {
|
||||
clicks++;
|
||||
}
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule({declarations: [MyApp, ButtonSubClass]});
|
||||
@ -1675,8 +1696,7 @@ describe('acceptance integration tests', () => {
|
||||
|
||||
it('should inherit host bindings from undecorated superclasses', () => {
|
||||
class BaseButton {
|
||||
@HostBinding('attr.tabindex')
|
||||
tabindex = -1;
|
||||
@HostBinding('attr.tabindex') tabindex = -1;
|
||||
}
|
||||
|
||||
@Component({selector: '[sub-button]', template: '<ng-content></ng-content>'})
|
||||
@ -1702,8 +1722,7 @@ describe('acceptance integration tests', () => {
|
||||
|
||||
it('should inherit host bindings from undecorated grand superclasses', () => {
|
||||
class SuperBaseButton {
|
||||
@HostBinding('attr.tabindex')
|
||||
tabindex = -1;
|
||||
@HostBinding('attr.tabindex') tabindex = -1;
|
||||
}
|
||||
|
||||
class BaseButton extends SuperBaseButton {}
|
||||
@ -1734,7 +1753,9 @@ describe('acceptance integration tests', () => {
|
||||
|
||||
class BaseButton {
|
||||
@HostListener('click')
|
||||
handleClick() { clicks++; }
|
||||
handleClick() {
|
||||
clicks++;
|
||||
}
|
||||
}
|
||||
|
||||
@Component({selector: '[sub-button]', template: '<ng-content></ng-content>'})
|
||||
@ -1761,7 +1782,9 @@ describe('acceptance integration tests', () => {
|
||||
@Directive({selector: '[baseButton]'})
|
||||
class BaseButton {
|
||||
@HostListener('click')
|
||||
handleClick() { clicks++; }
|
||||
handleClick() {
|
||||
clicks++;
|
||||
}
|
||||
}
|
||||
|
||||
@Component({selector: '[subButton]', template: '<ng-content></ng-content>'})
|
||||
@ -1788,7 +1811,9 @@ describe('acceptance integration tests', () => {
|
||||
@Directive({selector: '[superBaseButton]'})
|
||||
class SuperBaseButton {
|
||||
@HostListener('click')
|
||||
handleClick() { clicks++; }
|
||||
handleClick() {
|
||||
clicks++;
|
||||
}
|
||||
}
|
||||
|
||||
@Directive({selector: '[baseButton]'})
|
||||
@ -1819,7 +1844,9 @@ describe('acceptance integration tests', () => {
|
||||
@Directive({selector: '[superSuperBaseButton]'})
|
||||
class SuperSuperBaseButton {
|
||||
@HostListener('click')
|
||||
handleClick() { clicks++; }
|
||||
handleClick() {
|
||||
clicks++;
|
||||
}
|
||||
}
|
||||
|
||||
@Directive({selector: '[superBaseButton]'})
|
||||
@ -1855,9 +1882,13 @@ describe('acceptance integration tests', () => {
|
||||
inputs: ['dir'],
|
||||
})
|
||||
class Dir {
|
||||
get dir(): any { return null; }
|
||||
get dir(): any {
|
||||
return null;
|
||||
}
|
||||
|
||||
set dir(value: any) { throw new Error('this error is expected'); }
|
||||
set dir(value: any) {
|
||||
throw new Error('this error is expected');
|
||||
}
|
||||
}
|
||||
|
||||
@Component({
|
||||
@ -1919,7 +1950,7 @@ describe('acceptance integration tests', () => {
|
||||
});
|
||||
const fixture = TestBed.createComponent(Cmp);
|
||||
fixture.detectChanges(false);
|
||||
const element = fixture.nativeElement.querySelector('div') !;
|
||||
const element = fixture.nativeElement.querySelector('div')!;
|
||||
|
||||
assertAttrValues(element, 'first-update-pass');
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -16,36 +16,40 @@ function getNoOfNativeListeners(): number {
|
||||
}
|
||||
|
||||
describe('event listeners', () => {
|
||||
|
||||
describe('coalescing', () => {
|
||||
|
||||
@Component({
|
||||
selector: 'with-clicks-cmpt',
|
||||
template: `<button likes-clicks (click)="count()" md-button>Click me!</button>`
|
||||
})
|
||||
class WithClicksCmpt {
|
||||
counter = 0;
|
||||
count() { this.counter++; }
|
||||
count() {
|
||||
this.counter++;
|
||||
}
|
||||
}
|
||||
|
||||
@Directive({selector: '[md-button]'})
|
||||
class MdButton {
|
||||
counter = 0;
|
||||
@HostListener('click')
|
||||
count() { this.counter++; }
|
||||
count() {
|
||||
this.counter++;
|
||||
}
|
||||
}
|
||||
|
||||
@Directive({selector: '[likes-clicks]'})
|
||||
class LikesClicks {
|
||||
counter = 0;
|
||||
@HostListener('click')
|
||||
count() { this.counter++; }
|
||||
count() {
|
||||
this.counter++;
|
||||
}
|
||||
}
|
||||
|
||||
@Directive({selector: '[returns-false]'})
|
||||
class ReturnsFalse {
|
||||
counter = 0;
|
||||
event !: Event;
|
||||
event!: Event;
|
||||
handlerShouldReturn: boolean|undefined = undefined;
|
||||
|
||||
@HostListener('click', ['$event'])
|
||||
@ -65,7 +69,6 @@ describe('event listeners', () => {
|
||||
onlyInIvy('ngDevMode.rendererAddEventListener counters are only available in ivy')
|
||||
.it('should coalesce multiple event listeners for the same event on the same element',
|
||||
() => {
|
||||
|
||||
@Component({
|
||||
selector: 'test-cmpt',
|
||||
template:
|
||||
@ -107,7 +110,6 @@ describe('event listeners', () => {
|
||||
|
||||
onlyInIvy('ngDevMode.rendererAddEventListener counters are only available in ivy')
|
||||
.it('should coalesce multiple event listeners in presence of queries', () => {
|
||||
|
||||
@Component({
|
||||
selector: 'test-cmpt',
|
||||
template: `<button likes-clicks (click)="counter = counter+1">Click me!</button>`
|
||||
@ -115,7 +117,7 @@ describe('event listeners', () => {
|
||||
class TestCmpt {
|
||||
counter = 0;
|
||||
|
||||
@ViewChildren('nothing') nothing !: QueryList<any>;
|
||||
@ViewChildren('nothing') nothing!: QueryList<any>;
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule({declarations: [TestCmpt, LikesClicks]});
|
||||
@ -136,11 +138,12 @@ describe('event listeners', () => {
|
||||
|
||||
|
||||
it('should try to execute remaining coalesced listeners if one of the listeners throws', () => {
|
||||
|
||||
@Directive({selector: '[throws-on-clicks]'})
|
||||
class ThrowsOnClicks {
|
||||
@HostListener('click')
|
||||
dontCount() { throw new Error('I was clicked and I don\'t like it!'); }
|
||||
dontCount() {
|
||||
throw new Error('I was clicked and I don\'t like it!');
|
||||
}
|
||||
}
|
||||
|
||||
@Component(
|
||||
@ -151,7 +154,9 @@ describe('event listeners', () => {
|
||||
let noOfErrors = 0;
|
||||
|
||||
class CountingErrorHandler extends ErrorHandler {
|
||||
handleError(error: any): void { noOfErrors++; }
|
||||
handleError(error: any): void {
|
||||
noOfErrors++;
|
||||
}
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule({
|
||||
@ -209,7 +214,9 @@ describe('event listeners', () => {
|
||||
@Input('foo') model: any;
|
||||
@Output('fooChange') update = new EventEmitter();
|
||||
|
||||
updateValue(value: any) { this.update.emit(value); }
|
||||
updateValue(value: any) {
|
||||
this.update.emit(value);
|
||||
}
|
||||
}
|
||||
|
||||
@Component({
|
||||
@ -222,9 +229,13 @@ describe('event listeners', () => {
|
||||
|
||||
@ViewChild(FooDirective) fooDirective: FooDirective|null = null;
|
||||
|
||||
fooChange() { this.count++; }
|
||||
fooChange() {
|
||||
this.count++;
|
||||
}
|
||||
|
||||
triggerUpdate(value: any) { this.fooDirective !.updateValue(value); }
|
||||
triggerUpdate(value: any) {
|
||||
this.fooDirective!.updateValue(value);
|
||||
}
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule({declarations: [TestComponent, FooDirective]});
|
||||
@ -247,19 +258,25 @@ describe('event listeners', () => {
|
||||
})
|
||||
class MyComp {
|
||||
counter = 0;
|
||||
count() { log.push('component.click'); }
|
||||
count() {
|
||||
log.push('component.click');
|
||||
}
|
||||
}
|
||||
|
||||
@Directive({selector: '[dirA]'})
|
||||
class DirA {
|
||||
@HostListener('click')
|
||||
count() { log.push('dirA.click'); }
|
||||
count() {
|
||||
log.push('dirA.click');
|
||||
}
|
||||
}
|
||||
|
||||
@Directive({selector: '[dirB]'})
|
||||
class DirB {
|
||||
@HostListener('click')
|
||||
count() { log.push('dirB.click'); }
|
||||
count() {
|
||||
log.push('dirB.click');
|
||||
}
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule({declarations: [MyComp, DirA, DirB]});
|
||||
|
@ -7,7 +7,7 @@
|
||||
*/
|
||||
|
||||
import {CommonModule} from '@angular/common';
|
||||
import {CUSTOM_ELEMENTS_SCHEMA, Component, Injectable, InjectionToken, NO_ERRORS_SCHEMA, NgModule, NgModuleRef, ɵsetClassMetadata as setClassMetadata, ɵɵdefineComponent as defineComponent, ɵɵdefineInjector as defineInjector, ɵɵdefineNgModule as defineNgModule, ɵɵelement as element} from '@angular/core';
|
||||
import {Component, CUSTOM_ELEMENTS_SCHEMA, Injectable, InjectionToken, NgModule, NgModuleRef, NO_ERRORS_SCHEMA, ɵsetClassMetadata as setClassMetadata, ɵɵdefineComponent as defineComponent, ɵɵdefineInjector as defineInjector, ɵɵdefineNgModule as defineNgModule, ɵɵelement as element} from '@angular/core';
|
||||
import {TestBed} from '@angular/core/testing';
|
||||
import {expect} from '@angular/platform-browser/testing/src/matchers';
|
||||
import {modifiedInIvy, onlyInIvy} from '@angular/private/testing';
|
||||
@ -84,12 +84,16 @@ describe('NgModule', () => {
|
||||
}
|
||||
@NgModule({providers: [Service]})
|
||||
class RoutesModule {
|
||||
constructor(service: Service) { service.initializations.push('RoutesModule'); }
|
||||
constructor(service: Service) {
|
||||
service.initializations.push('RoutesModule');
|
||||
}
|
||||
}
|
||||
|
||||
@NgModule({imports: [RoutesModule]})
|
||||
class AppModule {
|
||||
constructor(service: Service) { service.initializations.push('AppModule'); }
|
||||
constructor(service: Service) {
|
||||
service.initializations.push('AppModule');
|
||||
}
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule({imports: [AppModule]});
|
||||
@ -188,7 +192,6 @@ describe('NgModule', () => {
|
||||
onlyInIvy('unknown element check logs a warning rather than throwing')
|
||||
.it('should warn about unknown element without CUSTOM_ELEMENTS_SCHEMA for element with dash in tag name',
|
||||
() => {
|
||||
|
||||
@Component({template: `<custom-el></custom-el>`})
|
||||
class MyComp {
|
||||
}
|
||||
@ -262,11 +265,12 @@ describe('NgModule', () => {
|
||||
selectors: [['comp']],
|
||||
decls: 1,
|
||||
vars: 0,
|
||||
template: function MyComp_Template(rf, ctx) {
|
||||
if (rf & 1) {
|
||||
element(0, 'custom-el');
|
||||
}
|
||||
},
|
||||
template:
|
||||
function MyComp_Template(rf, ctx) {
|
||||
if (rf & 1) {
|
||||
element(0, 'custom-el');
|
||||
}
|
||||
},
|
||||
encapsulation: 2
|
||||
});
|
||||
}
|
||||
@ -472,7 +476,6 @@ describe('NgModule', () => {
|
||||
fixture.detectChanges();
|
||||
}).not.toThrow();
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
it('should be able to use DI through the NgModuleRef inside the module constructor', () => {
|
||||
@ -484,7 +487,9 @@ describe('NgModule', () => {
|
||||
providers: [{provide: token, useValue: 'foo'}],
|
||||
})
|
||||
class TestModule {
|
||||
constructor(ngRef: NgModuleRef<TestModule>) { value = ngRef.injector.get(token); }
|
||||
constructor(ngRef: NgModuleRef<TestModule>) {
|
||||
value = ngRef.injector.get(token);
|
||||
}
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule({imports: [TestModule], declarations: [TestCmp]});
|
||||
@ -514,5 +519,4 @@ describe('NgModule', () => {
|
||||
TestBed.createComponent(TestCmp);
|
||||
expect(componentInstance).toBeAnInstanceOf(TestCmp);
|
||||
});
|
||||
|
||||
});
|
||||
|
@ -40,10 +40,9 @@ onlyInIvy('Debug information exist in ivy only').describe('ngDevMode debug', ()
|
||||
|
||||
const element: HTMLElement = fixture.nativeElement;
|
||||
fixture.detectChanges();
|
||||
const li = element.querySelector('li') !;
|
||||
const li = element.querySelector('li')!;
|
||||
const embeddedLView = loadLContext(li).lView;
|
||||
expect(embeddedLView.constructor.name).toEqual('LEmbeddedView_MyApp_li_1');
|
||||
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -13,29 +13,27 @@ import {TestBed} from '@angular/core/testing';
|
||||
describe('outputs', () => {
|
||||
@Component({selector: 'button-toggle', template: ''})
|
||||
class ButtonToggle {
|
||||
@Output('change')
|
||||
change = new EventEmitter();
|
||||
@Output('change') change = new EventEmitter();
|
||||
|
||||
@Output('reset')
|
||||
resetStream = new EventEmitter();
|
||||
@Output('reset') resetStream = new EventEmitter();
|
||||
}
|
||||
|
||||
@Directive({selector: '[otherDir]'})
|
||||
class OtherDir {
|
||||
@Output('change')
|
||||
changeStream = new EventEmitter();
|
||||
@Output('change') changeStream = new EventEmitter();
|
||||
}
|
||||
|
||||
@Component({selector: 'destroy-comp', template: ''})
|
||||
class DestroyComp implements OnDestroy {
|
||||
events: string[] = [];
|
||||
ngOnDestroy() { this.events.push('destroy'); }
|
||||
ngOnDestroy() {
|
||||
this.events.push('destroy');
|
||||
}
|
||||
}
|
||||
|
||||
@Directive({selector: '[myButton]'})
|
||||
class MyButton {
|
||||
@Output()
|
||||
click = new EventEmitter();
|
||||
@Output() click = new EventEmitter();
|
||||
}
|
||||
|
||||
it('should call component output function when event is emitted', () => {
|
||||
@ -43,8 +41,10 @@ describe('outputs', () => {
|
||||
|
||||
@Component({template: '<button-toggle (change)="onChange()"></button-toggle>'})
|
||||
class App {
|
||||
@ViewChild(ButtonToggle) buttonToggle !: ButtonToggle;
|
||||
onChange() { counter++; }
|
||||
@ViewChild(ButtonToggle) buttonToggle!: ButtonToggle;
|
||||
onChange() {
|
||||
counter++;
|
||||
}
|
||||
}
|
||||
TestBed.configureTestingModule({declarations: [App, ButtonToggle]});
|
||||
const fixture = TestBed.createComponent(App);
|
||||
@ -64,9 +64,13 @@ describe('outputs', () => {
|
||||
@Component(
|
||||
{template: '<button-toggle (change)="onChange()" (reset)="onReset()"></button-toggle>'})
|
||||
class App {
|
||||
@ViewChild(ButtonToggle) buttonToggle !: ButtonToggle;
|
||||
onChange() { counter++; }
|
||||
onReset() { resetCounter++; }
|
||||
@ViewChild(ButtonToggle) buttonToggle!: ButtonToggle;
|
||||
onChange() {
|
||||
counter++;
|
||||
}
|
||||
onReset() {
|
||||
resetCounter++;
|
||||
}
|
||||
}
|
||||
TestBed.configureTestingModule({declarations: [App, ButtonToggle]});
|
||||
const fixture = TestBed.createComponent(App);
|
||||
@ -82,7 +86,7 @@ describe('outputs', () => {
|
||||
it('should eval component output expression when event is emitted', () => {
|
||||
@Component({template: '<button-toggle (change)="counter = counter + 1"></button-toggle>'})
|
||||
class App {
|
||||
@ViewChild(ButtonToggle) buttonToggle !: ButtonToggle;
|
||||
@ViewChild(ButtonToggle) buttonToggle!: ButtonToggle;
|
||||
counter = 0;
|
||||
}
|
||||
TestBed.configureTestingModule({declarations: [App, ButtonToggle]});
|
||||
@ -102,10 +106,12 @@ describe('outputs', () => {
|
||||
@Component(
|
||||
{template: '<button-toggle *ngIf="condition" (change)="onChange()"></button-toggle>'})
|
||||
class App {
|
||||
@ViewChild(ButtonToggle) buttonToggle !: ButtonToggle;
|
||||
@ViewChild(ButtonToggle) buttonToggle!: ButtonToggle;
|
||||
condition = true;
|
||||
|
||||
onChange() { counter++; }
|
||||
onChange() {
|
||||
counter++;
|
||||
}
|
||||
}
|
||||
TestBed.configureTestingModule({imports: [CommonModule], declarations: [App, ButtonToggle]});
|
||||
const fixture = TestBed.createComponent(App);
|
||||
@ -133,11 +139,13 @@ describe('outputs', () => {
|
||||
`
|
||||
})
|
||||
class App {
|
||||
@ViewChild(ButtonToggle) buttonToggle !: ButtonToggle;
|
||||
@ViewChild(ButtonToggle) buttonToggle!: ButtonToggle;
|
||||
condition = true;
|
||||
condition2 = true;
|
||||
|
||||
onChange() { counter++; }
|
||||
onChange() {
|
||||
counter++;
|
||||
}
|
||||
}
|
||||
TestBed.configureTestingModule({imports: [CommonModule], declarations: [App, ButtonToggle]});
|
||||
const fixture = TestBed.createComponent(App);
|
||||
@ -168,12 +176,16 @@ describe('outputs', () => {
|
||||
`
|
||||
})
|
||||
class App {
|
||||
@ViewChild(ButtonToggle) buttonToggle !: ButtonToggle;
|
||||
@ViewChild(DestroyComp) destroyComp !: DestroyComp;
|
||||
@ViewChild(ButtonToggle) buttonToggle!: ButtonToggle;
|
||||
@ViewChild(DestroyComp) destroyComp!: DestroyComp;
|
||||
condition = true;
|
||||
|
||||
onClick() { clickCounter++; }
|
||||
onChange() { changeCounter++; }
|
||||
onClick() {
|
||||
clickCounter++;
|
||||
}
|
||||
onChange() {
|
||||
changeCounter++;
|
||||
}
|
||||
}
|
||||
TestBed.configureTestingModule(
|
||||
{imports: [CommonModule], declarations: [App, ButtonToggle, DestroyComp]});
|
||||
@ -206,8 +218,10 @@ describe('outputs', () => {
|
||||
|
||||
@Component({template: '<button myButton (click)="onClick()">Click me</button>'})
|
||||
class App {
|
||||
@ViewChild(MyButton) buttonDir !: MyButton;
|
||||
onClick() { counter++; }
|
||||
@ViewChild(MyButton) buttonDir!: MyButton;
|
||||
onClick() {
|
||||
counter++;
|
||||
}
|
||||
}
|
||||
TestBed.configureTestingModule({declarations: [App, MyButton]});
|
||||
const fixture = TestBed.createComponent(App);
|
||||
@ -228,9 +242,11 @@ describe('outputs', () => {
|
||||
|
||||
@Component({template: '<button-toggle (change)="onChange()" otherDir></button-toggle>'})
|
||||
class App {
|
||||
@ViewChild(ButtonToggle) buttonToggle !: ButtonToggle;
|
||||
@ViewChild(OtherDir) otherDir !: OtherDir;
|
||||
onChange() { counter++; }
|
||||
@ViewChild(ButtonToggle) buttonToggle!: ButtonToggle;
|
||||
@ViewChild(OtherDir) otherDir!: OtherDir;
|
||||
onChange() {
|
||||
counter++;
|
||||
}
|
||||
}
|
||||
TestBed.configureTestingModule({declarations: [App, ButtonToggle, OtherDir]});
|
||||
const fixture = TestBed.createComponent(App);
|
||||
@ -248,8 +264,7 @@ describe('outputs', () => {
|
||||
|
||||
@Directive({selector: '[otherChangeDir]'})
|
||||
class OtherChangeDir {
|
||||
@Input()
|
||||
change !: boolean;
|
||||
@Input() change!: boolean;
|
||||
}
|
||||
|
||||
@Component({
|
||||
@ -257,11 +272,13 @@ describe('outputs', () => {
|
||||
'<button-toggle (change)="onChange()" otherChangeDir [change]="change"></button-toggle>'
|
||||
})
|
||||
class App {
|
||||
@ViewChild(ButtonToggle) buttonToggle !: ButtonToggle;
|
||||
@ViewChild(OtherChangeDir) otherDir !: OtherChangeDir;
|
||||
@ViewChild(ButtonToggle) buttonToggle!: ButtonToggle;
|
||||
@ViewChild(OtherChangeDir) otherDir!: OtherChangeDir;
|
||||
change = true;
|
||||
|
||||
onChange() { counter++; }
|
||||
onChange() {
|
||||
counter++;
|
||||
}
|
||||
}
|
||||
TestBed.configureTestingModule({declarations: [App, ButtonToggle, OtherChangeDir]});
|
||||
const fixture = TestBed.createComponent(App);
|
||||
@ -278,5 +295,4 @@ describe('outputs', () => {
|
||||
buttonToggle.change.next();
|
||||
expect(counter).toBe(1);
|
||||
});
|
||||
|
||||
});
|
||||
|
@ -14,7 +14,9 @@ describe('pipe', () => {
|
||||
@Pipe({name: 'countingPipe'})
|
||||
class CountingPipe implements PipeTransform {
|
||||
state: number = 0;
|
||||
transform(value: any) { return `${value} state:${this.state++}`; }
|
||||
transform(value: any) {
|
||||
return `${value} state:${this.state++}`;
|
||||
}
|
||||
}
|
||||
|
||||
@Pipe({name: 'multiArgPipe'})
|
||||
@ -57,20 +59,21 @@ describe('pipe', () => {
|
||||
it('should support bindings', () => {
|
||||
@Directive({selector: '[my-dir]'})
|
||||
class Dir {
|
||||
@Input()
|
||||
dirProp: string = '';
|
||||
@Input() dirProp: string = '';
|
||||
}
|
||||
|
||||
@Pipe({name: 'double'})
|
||||
class DoublePipe implements PipeTransform {
|
||||
transform(value: any) { return `${value}${value}`; }
|
||||
transform(value: any) {
|
||||
return `${value}${value}`;
|
||||
}
|
||||
}
|
||||
|
||||
@Component({
|
||||
template: `<div my-dir [dirProp]="'a'|double"></div>`,
|
||||
})
|
||||
class App {
|
||||
@ViewChild(Dir) directive !: Dir;
|
||||
@ViewChild(Dir) directive!: Dir;
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule({declarations: [App, DoublePipe, Dir]});
|
||||
@ -113,7 +116,9 @@ describe('pipe', () => {
|
||||
it('should pick a Pipe defined in `declarations` over imported Pipes', () => {
|
||||
@Pipe({name: 'number'})
|
||||
class PipeA implements PipeTransform {
|
||||
transform(value: any) { return `PipeA: ${value}`; }
|
||||
transform(value: any) {
|
||||
return `PipeA: ${value}`;
|
||||
}
|
||||
}
|
||||
|
||||
@NgModule({
|
||||
@ -125,7 +130,9 @@ describe('pipe', () => {
|
||||
|
||||
@Pipe({name: 'number'})
|
||||
class PipeB implements PipeTransform {
|
||||
transform(value: any) { return `PipeB: ${value}`; }
|
||||
transform(value: any) {
|
||||
return `PipeB: ${value}`;
|
||||
}
|
||||
}
|
||||
|
||||
@Component({
|
||||
@ -150,7 +157,9 @@ describe('pipe', () => {
|
||||
() => {
|
||||
@Pipe({name: 'number'})
|
||||
class PipeA implements PipeTransform {
|
||||
transform(value: any) { return `PipeA: ${value}`; }
|
||||
transform(value: any) {
|
||||
return `PipeA: ${value}`;
|
||||
}
|
||||
}
|
||||
|
||||
@NgModule({
|
||||
@ -162,7 +171,9 @@ describe('pipe', () => {
|
||||
|
||||
@Pipe({name: 'number'})
|
||||
class PipeB implements PipeTransform {
|
||||
transform(value: any) { return `PipeB: ${value}`; }
|
||||
transform(value: any) {
|
||||
return `PipeB: ${value}`;
|
||||
}
|
||||
}
|
||||
|
||||
@NgModule({
|
||||
@ -222,12 +233,16 @@ describe('pipe', () => {
|
||||
it('should support duplicates by using the later entry', () => {
|
||||
@Pipe({name: 'duplicatePipe'})
|
||||
class DuplicatePipe1 implements PipeTransform {
|
||||
transform(value: any) { return `${value} from duplicate 1`; }
|
||||
transform(value: any) {
|
||||
return `${value} from duplicate 1`;
|
||||
}
|
||||
}
|
||||
|
||||
@Pipe({name: 'duplicatePipe'})
|
||||
class DuplicatePipe2 implements PipeTransform {
|
||||
transform(value: any) { return `${value} from duplicate 2`; }
|
||||
transform(value: any) {
|
||||
return `${value} from duplicate 2`;
|
||||
}
|
||||
}
|
||||
|
||||
@Component({
|
||||
@ -247,7 +262,9 @@ describe('pipe', () => {
|
||||
it('should support pipe in context of ternary operator', () => {
|
||||
@Pipe({name: 'pipe'})
|
||||
class MyPipe implements PipeTransform {
|
||||
transform(value: any): any { return value; }
|
||||
transform(value: any): any {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
@Component({
|
||||
@ -313,8 +330,12 @@ describe('pipe', () => {
|
||||
@Pipe({name: 'countingImpurePipe', pure: false})
|
||||
class CountingImpurePipe implements PipeTransform {
|
||||
state: number = 0;
|
||||
transform(value: any) { return `${value} state:${this.state++}`; }
|
||||
constructor() { impurePipeInstances.push(this); }
|
||||
transform(value: any) {
|
||||
return `${value} state:${this.state++}`;
|
||||
}
|
||||
constructor() {
|
||||
impurePipeInstances.push(this);
|
||||
}
|
||||
}
|
||||
|
||||
beforeEach(() => impurePipeInstances = []);
|
||||
@ -372,8 +393,12 @@ describe('pipe', () => {
|
||||
|
||||
@Pipe({name: 'pipeWithOnDestroy'})
|
||||
class PipeWithOnDestroy implements PipeTransform, OnDestroy {
|
||||
ngOnDestroy() { destroyCalls++; }
|
||||
transform(value: any): any { return null; }
|
||||
ngOnDestroy() {
|
||||
destroyCalls++;
|
||||
}
|
||||
transform(value: any): any {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Component({
|
||||
@ -401,7 +426,9 @@ describe('pipe', () => {
|
||||
@Pipe({name: 'myConcatPipe'})
|
||||
class ConcatPipe implements PipeTransform {
|
||||
constructor(public service: Service) {}
|
||||
transform(value: string): string { return `${value} - ${this.service.title}`; }
|
||||
transform(value: string): string {
|
||||
return `${value} - ${this.service.title}`;
|
||||
}
|
||||
}
|
||||
|
||||
@Component({
|
||||
@ -428,7 +455,9 @@ describe('pipe', () => {
|
||||
@Pipe({name: 'myConcatPipe'})
|
||||
class ConcatPipe implements PipeTransform {
|
||||
constructor(@Inject(token) public service: Service) {}
|
||||
transform(value: string): string { return `${value} - ${this.service.title}`; }
|
||||
transform(value: string): string {
|
||||
return `${value} - ${this.service.title}`;
|
||||
}
|
||||
}
|
||||
|
||||
@Component({
|
||||
@ -461,7 +490,9 @@ describe('pipe', () => {
|
||||
@Pipe({name: 'myConcatPipe'})
|
||||
class ConcatPipe implements PipeTransform {
|
||||
constructor(public service: Service) {}
|
||||
transform(value: string): string { return `${value} - ${this.service.title}`; }
|
||||
transform(value: string): string {
|
||||
return `${value} - ${this.service.title}`;
|
||||
}
|
||||
}
|
||||
|
||||
@Component({
|
||||
@ -501,7 +532,7 @@ describe('pipe', () => {
|
||||
})
|
||||
class App {
|
||||
@Input() something: any;
|
||||
@ViewChild(SomeComp) comp !: SomeComp;
|
||||
@ViewChild(SomeComp) comp!: SomeComp;
|
||||
pipeValue = 10;
|
||||
displayValue = 0;
|
||||
}
|
||||
@ -512,7 +543,9 @@ describe('pipe', () => {
|
||||
pipeChangeDetectorRef = changeDetectorRef;
|
||||
}
|
||||
|
||||
transform() { return ''; }
|
||||
transform() {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule({declarations: [App, SomeComp, TestPipe]});
|
||||
@ -521,7 +554,7 @@ describe('pipe', () => {
|
||||
|
||||
fixture.componentInstance.displayValue = 1;
|
||||
fixture.componentInstance.comp.displayValue = 1;
|
||||
pipeChangeDetectorRef !.markForCheck();
|
||||
pipeChangeDetectorRef!.markForCheck();
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(fixture.nativeElement.textContent).toContain('Outer value: "1"');
|
||||
@ -553,7 +586,7 @@ describe('pipe', () => {
|
||||
})
|
||||
class App {
|
||||
@Input() something: any;
|
||||
@ViewChild(SomeComp) comp !: SomeComp;
|
||||
@ViewChild(SomeComp) comp!: SomeComp;
|
||||
pipeValue = 10;
|
||||
displayValue = 0;
|
||||
}
|
||||
@ -564,7 +597,9 @@ describe('pipe', () => {
|
||||
pipeChangeDetectorRef = changeDetectorRef;
|
||||
}
|
||||
|
||||
transform() { return ''; }
|
||||
transform() {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule({declarations: [App, SomeComp, TestPipe]});
|
||||
@ -573,22 +608,21 @@ describe('pipe', () => {
|
||||
|
||||
fixture.componentInstance.displayValue = 1;
|
||||
fixture.componentInstance.comp.displayValue = 1;
|
||||
pipeChangeDetectorRef !.markForCheck();
|
||||
pipeChangeDetectorRef!.markForCheck();
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(fixture.nativeElement.textContent).toContain('Outer value: "1"');
|
||||
expect(fixture.nativeElement.textContent).toContain('Inner value: "0"');
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('pure pipe error handling', () => {
|
||||
|
||||
it('should not re-invoke pure pipes if it fails initially', () => {
|
||||
|
||||
@Pipe({name: 'throwPipe', pure: true})
|
||||
class ThrowPipe implements PipeTransform {
|
||||
transform(): never { throw new Error('ThrowPipeError'); }
|
||||
transform(): never {
|
||||
throw new Error('ThrowPipeError');
|
||||
}
|
||||
}
|
||||
@Component({template: `{{val | throwPipe}}`})
|
||||
class App {
|
||||
@ -607,7 +641,6 @@ describe('pipe', () => {
|
||||
|
||||
|
||||
it('should display the last known result from a pure pipe when it throws', () => {
|
||||
|
||||
@Pipe({name: 'throwPipe', pure: true})
|
||||
class ThrowPipe implements PipeTransform {
|
||||
transform(value: string): string {
|
||||
@ -647,7 +680,8 @@ describe('pipe', () => {
|
||||
describe('pure pipe error handling with multiple arguments', () => {
|
||||
const args: string[] = new Array(10).fill(':0');
|
||||
for (let numberOfPipeArgs = 0; numberOfPipeArgs < args.length; numberOfPipeArgs++) {
|
||||
it(`should not invoke ${numberOfPipeArgs} argument pure pipe second time if it throws unless input changes`,
|
||||
it(`should not invoke ${
|
||||
numberOfPipeArgs} argument pure pipe second time if it throws unless input changes`,
|
||||
() => {
|
||||
// https://stackblitz.com/edit/angular-mbx2pg
|
||||
const log: string[] = [];
|
||||
@ -685,8 +719,5 @@ describe('pipe', () => {
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
|
||||
});
|
||||
|
@ -91,7 +91,6 @@ describe('property bindings', () => {
|
||||
it('should not map properties whose names do not correspond to their attribute names, ' +
|
||||
'if they correspond to inputs',
|
||||
() => {
|
||||
|
||||
@Component({template: '', selector: 'my-comp'})
|
||||
class MyComp {
|
||||
@Input() for !:string;
|
||||
@ -393,7 +392,6 @@ describe('property bindings', () => {
|
||||
});
|
||||
|
||||
describe('attributes and input properties', () => {
|
||||
|
||||
@Directive({selector: '[myDir]', exportAs: 'myDir'})
|
||||
class MyDir {
|
||||
@Input() role: string|undefined;
|
||||
@ -596,7 +594,6 @@ describe('property bindings', () => {
|
||||
expect(comp2.children[0].getAttribute('role')).toBe('button');
|
||||
expect(comp2.textContent).toBe('role: button');
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
it('should not throw on synthetic property bindings when a directive on the same element injects ViewContainerRef',
|
||||
@ -626,5 +623,4 @@ describe('property bindings', () => {
|
||||
fixture.detectChanges();
|
||||
}).not.toThrow();
|
||||
});
|
||||
|
||||
});
|
||||
|
@ -8,7 +8,7 @@
|
||||
import {Component} from '@angular/core';
|
||||
import {TestBed} from '@angular/core/testing';
|
||||
import {By} from '@angular/platform-browser';
|
||||
import {of } from 'rxjs';
|
||||
import {of} from 'rxjs';
|
||||
|
||||
describe('property interpolation', () => {
|
||||
it('should handle all flavors of interpolated properties', () => {
|
||||
@ -67,7 +67,7 @@ describe('property interpolation', () => {
|
||||
`
|
||||
})
|
||||
class App {
|
||||
details = of ({
|
||||
details = of({
|
||||
title: 'cool image',
|
||||
url: 'http://somecooldomain:1234/cool_image.png',
|
||||
});
|
||||
@ -93,7 +93,11 @@ describe('property interpolation', () => {
|
||||
/** Clearly this is a doctor of heavy metals. */
|
||||
leadSurgeon = {
|
||||
getCommonInfo() {
|
||||
return {getPhotoUrl() { return 'http://somecooldomain:1234/cool_image.png'; }};
|
||||
return {
|
||||
getPhotoUrl() {
|
||||
return 'http://somecooldomain:1234/cool_image.png';
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -18,8 +18,7 @@ describe('components using pure function instructions internally', () => {
|
||||
template: ``,
|
||||
})
|
||||
class MyComp {
|
||||
@Input()
|
||||
names: string[] = [];
|
||||
@Input() names: string[] = [];
|
||||
}
|
||||
|
||||
|
||||
@ -60,7 +59,7 @@ describe('components using pure function instructions internally', () => {
|
||||
myComp.names = ['should not be overwritten'];
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(myComp !.names).toEqual(['should not be overwritten']);
|
||||
expect(myComp!.names).toEqual(['should not be overwritten']);
|
||||
});
|
||||
|
||||
|
||||
@ -91,11 +90,9 @@ describe('components using pure function instructions internally', () => {
|
||||
template: ``,
|
||||
})
|
||||
class ManyPropComp {
|
||||
@Input()
|
||||
names1: string[] = [];
|
||||
@Input() names1: string[] = [];
|
||||
|
||||
@Input()
|
||||
names2: string[] = [];
|
||||
@Input() names2: string[] = [];
|
||||
}
|
||||
|
||||
@Component({
|
||||
@ -117,14 +114,14 @@ describe('components using pure function instructions internally', () => {
|
||||
fixture.detectChanges();
|
||||
const manyPropComp = fixture.debugElement.query(By.directive(ManyPropComp)).componentInstance;
|
||||
|
||||
expect(manyPropComp !.names1).toEqual(['Nancy', 'Carson']);
|
||||
expect(manyPropComp !.names2).toEqual(['George']);
|
||||
expect(manyPropComp!.names1).toEqual(['Nancy', 'Carson']);
|
||||
expect(manyPropComp!.names2).toEqual(['George']);
|
||||
|
||||
fixture.componentInstance.customName = 'George';
|
||||
fixture.componentInstance.customName2 = 'Carson';
|
||||
fixture.detectChanges();
|
||||
expect(manyPropComp !.names1).toEqual(['Nancy', 'George']);
|
||||
expect(manyPropComp !.names2).toEqual(['Carson']);
|
||||
expect(manyPropComp!.names1).toEqual(['Nancy', 'George']);
|
||||
expect(manyPropComp!.names2).toEqual(['Carson']);
|
||||
});
|
||||
|
||||
|
||||
@ -222,7 +219,6 @@ describe('components using pure function instructions internally', () => {
|
||||
|
||||
|
||||
it('should work up to 8 bindings', () => {
|
||||
|
||||
@Component({
|
||||
template: `
|
||||
<my-comp [names]="['a', 'b', 'c', 'd', 'e', 'f', 'g', v8]"></my-comp>
|
||||
@ -346,8 +342,7 @@ describe('components using pure function instructions internally', () => {
|
||||
template: ``,
|
||||
})
|
||||
class ObjectComp {
|
||||
@Input()
|
||||
config: any = [];
|
||||
@Input() config: any = [];
|
||||
}
|
||||
|
||||
it('should support an object literal', () => {
|
||||
@ -495,7 +490,7 @@ describe('components using pure function instructions internally', () => {
|
||||
`
|
||||
})
|
||||
class App {
|
||||
@ViewChildren(Dir) directives !: QueryList<Dir>;
|
||||
@ViewChildren(Dir) directives!: QueryList<Dir>;
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule({declarations: [Dir, App]});
|
||||
@ -514,7 +509,7 @@ describe('components using pure function instructions internally', () => {
|
||||
`
|
||||
})
|
||||
class App {
|
||||
@ViewChildren(Dir) directives !: QueryList<Dir>;
|
||||
@ViewChildren(Dir) directives!: QueryList<Dir>;
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule({declarations: [Dir, App]});
|
||||
@ -528,7 +523,7 @@ describe('components using pure function instructions internally', () => {
|
||||
it('should not share object literals across component instances', () => {
|
||||
@Component({template: `<div [dir]="{}"></div>`})
|
||||
class App {
|
||||
@ViewChild(Dir) directive !: Dir;
|
||||
@ViewChild(Dir) directive!: Dir;
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule({declarations: [Dir, App]});
|
||||
@ -545,7 +540,7 @@ describe('components using pure function instructions internally', () => {
|
||||
it('should not share array literals across component instances', () => {
|
||||
@Component({template: `<div [dir]="[]"></div>`})
|
||||
class App {
|
||||
@ViewChild(Dir) directive !: Dir;
|
||||
@ViewChild(Dir) directive!: Dir;
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule({declarations: [Dir, App]});
|
||||
@ -567,7 +562,7 @@ describe('components using pure function instructions internally', () => {
|
||||
`
|
||||
})
|
||||
class App {
|
||||
@ViewChildren(Dir) directives !: QueryList<Dir>;
|
||||
@ViewChildren(Dir) directives!: QueryList<Dir>;
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule({declarations: [Dir, App]});
|
||||
@ -586,7 +581,7 @@ describe('components using pure function instructions internally', () => {
|
||||
`
|
||||
})
|
||||
class App {
|
||||
@ViewChildren(Dir) directives !: QueryList<Dir>;
|
||||
@ViewChildren(Dir) directives!: QueryList<Dir>;
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule({declarations: [Dir, App]});
|
||||
@ -605,8 +600,10 @@ describe('components using pure function instructions internally', () => {
|
||||
`
|
||||
})
|
||||
class App {
|
||||
@ViewChildren(Dir) directives !: QueryList<Dir>;
|
||||
getFoo() { return 'foo!'; }
|
||||
@ViewChildren(Dir) directives!: QueryList<Dir>;
|
||||
getFoo() {
|
||||
return 'foo!';
|
||||
}
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule({declarations: [Dir, App]});
|
||||
@ -616,8 +613,5 @@ describe('components using pure function instructions internally', () => {
|
||||
|
||||
expect(values).toEqual([{foo: null}, {foo: 'foo!'}]);
|
||||
});
|
||||
|
||||
|
||||
|
||||
});
|
||||
});
|
||||
|
@ -7,7 +7,7 @@
|
||||
*/
|
||||
|
||||
import {CommonModule} from '@angular/common';
|
||||
import {AfterViewInit, Component, ContentChild, ContentChildren, Directive, ElementRef, Input, QueryList, TemplateRef, Type, ViewChild, ViewChildren, ViewContainerRef, ViewRef, forwardRef} from '@angular/core';
|
||||
import {AfterViewInit, Component, ContentChild, ContentChildren, Directive, ElementRef, forwardRef, Input, QueryList, TemplateRef, Type, ViewChild, ViewChildren, ViewContainerRef, ViewRef} from '@angular/core';
|
||||
import {TestBed} from '@angular/core/testing';
|
||||
import {By} from '@angular/platform-browser';
|
||||
import {expect} from '@angular/platform-browser/testing/src/matchers';
|
||||
@ -215,7 +215,7 @@ describe('query logic', () => {
|
||||
}
|
||||
|
||||
class MyComp {
|
||||
@ViewChildren(SomeDir) foo !: QueryList<SomeDir>;
|
||||
@ViewChildren(SomeDir) foo!: QueryList<SomeDir>;
|
||||
}
|
||||
|
||||
@Component({
|
||||
@ -242,7 +242,7 @@ describe('query logic', () => {
|
||||
}
|
||||
|
||||
class MySuperComp {
|
||||
@ViewChildren(SomeDir) foo !: QueryList<SomeDir>;
|
||||
@ViewChildren(SomeDir) foo!: QueryList<SomeDir>;
|
||||
}
|
||||
|
||||
class MyComp extends MySuperComp {}
|
||||
@ -275,7 +275,7 @@ describe('query logic', () => {
|
||||
template: `<ng-container [ngTemplateOutlet]="content"></ng-container>`
|
||||
})
|
||||
class Insertion {
|
||||
@Input() content !: TemplateRef<{}>;
|
||||
@Input() content!: TemplateRef<{}>;
|
||||
}
|
||||
|
||||
@Component({
|
||||
@ -287,7 +287,7 @@ describe('query logic', () => {
|
||||
`
|
||||
})
|
||||
class App {
|
||||
@ViewChild(Required) requiredEl !: Required;
|
||||
@ViewChild(Required) requiredEl!: Required;
|
||||
viewChildAvailableInAfterViewInit?: boolean;
|
||||
|
||||
ngAfterViewInit() {
|
||||
@ -300,7 +300,6 @@ describe('query logic', () => {
|
||||
fixture.detectChanges();
|
||||
expect(fixture.componentInstance.viewChildAvailableInAfterViewInit).toBe(true);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('content queries', () => {
|
||||
@ -538,7 +537,7 @@ describe('query logic', () => {
|
||||
|
||||
@Component({template: '<sub-comp><div #foo></div></sub-comp>'})
|
||||
class App {
|
||||
@ViewChild(SubComp) subComp !: SubComp;
|
||||
@ViewChild(SubComp) subComp!: SubComp;
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule({declarations: [App, SubComp]});
|
||||
@ -561,7 +560,7 @@ describe('query logic', () => {
|
||||
|
||||
@Component({template: '<sub-comp><div #foo></div></sub-comp>'})
|
||||
class App {
|
||||
@ViewChild(SubComp) subComp !: SubComp;
|
||||
@ViewChild(SubComp) subComp!: SubComp;
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule({declarations: [App, SubComp]});
|
||||
@ -577,7 +576,7 @@ describe('query logic', () => {
|
||||
}
|
||||
|
||||
class MyComp {
|
||||
@ContentChildren(SomeDir) foo !: QueryList<SomeDir>;
|
||||
@ContentChildren(SomeDir) foo!: QueryList<SomeDir>;
|
||||
}
|
||||
|
||||
@Component({selector: 'sub-comp', template: '<ng-content></ng-content>'})
|
||||
@ -593,7 +592,7 @@ describe('query logic', () => {
|
||||
`
|
||||
})
|
||||
class App {
|
||||
@ViewChild(SubComp) subComp !: SubComp;
|
||||
@ViewChild(SubComp) subComp!: SubComp;
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule({declarations: [App, SubComp, SomeDir]});
|
||||
@ -610,7 +609,7 @@ describe('query logic', () => {
|
||||
}
|
||||
|
||||
class MySuperComp {
|
||||
@ContentChildren(SomeDir) foo !: QueryList<SomeDir>;
|
||||
@ContentChildren(SomeDir) foo!: QueryList<SomeDir>;
|
||||
}
|
||||
|
||||
class MyComp extends MySuperComp {}
|
||||
@ -628,7 +627,7 @@ describe('query logic', () => {
|
||||
`
|
||||
})
|
||||
class App {
|
||||
@ViewChild(SubComp) subComp !: SubComp;
|
||||
@ViewChild(SubComp) subComp!: SubComp;
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule({declarations: [App, SubComp, SomeDir]});
|
||||
@ -657,7 +656,7 @@ describe('query logic', () => {
|
||||
template: '',
|
||||
})
|
||||
class ShallowComp {
|
||||
@ContentChildren('foo', {descendants: false}) foos !: QueryList<ElementRef>;
|
||||
@ContentChildren('foo', {descendants: false}) foos!: QueryList<ElementRef>;
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule(
|
||||
@ -666,7 +665,7 @@ describe('query logic', () => {
|
||||
fixture.detectChanges();
|
||||
|
||||
const shallowComp = fixture.debugElement.query(By.directive(ShallowComp)).componentInstance;
|
||||
const queryList = shallowComp !.foos;
|
||||
const queryList = shallowComp!.foos;
|
||||
expect(queryList.length).toBe(0);
|
||||
|
||||
fixture.componentInstance.showing = true;
|
||||
@ -691,24 +690,24 @@ describe('query logic', () => {
|
||||
});
|
||||
|
||||
describe('descendants: false (default)', () => {
|
||||
|
||||
/**
|
||||
* A helper function to check if a given object looks like ElementRef. It is used in place of
|
||||
* the `instanceof ElementRef` check since ivy returns a type that looks like ElementRef (have
|
||||
* the same properties but doesn't pass the instanceof ElementRef test)
|
||||
*/
|
||||
function isElementRefLike(result: any): boolean { return result.nativeElement != null; }
|
||||
function isElementRefLike(result: any): boolean {
|
||||
return result.nativeElement != null;
|
||||
}
|
||||
|
||||
it('should match directives on elements that used to be wrapped by a required parent in HTML parser',
|
||||
() => {
|
||||
|
||||
@Directive({selector: '[myDef]'})
|
||||
class MyDef {
|
||||
}
|
||||
|
||||
@Component({selector: 'my-container', template: ``})
|
||||
class MyContainer {
|
||||
@ContentChildren(MyDef) myDefs !: QueryList<MyDef>;
|
||||
@ContentChildren(MyDef) myDefs!: QueryList<MyDef>;
|
||||
}
|
||||
@Component(
|
||||
{selector: 'test-cmpt', template: `<my-container><tr myDef></tr></my-container>`})
|
||||
@ -724,10 +723,9 @@ describe('query logic', () => {
|
||||
});
|
||||
|
||||
it('should match elements with local refs inside <ng-container>', () => {
|
||||
|
||||
@Component({selector: 'needs-target', template: ``})
|
||||
class NeedsTarget {
|
||||
@ContentChildren('target') targets !: QueryList<ElementRef>;
|
||||
@ContentChildren('target') targets!: QueryList<ElementRef>;
|
||||
}
|
||||
@Component({
|
||||
selector: 'test-cmpt',
|
||||
@ -752,10 +750,9 @@ describe('query logic', () => {
|
||||
});
|
||||
|
||||
it('should match elements with local refs inside nested <ng-container>', () => {
|
||||
|
||||
@Component({selector: 'needs-target', template: ``})
|
||||
class NeedsTarget {
|
||||
@ContentChildren('target') targets !: QueryList<ElementRef>;
|
||||
@ContentChildren('target') targets!: QueryList<ElementRef>;
|
||||
}
|
||||
|
||||
@Component({
|
||||
@ -791,7 +788,7 @@ describe('query logic', () => {
|
||||
|
||||
@Component({selector: 'needs-target', template: ``})
|
||||
class NeedsTarget {
|
||||
@ContentChildren(TargetDir) targets !: QueryList<HTMLElement>;
|
||||
@ContentChildren(TargetDir) targets!: QueryList<HTMLElement>;
|
||||
}
|
||||
|
||||
@Component({
|
||||
@ -823,7 +820,7 @@ describe('query logic', () => {
|
||||
|
||||
@Component({selector: 'needs-target', template: ``})
|
||||
class NeedsTarget {
|
||||
@ContentChildren(TargetDir) targets !: QueryList<HTMLElement>;
|
||||
@ContentChildren(TargetDir) targets!: QueryList<HTMLElement>;
|
||||
}
|
||||
|
||||
@Component({
|
||||
@ -859,7 +856,7 @@ describe('query logic', () => {
|
||||
|
||||
@Directive({selector: '[needs-target]'})
|
||||
class NeedsTarget {
|
||||
@ContentChildren(TargetDir) targets !: QueryList<HTMLElement>;
|
||||
@ContentChildren(TargetDir) targets!: QueryList<HTMLElement>;
|
||||
}
|
||||
|
||||
@Component({
|
||||
@ -893,8 +890,8 @@ describe('query logic', () => {
|
||||
|
||||
@Component({selector: 'needs-target', template: ``})
|
||||
class NeedsTarget {
|
||||
@ContentChildren(TargetDir) dirTargets !: QueryList<TargetDir>;
|
||||
@ContentChildren('target') localRefsTargets !: QueryList<ElementRef>;
|
||||
@ContentChildren(TargetDir) dirTargets!: QueryList<TargetDir>;
|
||||
@ContentChildren('target') localRefsTargets!: QueryList<ElementRef>;
|
||||
}
|
||||
|
||||
@Component({
|
||||
@ -931,7 +928,7 @@ describe('query logic', () => {
|
||||
|
||||
@Component({selector: 'needs-target', template: ``})
|
||||
class NeedsTarget {
|
||||
@ContentChildren(TargetDir) targets !: QueryList<HTMLElement>;
|
||||
@ContentChildren(TargetDir) targets!: QueryList<HTMLElement>;
|
||||
}
|
||||
|
||||
@Component({
|
||||
@ -963,7 +960,6 @@ describe('query logic', () => {
|
||||
|
||||
|
||||
describe('observable interface', () => {
|
||||
|
||||
it('should allow observing changes to query list', () => {
|
||||
const fixture = TestBed.createComponent(QueryCompWithChanges);
|
||||
let changes = 0;
|
||||
@ -983,13 +979,10 @@ describe('query logic', () => {
|
||||
fixture.detectChanges();
|
||||
expect(changes).toBe(1);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('view boundaries', () => {
|
||||
|
||||
describe('ViewContainerRef', () => {
|
||||
|
||||
@Directive({selector: '[vc]', exportAs: 'vc'})
|
||||
class ViewContainerManipulatorDirective {
|
||||
constructor(private _vcRef: ViewContainerRef) {}
|
||||
@ -998,9 +991,13 @@ describe('query logic', () => {
|
||||
return this._vcRef.createEmbeddedView(tpl, ctx, idx);
|
||||
}
|
||||
|
||||
remove(index?: number) { this._vcRef.remove(index); }
|
||||
remove(index?: number) {
|
||||
this._vcRef.remove(index);
|
||||
}
|
||||
|
||||
move(viewRef: ViewRef, index: number) { this._vcRef.move(viewRef, index); }
|
||||
move(viewRef: ViewRef, index: number) {
|
||||
this._vcRef.move(viewRef, index);
|
||||
}
|
||||
}
|
||||
|
||||
it('should report results in views inserted / removed by ngIf', () => {
|
||||
@ -1014,7 +1011,7 @@ describe('query logic', () => {
|
||||
})
|
||||
class TestComponent {
|
||||
value: boolean = false;
|
||||
@ViewChildren('foo') query !: QueryList<any>;
|
||||
@ViewChildren('foo') query!: QueryList<any>;
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule({declarations: [TestComponent]});
|
||||
@ -1045,7 +1042,7 @@ describe('query logic', () => {
|
||||
})
|
||||
class TestComponent {
|
||||
value: string[]|undefined;
|
||||
@ViewChildren('foo') query !: QueryList<any>;
|
||||
@ViewChildren('foo') query!: QueryList<any>;
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule({declarations: [TestComponent]});
|
||||
@ -1082,7 +1079,6 @@ describe('query logic', () => {
|
||||
*/
|
||||
it('should notify on changes when a given view is removed and re-inserted at the same index',
|
||||
() => {
|
||||
|
||||
@Component({
|
||||
selector: 'test-comp',
|
||||
template: `
|
||||
@ -1093,10 +1089,9 @@ describe('query logic', () => {
|
||||
class TestComponent implements AfterViewInit {
|
||||
queryListNotificationCounter = 0;
|
||||
|
||||
@ViewChild(ViewContainerManipulatorDirective)
|
||||
vc !: ViewContainerManipulatorDirective;
|
||||
@ViewChild('tpl') tpl !: TemplateRef<any>;
|
||||
@ViewChildren('foo') query !: QueryList<any>;
|
||||
@ViewChild(ViewContainerManipulatorDirective) vc!: ViewContainerManipulatorDirective;
|
||||
@ViewChild('tpl') tpl!: TemplateRef<any>;
|
||||
@ViewChildren('foo') query!: QueryList<any>;
|
||||
|
||||
ngAfterViewInit() {
|
||||
this.query.changes.subscribe(() => this.queryListNotificationCounter++);
|
||||
@ -1125,13 +1120,13 @@ describe('query logic', () => {
|
||||
it('should support a mix of content queries from the declaration and embedded view', () => {
|
||||
@Directive({selector: '[query-for-lots-of-content]'})
|
||||
class QueryForLotsOfContent {
|
||||
@ContentChildren('foo', {descendants: true}) foos1 !: QueryList<ElementRef>;
|
||||
@ContentChildren('foo', {descendants: true}) foos2 !: QueryList<ElementRef>;
|
||||
@ContentChildren('foo', {descendants: true}) foos1!: QueryList<ElementRef>;
|
||||
@ContentChildren('foo', {descendants: true}) foos2!: QueryList<ElementRef>;
|
||||
}
|
||||
|
||||
@Directive({selector: '[query-for-content]'})
|
||||
class QueryForContent {
|
||||
@ContentChildren('foo') foos !: QueryList<ElementRef>;
|
||||
@ContentChildren('foo') foos!: QueryList<ElementRef>;
|
||||
}
|
||||
|
||||
@Component({
|
||||
@ -1192,11 +1187,10 @@ describe('query logic', () => {
|
||||
`,
|
||||
})
|
||||
class TestComponent {
|
||||
@ViewChild(ViewContainerManipulatorDirective)
|
||||
vc !: ViewContainerManipulatorDirective;
|
||||
@ViewChild('tpl1') tpl1 !: TemplateRef<any>;
|
||||
@ViewChild('tpl2') tpl2 !: TemplateRef<any>;
|
||||
@ViewChildren('foo') query !: QueryList<any>;
|
||||
@ViewChild(ViewContainerManipulatorDirective) vc!: ViewContainerManipulatorDirective;
|
||||
@ViewChild('tpl1') tpl1!: TemplateRef<any>;
|
||||
@ViewChild('tpl2') tpl2!: TemplateRef<any>;
|
||||
@ViewChildren('foo') query!: QueryList<any>;
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule(
|
||||
@ -1210,8 +1204,8 @@ describe('query logic', () => {
|
||||
expect(queryList.length).toBe(1);
|
||||
expect(queryList.first.nativeElement.getAttribute('id')).toBe('middle');
|
||||
|
||||
vc.insertTpl(tpl1 !, {idx: 0}, 0);
|
||||
vc.insertTpl(tpl2 !, {idx: 1}, 1);
|
||||
vc.insertTpl(tpl1!, {idx: 0}, 0);
|
||||
vc.insertTpl(tpl2!, {idx: 1}, 1);
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(queryList.length).toBe(3);
|
||||
@ -1220,7 +1214,7 @@ describe('query logic', () => {
|
||||
expect(qListArr[1].nativeElement.getAttribute('id')).toBe('middle');
|
||||
expect(qListArr[2].nativeElement.getAttribute('id')).toBe('foo2_1');
|
||||
|
||||
vc.insertTpl(tpl1 !, {idx: 1}, 1);
|
||||
vc.insertTpl(tpl1!, {idx: 1}, 1);
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(queryList.length).toBe(4);
|
||||
@ -1264,10 +1258,10 @@ describe('query logic', () => {
|
||||
`,
|
||||
})
|
||||
class TestComponent {
|
||||
@ViewChild('tpl') tpl !: TemplateRef<any>;
|
||||
@ViewChild('vi0') vi0 !: ViewContainerManipulatorDirective;
|
||||
@ViewChild('vi1') vi1 !: ViewContainerManipulatorDirective;
|
||||
@ViewChildren('foo') query !: QueryList<any>;
|
||||
@ViewChild('tpl') tpl!: TemplateRef<any>;
|
||||
@ViewChild('vi0') vi0!: ViewContainerManipulatorDirective;
|
||||
@ViewChild('vi1') vi1!: ViewContainerManipulatorDirective;
|
||||
@ViewChildren('foo') query!: QueryList<any>;
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule(
|
||||
@ -1280,8 +1274,8 @@ describe('query logic', () => {
|
||||
|
||||
expect(queryList.length).toBe(0);
|
||||
|
||||
vi0.insertTpl(tpl !, {idx: 0, container_idx: 0}, 0);
|
||||
vi1.insertTpl(tpl !, {idx: 0, container_idx: 1}, 0);
|
||||
vi0.insertTpl(tpl!, {idx: 0, container_idx: 0}, 0);
|
||||
vi1.insertTpl(tpl!, {idx: 0, container_idx: 1}, 0);
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(queryList.length).toBe(2);
|
||||
@ -1315,7 +1309,7 @@ describe('query logic', () => {
|
||||
})
|
||||
class MyApp {
|
||||
show = false;
|
||||
@ViewChildren('foo') query !: QueryList<any>;
|
||||
@ViewChildren('foo') query!: QueryList<any>;
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule({declarations: [MyApp], imports: [CommonModule]});
|
||||
@ -1334,14 +1328,11 @@ describe('query logic', () => {
|
||||
fixture.detectChanges();
|
||||
expect(queryList.length).toBe(0);
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
describe('non-regression', () => {
|
||||
|
||||
it('should query by provider super-type in an embedded view', () => {
|
||||
|
||||
@Directive({selector: '[child]'})
|
||||
class Child {
|
||||
}
|
||||
@ -1356,7 +1347,7 @@ describe('query logic', () => {
|
||||
`<ng-template [ngIf]="true"><ng-template [ngIf]="true"><div parent></div></ng-template></ng-template>`
|
||||
})
|
||||
class TestCmpt {
|
||||
@ViewChildren(Child) instances !: QueryList<Child>;
|
||||
@ViewChildren(Child) instances!: QueryList<Child>;
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule({declarations: [TestCmpt, Parent, Child]});
|
||||
@ -1367,7 +1358,6 @@ describe('query logic', () => {
|
||||
});
|
||||
|
||||
it('should flatten multi-provider results', () => {
|
||||
|
||||
class MyClass {}
|
||||
|
||||
@Component({
|
||||
@ -1381,7 +1371,7 @@ describe('query logic', () => {
|
||||
|
||||
@Component({selector: 'test-cmpt', template: `<with-multi-provider></with-multi-provider>`})
|
||||
class TestCmpt {
|
||||
@ViewChildren(MyClass) queryResults !: QueryList<WithMultiProvider>;
|
||||
@ViewChildren(MyClass) queryResults!: QueryList<WithMultiProvider>;
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule({declarations: [TestCmpt, WithMultiProvider]});
|
||||
@ -1393,7 +1383,6 @@ describe('query logic', () => {
|
||||
});
|
||||
|
||||
it('should flatten multi-provider results when crossing ng-template', () => {
|
||||
|
||||
class MyClass {}
|
||||
|
||||
@Component({
|
||||
@ -1413,7 +1402,7 @@ describe('query logic', () => {
|
||||
`
|
||||
})
|
||||
class TestCmpt {
|
||||
@ViewChildren(MyClass) queryResults !: QueryList<WithMultiProvider>;
|
||||
@ViewChildren(MyClass) queryResults!: QueryList<WithMultiProvider>;
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule({declarations: [TestCmpt, WithMultiProvider]});
|
||||
@ -1444,7 +1433,7 @@ describe('query logic', () => {
|
||||
`
|
||||
})
|
||||
class App {
|
||||
@ViewChild(GroupDir) group !: GroupDir;
|
||||
@ViewChild(GroupDir) group!: GroupDir;
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule(
|
||||
@ -1481,7 +1470,7 @@ describe('query logic', () => {
|
||||
`
|
||||
})
|
||||
class App {
|
||||
@ViewChildren(GroupDir) groups !: QueryList<GroupDir>;
|
||||
@ViewChildren(GroupDir) groups!: QueryList<GroupDir>;
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule(
|
||||
@ -1497,9 +1486,7 @@ describe('query logic', () => {
|
||||
expect(groups[1]).toBeAnInstanceOf(GroupDir);
|
||||
expect(groups[2]).toBeUndefined();
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
function initWithTemplate(compType: Type<any>, template: string) {
|
||||
@ -1511,11 +1498,11 @@ function initWithTemplate(compType: Type<any>, template: string) {
|
||||
|
||||
@Component({selector: 'local-ref-query-component', template: '<ng-content></ng-content>'})
|
||||
class QueryComp {
|
||||
@ViewChild('viewQuery') viewChild !: any;
|
||||
@ContentChild('contentQuery') contentChild !: any;
|
||||
@ViewChild('viewQuery') viewChild!: any;
|
||||
@ContentChild('contentQuery') contentChild!: any;
|
||||
|
||||
@ViewChildren('viewQuery') viewChildren !: QueryList<any>;
|
||||
@ContentChildren('contentQuery') contentChildren !: QueryList<any>;
|
||||
@ViewChildren('viewQuery') viewChildren!: QueryList<any>;
|
||||
@ContentChildren('contentQuery') contentChildren!: QueryList<any>;
|
||||
}
|
||||
|
||||
@Component({selector: 'app-comp', template: ``})
|
||||
@ -1543,12 +1530,14 @@ class TextDirective {
|
||||
`
|
||||
})
|
||||
class StaticViewQueryComp {
|
||||
private _textDir !: TextDirective;
|
||||
private _foo !: ElementRef;
|
||||
private _textDir!: TextDirective;
|
||||
private _foo!: ElementRef;
|
||||
setEvents: string[] = [];
|
||||
|
||||
@ViewChild(TextDirective, {static: true})
|
||||
get textDir(): TextDirective { return this._textDir; }
|
||||
get textDir(): TextDirective {
|
||||
return this._textDir;
|
||||
}
|
||||
|
||||
set textDir(value: TextDirective) {
|
||||
this.setEvents.push('textDir set');
|
||||
@ -1556,7 +1545,9 @@ class StaticViewQueryComp {
|
||||
}
|
||||
|
||||
@ViewChild('foo')
|
||||
get foo(): ElementRef { return this._foo; }
|
||||
get foo(): ElementRef {
|
||||
return this._foo;
|
||||
}
|
||||
|
||||
set foo(value: ElementRef) {
|
||||
this.setEvents.push('foo set');
|
||||
@ -1577,22 +1568,22 @@ class StaticViewQueryComp {
|
||||
`
|
||||
})
|
||||
class SubclassStaticViewQueryComp extends StaticViewQueryComp {
|
||||
@ViewChild('bar', {static: true})
|
||||
bar !: ElementRef;
|
||||
@ViewChild('bar', {static: true}) bar!: ElementRef;
|
||||
|
||||
@ViewChild('baz')
|
||||
baz !: ElementRef;
|
||||
@ViewChild('baz') baz!: ElementRef;
|
||||
}
|
||||
|
||||
|
||||
@Component({selector: 'static-content-query-comp', template: `<ng-content></ng-content>`})
|
||||
class StaticContentQueryComp {
|
||||
private _textDir !: TextDirective;
|
||||
private _foo !: ElementRef;
|
||||
private _textDir!: TextDirective;
|
||||
private _foo!: ElementRef;
|
||||
setEvents: string[] = [];
|
||||
|
||||
@ContentChild(TextDirective, {static: true})
|
||||
get textDir(): TextDirective { return this._textDir; }
|
||||
get textDir(): TextDirective {
|
||||
return this._textDir;
|
||||
}
|
||||
|
||||
set textDir(value: TextDirective) {
|
||||
this.setEvents.push('textDir set');
|
||||
@ -1600,7 +1591,9 @@ class StaticContentQueryComp {
|
||||
}
|
||||
|
||||
@ContentChild('foo')
|
||||
get foo(): ElementRef { return this._foo; }
|
||||
get foo(): ElementRef {
|
||||
return this._foo;
|
||||
}
|
||||
|
||||
set foo(value: ElementRef) {
|
||||
this.setEvents.push('foo set');
|
||||
@ -1610,12 +1603,14 @@ class StaticContentQueryComp {
|
||||
|
||||
@Directive({selector: '[staticContentQueryDir]'})
|
||||
class StaticContentQueryDir {
|
||||
private _textDir !: TextDirective;
|
||||
private _foo !: ElementRef;
|
||||
private _textDir!: TextDirective;
|
||||
private _foo!: ElementRef;
|
||||
setEvents: string[] = [];
|
||||
|
||||
@ContentChild(TextDirective, {static: true})
|
||||
get textDir(): TextDirective { return this._textDir; }
|
||||
get textDir(): TextDirective {
|
||||
return this._textDir;
|
||||
}
|
||||
|
||||
set textDir(value: TextDirective) {
|
||||
this.setEvents.push('textDir set');
|
||||
@ -1623,7 +1618,9 @@ class StaticContentQueryDir {
|
||||
}
|
||||
|
||||
@ContentChild('foo')
|
||||
get foo(): ElementRef { return this._foo; }
|
||||
get foo(): ElementRef {
|
||||
return this._foo;
|
||||
}
|
||||
|
||||
set foo(value: ElementRef) {
|
||||
this.setEvents.push('foo set');
|
||||
@ -1633,11 +1630,9 @@ class StaticContentQueryDir {
|
||||
|
||||
@Component({selector: 'subclass-static-content-query-comp', template: `<ng-content></ng-content>`})
|
||||
class SubclassStaticContentQueryComp extends StaticContentQueryComp {
|
||||
@ContentChild('bar', {static: true})
|
||||
bar !: ElementRef;
|
||||
@ContentChild('bar', {static: true}) bar!: ElementRef;
|
||||
|
||||
@ContentChild('baz')
|
||||
baz !: ElementRef;
|
||||
@ContentChild('baz') baz!: ElementRef;
|
||||
}
|
||||
|
||||
@Component({
|
||||
@ -1647,7 +1642,7 @@ class SubclassStaticContentQueryComp extends StaticContentQueryComp {
|
||||
`
|
||||
})
|
||||
export class QueryCompWithChanges {
|
||||
@ViewChildren('foo') foos !: QueryList<any>;
|
||||
@ViewChildren('foo') foos!: QueryList<any>;
|
||||
|
||||
showing = false;
|
||||
}
|
||||
@ -1658,7 +1653,7 @@ class SuperDirectiveQueryTarget {
|
||||
|
||||
@Directive({selector: '[super-directive]'})
|
||||
class SuperDirective {
|
||||
@ViewChildren(SuperDirectiveQueryTarget) headers !: QueryList<SuperDirectiveQueryTarget>;
|
||||
@ViewChildren(SuperDirectiveQueryTarget) headers!: QueryList<SuperDirectiveQueryTarget>;
|
||||
}
|
||||
|
||||
@Component({
|
||||
|
@ -24,19 +24,29 @@ describe('renderer factory lifecycle', () => {
|
||||
|
||||
@Component({selector: 'some-component', template: `foo`})
|
||||
class SomeComponent implements DoCheck {
|
||||
ngOnInit() { logs.push('some_component create'); }
|
||||
ngDoCheck() { logs.push('some_component update'); }
|
||||
ngOnInit() {
|
||||
logs.push('some_component create');
|
||||
}
|
||||
ngDoCheck() {
|
||||
logs.push('some_component update');
|
||||
}
|
||||
}
|
||||
|
||||
@Component({selector: 'some-component-with-error', template: `With error`})
|
||||
class SomeComponentWhichThrows {
|
||||
ngOnInit() { throw new Error('SomeComponentWhichThrows threw'); }
|
||||
ngOnInit() {
|
||||
throw new Error('SomeComponentWhichThrows threw');
|
||||
}
|
||||
}
|
||||
|
||||
@Component({selector: 'lol', template: `<some-component></some-component>`})
|
||||
class TestComponent implements DoCheck {
|
||||
ngOnInit() { logs.push('test_component create'); }
|
||||
ngDoCheck() { logs.push('test_component update'); }
|
||||
ngOnInit() {
|
||||
logs.push('test_component create');
|
||||
}
|
||||
ngDoCheck() {
|
||||
logs.push('test_component update');
|
||||
}
|
||||
}
|
||||
|
||||
/** Creates a patched renderer factory that pushes entries to the test log */
|
||||
@ -44,7 +54,7 @@ describe('renderer factory lifecycle', () => {
|
||||
let rendererFactory = getRendererFactory2(document);
|
||||
const createRender = rendererFactory.createRenderer;
|
||||
|
||||
rendererFactory.createRenderer = (hostElement: any, type: RendererType2 | null) => {
|
||||
rendererFactory.createRenderer = (hostElement: any, type: RendererType2|null) => {
|
||||
logs.push('create');
|
||||
return createRender.apply(rendererFactory, [hostElement, type]);
|
||||
};
|
||||
@ -168,7 +178,7 @@ describe('animation renderer factory', () => {
|
||||
]);
|
||||
player.finish();
|
||||
|
||||
rendererFactory !.whenRenderingDone !().then(() => {
|
||||
rendererFactory!.whenRenderingDone!().then(() => {
|
||||
expect(eventLogs).toEqual(['void - start', 'void - done', 'on - start', 'on - done']);
|
||||
done();
|
||||
});
|
||||
|
@ -440,11 +440,12 @@ describe('styling', () => {
|
||||
|
||||
@Directive({selector: '[dir-shadows-class-input]'})
|
||||
class DirectiveShadowsClassInput {
|
||||
@Input('class')
|
||||
klass: string|undefined;
|
||||
@Input('class') klass: string|undefined;
|
||||
|
||||
@HostBinding('class')
|
||||
get hostClasses() { return `${this.klass} SUFFIX`; }
|
||||
get hostClasses() {
|
||||
return `${this.klass} SUFFIX`;
|
||||
}
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule({declarations: [Cmp, DirectiveShadowsClassInput]});
|
||||
@ -671,7 +672,7 @@ describe('styling', () => {
|
||||
it('should be able to bind zero', () => {
|
||||
@Component({template: '<div #div [style.opacity]="opacity"></div>'})
|
||||
class App {
|
||||
@ViewChild('div') div !: ElementRef<HTMLElement>;
|
||||
@ViewChild('div') div!: ElementRef<HTMLElement>;
|
||||
opacity = 0;
|
||||
}
|
||||
|
||||
@ -685,7 +686,7 @@ describe('styling', () => {
|
||||
it('should be able to bind a SafeValue to backgroundImage', () => {
|
||||
@Component({template: '<div [style.backgroundImage]="image"></div>'})
|
||||
class Cmp {
|
||||
image !: SafeStyle;
|
||||
image!: SafeStyle;
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule({declarations: [Cmp]});
|
||||
@ -724,7 +725,7 @@ describe('styling', () => {
|
||||
it('should be able to bind a SafeValue to clip-path', () => {
|
||||
@Component({template: '<div [style.clip-path]="path"></div>'})
|
||||
class Cmp {
|
||||
path !: SafeStyle;
|
||||
path!: SafeStyle;
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule({declarations: [Cmp]});
|
||||
@ -1011,15 +1012,15 @@ describe('styling', () => {
|
||||
expect(capturedClassBindingCount).toEqual(1);
|
||||
expect(capturedClassBindingValue as any).toEqual(null);
|
||||
expect(capturedMyClassBindingCount).toEqual(1);
|
||||
expect(capturedMyClassBindingValue !).toEqual('foo');
|
||||
expect(capturedMyClassBindingValue!).toEqual('foo');
|
||||
|
||||
fixture.componentInstance.c = 'dynamic-value';
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(capturedClassBindingCount).toEqual(2);
|
||||
expect(capturedClassBindingValue !).toEqual('dynamic-value');
|
||||
expect(capturedClassBindingValue!).toEqual('dynamic-value');
|
||||
expect(capturedMyClassBindingCount).toEqual(1);
|
||||
expect(capturedMyClassBindingValue !).toEqual('foo');
|
||||
expect(capturedMyClassBindingValue!).toEqual('foo');
|
||||
|
||||
fixture.componentInstance.c = null;
|
||||
fixture.detectChanges();
|
||||
@ -1027,7 +1028,7 @@ describe('styling', () => {
|
||||
expect(capturedClassBindingCount).toEqual(3);
|
||||
expect(capturedClassBindingValue as any).toEqual(null);
|
||||
expect(capturedMyClassBindingCount).toEqual(1);
|
||||
expect(capturedMyClassBindingValue !).toEqual('foo');
|
||||
expect(capturedMyClassBindingValue!).toEqual('foo');
|
||||
|
||||
fixture.componentInstance.c = '';
|
||||
fixture.detectChanges();
|
||||
@ -1035,7 +1036,7 @@ describe('styling', () => {
|
||||
expect(capturedClassBindingCount).toEqual(4);
|
||||
expect(capturedClassBindingValue as any).toEqual('');
|
||||
expect(capturedMyClassBindingCount).toEqual(1);
|
||||
expect(capturedMyClassBindingValue !).toEqual('foo');
|
||||
expect(capturedMyClassBindingValue!).toEqual('foo');
|
||||
});
|
||||
|
||||
it('should write to [class] binding during `update` mode if there is an instantiation-level value',
|
||||
@ -1069,7 +1070,7 @@ describe('styling', () => {
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(capturedClassBindingCount).toEqual(2);
|
||||
expect(capturedClassBindingValue !).toEqual('dynamic-bar');
|
||||
expect(capturedClassBindingValue!).toEqual('dynamic-bar');
|
||||
});
|
||||
|
||||
it('should write to a `class` input binding if there is a static class value', () => {
|
||||
@ -1102,9 +1103,9 @@ describe('styling', () => {
|
||||
const fixture = TestBed.createComponent(Cmp);
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(capturedClassBindingValue !).toEqual('static-val');
|
||||
expect(capturedClassBindingValue!).toEqual('static-val');
|
||||
expect(capturedClassBindingCount).toEqual(1);
|
||||
expect(capturedMyClassBindingValue !).toEqual('foo');
|
||||
expect(capturedMyClassBindingValue!).toEqual('foo');
|
||||
expect(capturedMyClassBindingCount).toEqual(1);
|
||||
});
|
||||
|
||||
@ -1195,19 +1196,19 @@ describe('styling', () => {
|
||||
// would check if we possibly have this situation on every `advance()`
|
||||
// instruction. We don't think this is worth it, and we are just going to live
|
||||
// with this.
|
||||
);
|
||||
expect(capturedClassBindingValue !).toEqual('static-val');
|
||||
);
|
||||
expect(capturedClassBindingValue!).toEqual('static-val');
|
||||
expect(capturedMyClassBindingCount).toEqual(1);
|
||||
expect(capturedMyClassBindingValue !).toEqual('foo');
|
||||
expect(capturedMyClassBindingValue!).toEqual('foo');
|
||||
|
||||
capturedClassBindingCount = 0;
|
||||
fixture.componentInstance.c = 'dynamic-val';
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(capturedClassBindingCount).toEqual(1);
|
||||
expect(capturedClassBindingValue !).toEqual('static-val dynamic-val');
|
||||
expect(capturedClassBindingValue!).toEqual('static-val dynamic-val');
|
||||
expect(capturedMyClassBindingCount).toEqual(1);
|
||||
expect(capturedMyClassBindingValue !).toEqual('foo');
|
||||
expect(capturedMyClassBindingValue!).toEqual('foo');
|
||||
});
|
||||
|
||||
onlyInIvy('only ivy balances styling across directives and component host bindings')
|
||||
@ -1240,7 +1241,6 @@ describe('styling', () => {
|
||||
});
|
||||
|
||||
describe('NgClass', () => {
|
||||
|
||||
// We had a bug where NgClass would not allocate sufficient slots for host bindings,
|
||||
// so it would overwrite information about other directives nearby. This test checks
|
||||
// that TestDir's injector is not overwritten by NgClass, so TestDir should still
|
||||
@ -1297,7 +1297,7 @@ describe('styling', () => {
|
||||
template: '<span dir [classesInSchool]="classes" [styleOfClothing]="style"></span>',
|
||||
})
|
||||
class App {
|
||||
@ViewChild(Dir) dir !: Dir;
|
||||
@ViewChild(Dir) dir!: Dir;
|
||||
|
||||
classes = 'math';
|
||||
style = '80s';
|
||||
@ -1315,8 +1315,7 @@ describe('styling', () => {
|
||||
it('should be able to bind to `className`', () => {
|
||||
@Component({template: ''})
|
||||
class App {
|
||||
@HostBinding('className')
|
||||
klass = 'one two';
|
||||
@HostBinding('className') klass = 'one two';
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule({declarations: [App]});
|
||||
@ -1471,8 +1470,7 @@ describe('styling', () => {
|
||||
opacity: string|null = '0.5';
|
||||
@ViewChild(CompWithStyling, {static: true})
|
||||
compWithStyling: CompWithStyling|null = null;
|
||||
@ViewChild(DirWithStyling, {static: true})
|
||||
dirWithStyling: DirWithStyling|null = null;
|
||||
@ViewChild(DirWithStyling, {static: true}) dirWithStyling: DirWithStyling|null = null;
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule({declarations: [Cmp, DirWithStyling, CompWithStyling]});
|
||||
@ -1488,13 +1486,13 @@ describe('styling', () => {
|
||||
expect(element.style.fontSize).toEqual('100px');
|
||||
|
||||
// once for the template flush and again for the host bindings
|
||||
expect(ngDevMode !.rendererSetStyle).toEqual(4);
|
||||
expect(ngDevMode!.rendererSetStyle).toEqual(4);
|
||||
ngDevModeResetPerfCounters();
|
||||
|
||||
component.opacity = '0.6';
|
||||
component.compWithStyling !.height = '100px';
|
||||
component.compWithStyling !.width = '100px';
|
||||
component.dirWithStyling !.fontSize = '50px';
|
||||
component.compWithStyling!.height = '100px';
|
||||
component.compWithStyling!.width = '100px';
|
||||
component.dirWithStyling!.fontSize = '50px';
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(element.style.opacity).toEqual('0.6');
|
||||
@ -1503,7 +1501,7 @@ describe('styling', () => {
|
||||
expect(element.style.fontSize).toEqual('50px');
|
||||
|
||||
// once for the template flush and again for the host bindings
|
||||
expect(ngDevMode !.rendererSetStyle).toEqual(4);
|
||||
expect(ngDevMode!.rendererSetStyle).toEqual(4);
|
||||
});
|
||||
|
||||
onlyInIvy('ivy resolves styling across directives, components and templates in unison')
|
||||
@ -1634,8 +1632,12 @@ describe('styling', () => {
|
||||
public c: {[key: string]: any}|null = null;
|
||||
updateClasses(classes: string) {
|
||||
const c = this.c || (this.c = {});
|
||||
Object.keys(this.c).forEach(className => { c[className] = false; });
|
||||
classes.split(/\s+/).forEach(className => { c[className] = true; });
|
||||
Object.keys(this.c).forEach(className => {
|
||||
c[className] = false;
|
||||
});
|
||||
classes.split(/\s+/).forEach(className => {
|
||||
c[className] = true;
|
||||
});
|
||||
}
|
||||
|
||||
public s: {[key: string]: any}|null = null;
|
||||
@ -1694,8 +1696,7 @@ describe('styling', () => {
|
||||
map: any = {width: '111px', opacity: '0.5'};
|
||||
width: string|null|undefined = '555px';
|
||||
|
||||
@ViewChild('dir', {read: DirThatSetsStyling, static: true})
|
||||
dir !: DirThatSetsStyling;
|
||||
@ViewChild('dir', {read: DirThatSetsStyling, static: true}) dir!: DirThatSetsStyling;
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule({declarations: [Cmp, DirThatSetsStyling]});
|
||||
@ -1763,8 +1764,7 @@ describe('styling', () => {
|
||||
|
||||
map: any = {width: '555px', height: '555px'};
|
||||
|
||||
@ViewChild('dir', {read: DirThatSetsStyling, static: true})
|
||||
dir !: DirThatSetsStyling;
|
||||
@ViewChild('dir', {read: DirThatSetsStyling, static: true}) dir!: DirThatSetsStyling;
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule({declarations: [Cmp, DirThatSetsStyling]});
|
||||
@ -1991,7 +1991,7 @@ describe('styling', () => {
|
||||
it('should be able to bind a SafeValue to clip-path', () => {
|
||||
@Component({template: '<div [style.clip-path]="path"></div>'})
|
||||
class Cmp {
|
||||
path !: SafeStyle;
|
||||
path!: SafeStyle;
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule({declarations: [Cmp]});
|
||||
@ -2028,12 +2028,22 @@ describe('styling', () => {
|
||||
public background: string = '1.png';
|
||||
public color: string = 'red';
|
||||
|
||||
private getSafeStyle(value: string) { return this.sanitizer.bypassSecurityTrustStyle(value); }
|
||||
private getSafeStyle(value: string) {
|
||||
return this.sanitizer.bypassSecurityTrustStyle(value);
|
||||
}
|
||||
|
||||
getBackgroundSafe() { return this.getSafeStyle(`url("/${this.background}")`); }
|
||||
getWidthSafe() { return this.getSafeStyle(this.width); }
|
||||
getHeightSafe() { return this.getSafeStyle(this.height); }
|
||||
getColorUnsafe() { return this.color; }
|
||||
getBackgroundSafe() {
|
||||
return this.getSafeStyle(`url("/${this.background}")`);
|
||||
}
|
||||
getWidthSafe() {
|
||||
return this.getSafeStyle(this.width);
|
||||
}
|
||||
getHeightSafe() {
|
||||
return this.getSafeStyle(this.height);
|
||||
}
|
||||
getColorUnsafe() {
|
||||
return this.color;
|
||||
}
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule({
|
||||
@ -2089,11 +2099,9 @@ describe('styling', () => {
|
||||
class Cmp {
|
||||
map: any = null;
|
||||
|
||||
@ViewChild('div', {read: DirWithStyleMap, static: true})
|
||||
dir1 !: DirWithStyleMap;
|
||||
@ViewChild('div', {read: DirWithStyleMap, static: true}) dir1!: DirWithStyleMap;
|
||||
|
||||
@ViewChild('div', {read: DirWithStyleMapPart2, static: true})
|
||||
dir2 !: DirWithStyleMapPart2;
|
||||
@ViewChild('div', {read: DirWithStyleMapPart2, static: true}) dir2!: DirWithStyleMapPart2;
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule(
|
||||
@ -2352,8 +2360,8 @@ describe('styling', () => {
|
||||
|
||||
@Component({template: '<div #target one two></div>'})
|
||||
class Cmp {
|
||||
@ViewChild('target', {read: DirOne, static: true}) one !: DirOne;
|
||||
@ViewChild('target', {read: DirTwo, static: true}) two !: DirTwo;
|
||||
@ViewChild('target', {read: DirOne, static: true}) one!: DirOne;
|
||||
@ViewChild('target', {read: DirTwo, static: true}) two!: DirTwo;
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule({declarations: [Cmp, DirOne, DirTwo]});
|
||||
@ -2443,7 +2451,9 @@ describe('styling', () => {
|
||||
class Cmp {
|
||||
s: any = {opacity: '1'};
|
||||
|
||||
clearStyle(): void { this.s = null; }
|
||||
clearStyle(): void {
|
||||
this.s = null;
|
||||
}
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule({declarations: [Cmp]});
|
||||
@ -2471,8 +2481,7 @@ describe('styling', () => {
|
||||
class ChildCmp {
|
||||
readyTpl = false;
|
||||
|
||||
@HostBinding('class.ready-host')
|
||||
readyHost = false;
|
||||
@HostBinding('class.ready-host') readyHost = false;
|
||||
}
|
||||
|
||||
@Component({
|
||||
@ -2490,10 +2499,9 @@ describe('styling', () => {
|
||||
class ParentCmp {
|
||||
private _prop = '';
|
||||
|
||||
@ViewChild('template', {read: ViewContainerRef})
|
||||
vcr: ViewContainerRef = null !;
|
||||
@ViewChild('template', {read: ViewContainerRef}) vcr: ViewContainerRef = null!;
|
||||
|
||||
private child: ComponentRef<ChildCmp> = null !;
|
||||
private child: ComponentRef<ChildCmp> = null!;
|
||||
|
||||
@Input()
|
||||
set prop(value: string) {
|
||||
@ -2505,7 +2513,9 @@ describe('styling', () => {
|
||||
}
|
||||
}
|
||||
|
||||
get prop() { return this._prop; }
|
||||
get prop() {
|
||||
return this._prop;
|
||||
}
|
||||
|
||||
ngAfterViewInit() {
|
||||
const factory = this.componentFactoryResolver.resolveComponentFactory(ChildCmp);
|
||||
@ -2559,8 +2569,7 @@ describe('styling', () => {
|
||||
class ChildCmp {
|
||||
readyTpl = false;
|
||||
|
||||
@HostBinding('class.ready-host')
|
||||
readyHost = false;
|
||||
@HostBinding('class.ready-host') readyHost = false;
|
||||
}
|
||||
|
||||
@Component({
|
||||
@ -2575,10 +2584,9 @@ describe('styling', () => {
|
||||
class ParentCmp {
|
||||
updateChild = false;
|
||||
|
||||
@ViewChild('template', {read: ViewContainerRef})
|
||||
vcr: ViewContainerRef = null !;
|
||||
@ViewChild('template', {read: ViewContainerRef}) vcr: ViewContainerRef = null!;
|
||||
|
||||
private child: ComponentRef<ChildCmp> = null !;
|
||||
private child: ComponentRef<ChildCmp> = null!;
|
||||
|
||||
ngDoCheck() {
|
||||
if (this.updateChild) {
|
||||
@ -2619,7 +2627,7 @@ describe('styling', () => {
|
||||
expect(readyHost).toBeFalsy();
|
||||
expect(readyChild).toBeFalsy();
|
||||
|
||||
const parent = fixture.componentInstance.parent !;
|
||||
const parent = fixture.componentInstance.parent!;
|
||||
parent.updateChild = true;
|
||||
fixture.detectChanges(false);
|
||||
|
||||
@ -2675,7 +2683,9 @@ describe('styling', () => {
|
||||
selector: '[dir]',
|
||||
})
|
||||
class Dir {
|
||||
constructor(public elementRef: ElementRef, public renderer: Renderer2) { dirInstance = this; }
|
||||
constructor(public elementRef: ElementRef, public renderer: Renderer2) {
|
||||
dirInstance = this;
|
||||
}
|
||||
|
||||
setStyles() {
|
||||
const nativeEl = this.elementRef.nativeElement;
|
||||
@ -2730,11 +2740,9 @@ describe('styling', () => {
|
||||
() => {
|
||||
@Directive({selector: '[blockStyles]'})
|
||||
class StylesDirective {
|
||||
@HostBinding('style.border')
|
||||
border = '1px solid red';
|
||||
@HostBinding('style.border') border = '1px solid red';
|
||||
|
||||
@HostBinding('style.background')
|
||||
background = 'white';
|
||||
@HostBinding('style.background') background = 'white';
|
||||
}
|
||||
|
||||
@Component({
|
||||
@ -2863,7 +2871,7 @@ describe('styling', () => {
|
||||
TestBed.configureTestingModule({declarations: [MyComp]}).createComponent(MyComp);
|
||||
fixture.detectChanges();
|
||||
|
||||
const div = fixture.nativeElement.querySelector('div') !;
|
||||
const div = fixture.nativeElement.querySelector('div')!;
|
||||
div.className += ' abc';
|
||||
expect(splitSortJoin(div.className)).toEqual('abc');
|
||||
|
||||
@ -2877,7 +2885,9 @@ describe('styling', () => {
|
||||
|
||||
expect(splitSortJoin(div.className)).toEqual('4 5 6 7 abc');
|
||||
|
||||
function splitSortJoin(s: string) { return s.split(/\s+/).sort().join(' ').trim(); }
|
||||
function splitSortJoin(s: string) {
|
||||
return s.split(/\s+/).sort().join(' ').trim();
|
||||
}
|
||||
});
|
||||
|
||||
describe('ExpressionChangedAfterItHasBeenCheckedError', () => {
|
||||
@ -2885,7 +2895,9 @@ describe('styling', () => {
|
||||
@Component({template: `<div [style.background-image]="iconSafe"></div>`})
|
||||
class MyComp {
|
||||
icon = 'https://i.imgur.com/4AiXzf8.jpg';
|
||||
get iconSafe() { return this.sanitizer.bypassSecurityTrustStyle(`url("${this.icon}")`); }
|
||||
get iconSafe() {
|
||||
return this.sanitizer.bypassSecurityTrustStyle(`url("${this.icon}")`);
|
||||
}
|
||||
|
||||
constructor(private sanitizer: DomSanitizer) {}
|
||||
}
|
||||
@ -2951,7 +2963,7 @@ describe('styling', () => {
|
||||
TestBed.configureTestingModule({declarations: [MyComp, DirThatSetsStyling]})
|
||||
.createComponent(MyComp);
|
||||
fixture.detectChanges();
|
||||
const div = fixture.nativeElement.querySelector('div') !;
|
||||
const div = fixture.nativeElement.querySelector('div')!;
|
||||
expect(div.style.getPropertyValue('width')).toEqual('100px');
|
||||
expect(div.style.getPropertyValue('height')).toEqual('200px');
|
||||
expect(div.style.getPropertyValue('font-size')).toEqual('300px');
|
||||
@ -3051,20 +3063,16 @@ describe('styling', () => {
|
||||
() => {
|
||||
@Component({selector: 'my-comp-with-styling', template: ''})
|
||||
class MyCompWithStyling {
|
||||
@HostBinding('style')
|
||||
myStyles: any = {width: '300px'};
|
||||
@HostBinding('style') myStyles: any = {width: '300px'};
|
||||
|
||||
@HostBinding('style.height')
|
||||
myHeight: any = '305px';
|
||||
@HostBinding('style.height') myHeight: any = '305px';
|
||||
}
|
||||
|
||||
@Directive({selector: '[my-dir-with-styling]'})
|
||||
class MyDirWithStyling {
|
||||
@HostBinding('style')
|
||||
myStyles: any = {width: '200px'};
|
||||
@HostBinding('style') myStyles: any = {width: '200px'};
|
||||
|
||||
@HostBinding('style.height')
|
||||
myHeight: any = '205px';
|
||||
@HostBinding('style.height') myHeight: any = '205px';
|
||||
}
|
||||
|
||||
@Component({
|
||||
@ -3081,15 +3089,15 @@ describe('styling', () => {
|
||||
myStyles: {width?: string} = {width: '100px'};
|
||||
myHeight: string|null|undefined = '100px';
|
||||
|
||||
@ViewChild(MyDirWithStyling) dir !: MyDirWithStyling;
|
||||
@ViewChild(MyCompWithStyling) comp !: MyCompWithStyling;
|
||||
@ViewChild(MyDirWithStyling) dir!: MyDirWithStyling;
|
||||
@ViewChild(MyCompWithStyling) comp!: MyCompWithStyling;
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule(
|
||||
{declarations: [MyComp, MyCompWithStyling, MyDirWithStyling]});
|
||||
const fixture = TestBed.createComponent(MyComp);
|
||||
const comp = fixture.componentInstance;
|
||||
const elm = fixture.nativeElement.querySelector('my-comp-with-styling') !;
|
||||
const elm = fixture.nativeElement.querySelector('my-comp-with-styling')!;
|
||||
|
||||
fixture.detectChanges();
|
||||
expect(elm.style.width).toEqual('100px');
|
||||
@ -3131,7 +3139,7 @@ describe('styling', () => {
|
||||
TestBed.configureTestingModule(
|
||||
{declarations: [MyComp, MyCompWithStyling, MyDirWithStyling]});
|
||||
const fixture = TestBed.createComponent(MyComp);
|
||||
const elm = fixture.nativeElement.querySelector('my-comp-with-styling') !;
|
||||
const elm = fixture.nativeElement.querySelector('my-comp-with-styling')!;
|
||||
|
||||
fixture.detectChanges();
|
||||
expect(elm.style.color).toEqual('red');
|
||||
@ -3139,7 +3147,6 @@ describe('styling', () => {
|
||||
|
||||
|
||||
it('should combine host class.foo bindings from multiple directives', () => {
|
||||
|
||||
@Directive({
|
||||
selector: '[dir-that-sets-one-two]',
|
||||
exportAs: 'one',
|
||||
@ -3197,8 +3204,8 @@ describe('styling', () => {
|
||||
expect(div2.className).toBe('');
|
||||
|
||||
const comp = fixture.componentInstance;
|
||||
comp.dirOneA !.one = comp.dirOneB !.one = true;
|
||||
comp.dirOneA !.two = comp.dirOneB !.two = true;
|
||||
comp.dirOneA!.one = comp.dirOneB!.one = true;
|
||||
comp.dirOneA!.two = comp.dirOneB!.two = true;
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(div1.classList.contains('one')).toBeTruthy();
|
||||
@ -3211,8 +3218,8 @@ describe('styling', () => {
|
||||
expect(div2.classList.contains('four')).toBeFalsy();
|
||||
expect(div2.classList.contains('zero')).toBeFalsy();
|
||||
|
||||
comp.dirTwoA !.three = comp.dirTwoB !.three = true;
|
||||
comp.dirTwoA !.four = comp.dirTwoB !.four = true;
|
||||
comp.dirTwoA!.three = comp.dirTwoB!.three = true;
|
||||
comp.dirTwoA!.four = comp.dirTwoB!.four = true;
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(div1.classList.contains('one')).toBeTruthy();
|
||||
@ -3242,7 +3249,9 @@ describe('styling', () => {
|
||||
it('should combine static host classes with component "class" host attribute', () => {
|
||||
@Component({selector: 'comp-with-classes', template: '', host: {'class': 'host'}})
|
||||
class CompWithClasses {
|
||||
constructor(ref: ElementRef) { ref.nativeElement.classList.add('custom'); }
|
||||
constructor(ref: ElementRef) {
|
||||
ref.nativeElement.classList.add('custom');
|
||||
}
|
||||
}
|
||||
|
||||
@Component({
|
||||
@ -3282,8 +3291,7 @@ describe('styling', () => {
|
||||
|
||||
@Directive({selector: '[single-host-style-dir]'})
|
||||
class SingleHostStyleDir {
|
||||
@HostBinding('style.width')
|
||||
width = '100px';
|
||||
@HostBinding('style.width') width = '100px';
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule({declarations: [Cmp, SingleHostStyleDir]});
|
||||
@ -3340,7 +3348,9 @@ describe('styling', () => {
|
||||
@Directive({selector: '[test]'})
|
||||
class MyDir {
|
||||
@Input('class')
|
||||
set className(value: string) { logs.push(value); }
|
||||
set className(value: string) {
|
||||
logs.push(value);
|
||||
}
|
||||
}
|
||||
|
||||
@Component({
|
||||
@ -3456,8 +3466,7 @@ describe('styling', () => {
|
||||
template: `className = {{className}}`,
|
||||
})
|
||||
class MyCmp {
|
||||
@Input()
|
||||
className: string = 'unbound';
|
||||
@Input() className: string = 'unbound';
|
||||
}
|
||||
@Component({template: `<my-cmp [class]="'bound'"></my-cmp>`})
|
||||
class MyApp {
|
||||
@ -3475,8 +3484,7 @@ describe('styling', () => {
|
||||
template: `className = {{className}}`,
|
||||
})
|
||||
class MyCmp {
|
||||
@Input()
|
||||
className: string = 'unbound';
|
||||
@Input() className: string = 'unbound';
|
||||
}
|
||||
@Component({template: `<my-cmp class="bound"></my-cmp>`})
|
||||
class MyApp {
|
||||
@ -3491,8 +3499,8 @@ describe('styling', () => {
|
||||
});
|
||||
|
||||
function assertStyleCounters(countForSet: number, countForRemove: number) {
|
||||
expect(ngDevMode !.rendererSetStyle).toEqual(countForSet);
|
||||
expect(ngDevMode !.rendererRemoveStyle).toEqual(countForRemove);
|
||||
expect(ngDevMode!.rendererSetStyle).toEqual(countForSet);
|
||||
expect(ngDevMode!.rendererRemoveStyle).toEqual(countForRemove);
|
||||
}
|
||||
|
||||
function assertStyle(element: HTMLElement, prop: string, value: any) {
|
||||
|
@ -13,11 +13,9 @@ import {ivyEnabled, onlyInIvy} from '@angular/private/testing';
|
||||
|
||||
describe('TemplateRef', () => {
|
||||
describe('rootNodes', () => {
|
||||
|
||||
@Component({template: `<ng-template #templateRef></ng-template>`})
|
||||
class App {
|
||||
@ViewChild('templateRef', {static: true})
|
||||
templateRef !: TemplateRef<any>;
|
||||
@ViewChild('templateRef', {static: true}) templateRef!: TemplateRef<any>;
|
||||
minutes = 0;
|
||||
}
|
||||
|
||||
@ -80,7 +78,7 @@ describe('TemplateRef', () => {
|
||||
`
|
||||
})
|
||||
class App {
|
||||
@ViewChild(MenuContent) content !: MenuContent;
|
||||
@ViewChild(MenuContent) content!: MenuContent;
|
||||
|
||||
constructor(public viewContainerRef: ViewContainerRef) {}
|
||||
}
|
||||
|
@ -7,7 +7,7 @@
|
||||
*/
|
||||
import {Component} from '@angular/core';
|
||||
import {TestBed} from '@angular/core/testing';
|
||||
import {of } from 'rxjs';
|
||||
import {of} from 'rxjs';
|
||||
|
||||
describe('text instructions', () => {
|
||||
it('should handle all flavors of interpolated text', () => {
|
||||
@ -66,8 +66,8 @@ describe('text instructions', () => {
|
||||
`
|
||||
})
|
||||
class App {
|
||||
who = of ('Sally');
|
||||
item = of ({
|
||||
who = of('Sally');
|
||||
item = of({
|
||||
what: 'seashells',
|
||||
where: 'seashore',
|
||||
});
|
||||
|
@ -8,7 +8,7 @@
|
||||
|
||||
import {CommonModule, DOCUMENT} from '@angular/common';
|
||||
import {computeMsgId} from '@angular/compiler';
|
||||
import {Compiler, Component, ComponentFactoryResolver, Directive, DoCheck, ElementRef, EmbeddedViewRef, ErrorHandler, Injector, NO_ERRORS_SCHEMA, NgModule, OnInit, Pipe, PipeTransform, QueryList, RendererFactory2, RendererType2, Sanitizer, TemplateRef, ViewChild, ViewChildren, ViewContainerRef, ɵsetDocument} from '@angular/core';
|
||||
import {Compiler, Component, ComponentFactoryResolver, Directive, DoCheck, ElementRef, EmbeddedViewRef, ErrorHandler, Injector, NgModule, NO_ERRORS_SCHEMA, OnInit, Pipe, PipeTransform, QueryList, RendererFactory2, RendererType2, Sanitizer, TemplateRef, ViewChild, ViewChildren, ViewContainerRef, ɵsetDocument} from '@angular/core';
|
||||
import {Input} from '@angular/core/src/metadata';
|
||||
import {ngDevModeResetPerfCounters} from '@angular/core/src/util/ng_dev_mode';
|
||||
import {TestBed, TestComponentRenderer} from '@angular/core/testing';
|
||||
@ -18,7 +18,6 @@ import {expect} from '@angular/platform-browser/testing/src/matchers';
|
||||
import {ivyEnabled, onlyInIvy} from '@angular/private/testing';
|
||||
|
||||
describe('ViewContainerRef', () => {
|
||||
|
||||
/**
|
||||
* Gets the inner HTML of the given element with all HTML comments and Angular internal
|
||||
* reflect attributes omitted. This makes HTML comparisons easier and less verbose.
|
||||
@ -40,7 +39,6 @@ describe('ViewContainerRef', () => {
|
||||
afterEach(() => clearTranslations());
|
||||
|
||||
describe('create', () => {
|
||||
|
||||
it('should support view queries inside embedded views created in dir constructors', () => {
|
||||
const fixture = TestBed.createComponent(ConstructorApp);
|
||||
fixture.detectChanges();
|
||||
@ -87,7 +85,7 @@ describe('ViewContainerRef', () => {
|
||||
`
|
||||
})
|
||||
class TestComp {
|
||||
@ViewChild(VCRefDirective, {static: true}) vcRefDir !: VCRefDirective;
|
||||
@ViewChild(VCRefDirective, {static: true}) vcRefDir!: VCRefDirective;
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule(
|
||||
@ -141,7 +139,7 @@ describe('ViewContainerRef', () => {
|
||||
`
|
||||
})
|
||||
class TestComp {
|
||||
@ViewChild('container', {read: ViewContainerRef}) vcRef !: ViewContainerRef;
|
||||
@ViewChild('container', {read: ViewContainerRef}) vcRef!: ViewContainerRef;
|
||||
|
||||
constructor(public cfr: ComponentFactoryResolver) {}
|
||||
|
||||
@ -196,8 +194,8 @@ describe('ViewContainerRef', () => {
|
||||
`
|
||||
})
|
||||
class TestComp {
|
||||
@ViewChild('svg', {read: ViewContainerRef}) svgVCRef !: ViewContainerRef;
|
||||
@ViewChild('mathml', {read: ViewContainerRef}) mathMLVCRef !: ViewContainerRef;
|
||||
@ViewChild('svg', {read: ViewContainerRef}) svgVCRef!: ViewContainerRef;
|
||||
@ViewChild('mathml', {read: ViewContainerRef}) mathMLVCRef!: ViewContainerRef;
|
||||
|
||||
constructor(public cfr: ComponentFactoryResolver) {}
|
||||
|
||||
@ -273,7 +271,7 @@ describe('ViewContainerRef', () => {
|
||||
`
|
||||
})
|
||||
class TestComp {
|
||||
@ViewChild('container', {read: ViewContainerRef}) vcRef !: ViewContainerRef;
|
||||
@ViewChild('container', {read: ViewContainerRef}) vcRef!: ViewContainerRef;
|
||||
|
||||
private helloCompFactory = this.cfr.resolveComponentFactory(HelloComp);
|
||||
|
||||
@ -357,7 +355,9 @@ describe('ViewContainerRef', () => {
|
||||
// Insert the view again at the same index
|
||||
viewContainerRef.insert(ref0, 0);
|
||||
|
||||
expect(() => { fixture.destroy(); }).not.toThrow();
|
||||
expect(() => {
|
||||
fixture.destroy();
|
||||
}).not.toThrow();
|
||||
|
||||
expect(fixture.nativeElement.textContent).toEqual('0');
|
||||
});
|
||||
@ -405,7 +405,6 @@ describe('ViewContainerRef', () => {
|
||||
});
|
||||
|
||||
it('should insert a view already inserted into another container', () => {
|
||||
|
||||
@Component({
|
||||
selector: 'test-cmpt',
|
||||
template: `
|
||||
@ -414,9 +413,9 @@ describe('ViewContainerRef', () => {
|
||||
`
|
||||
})
|
||||
class TestComponent {
|
||||
@ViewChild('t', {static: true}) t !: TemplateRef<{}>;
|
||||
@ViewChild('c1', {static: true, read: ViewContainerRef}) c1 !: ViewContainerRef;
|
||||
@ViewChild('c2', {static: true, read: ViewContainerRef}) c2 !: ViewContainerRef;
|
||||
@ViewChild('t', {static: true}) t!: TemplateRef<{}>;
|
||||
@ViewChild('c1', {static: true, read: ViewContainerRef}) c1!: ViewContainerRef;
|
||||
@ViewChild('c2', {static: true, read: ViewContainerRef}) c2!: ViewContainerRef;
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule({declarations: [TestComponent]});
|
||||
@ -660,7 +659,6 @@ describe('ViewContainerRef', () => {
|
||||
});
|
||||
|
||||
describe('get and indexOf', () => {
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({declarations: [EmbeddedViewInsertionComp, VCRefDirective]});
|
||||
});
|
||||
@ -677,13 +675,13 @@ describe('ViewContainerRef', () => {
|
||||
fixture.detectChanges();
|
||||
|
||||
let viewRef = vcRefDir.vcref.get(0);
|
||||
expect(vcRefDir.vcref.indexOf(viewRef !)).toEqual(0);
|
||||
expect(vcRefDir.vcref.indexOf(viewRef!)).toEqual(0);
|
||||
|
||||
viewRef = vcRefDir.vcref.get(1);
|
||||
expect(vcRefDir.vcref.indexOf(viewRef !)).toEqual(1);
|
||||
expect(vcRefDir.vcref.indexOf(viewRef!)).toEqual(1);
|
||||
|
||||
viewRef = vcRefDir.vcref.get(2);
|
||||
expect(vcRefDir.vcref.indexOf(viewRef !)).toEqual(2);
|
||||
expect(vcRefDir.vcref.indexOf(viewRef!)).toEqual(2);
|
||||
});
|
||||
|
||||
it('should handle out of bounds cases', () => {
|
||||
@ -700,7 +698,7 @@ describe('ViewContainerRef', () => {
|
||||
|
||||
const viewRef = vcRefDir.vcref.get(0);
|
||||
vcRefDir.vcref.remove(0);
|
||||
expect(vcRefDir.vcref.indexOf(viewRef !)).toEqual(-1);
|
||||
expect(vcRefDir.vcref.indexOf(viewRef!)).toEqual(-1);
|
||||
});
|
||||
|
||||
it('should return -1 as indexOf when no views were inserted', () => {
|
||||
@ -742,21 +740,21 @@ describe('ViewContainerRef', () => {
|
||||
expect(getElementHtml(fixture.nativeElement)).toEqual('<p vcref=""></p>**A**BC');
|
||||
|
||||
let viewRef = vcRefDir.vcref.get(0);
|
||||
vcRefDir.vcref.move(viewRef !, 2);
|
||||
vcRefDir.vcref.move(viewRef!, 2);
|
||||
fixture.detectChanges();
|
||||
expect(getElementHtml(fixture.nativeElement)).toEqual('<p vcref=""></p>BC**A**');
|
||||
|
||||
vcRefDir.vcref.move(viewRef !, 0);
|
||||
vcRefDir.vcref.move(viewRef!, 0);
|
||||
fixture.detectChanges();
|
||||
expect(getElementHtml(fixture.nativeElement)).toEqual('<p vcref=""></p>**A**BC');
|
||||
|
||||
vcRefDir.vcref.move(viewRef !, 1);
|
||||
vcRefDir.vcref.move(viewRef!, 1);
|
||||
fixture.detectChanges();
|
||||
expect(getElementHtml(fixture.nativeElement)).toEqual('<p vcref=""></p>B**A**C');
|
||||
|
||||
// Invalid indices when detaching throws an exception in Ivy: FW-1330.
|
||||
ivyEnabled && expect(() => vcRefDir.vcref.move(viewRef !, -1)).toThrow();
|
||||
ivyEnabled && expect(() => vcRefDir.vcref.move(viewRef !, 42)).toThrow();
|
||||
ivyEnabled && expect(() => vcRefDir.vcref.move(viewRef!, -1)).toThrow();
|
||||
ivyEnabled && expect(() => vcRefDir.vcref.move(viewRef!, 42)).toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
@ -769,7 +767,7 @@ describe('ViewContainerRef', () => {
|
||||
`
|
||||
})
|
||||
class TestComponent {
|
||||
@ViewChild(VCRefDirective, {static: true}) vcRefDir !: VCRefDirective;
|
||||
@ViewChild(VCRefDirective, {static: true}) vcRefDir!: VCRefDirective;
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule({declarations: [VCRefDirective, TestComponent]});
|
||||
@ -789,7 +787,6 @@ describe('ViewContainerRef', () => {
|
||||
});
|
||||
|
||||
describe('detach', () => {
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({declarations: [EmbeddedViewInsertionComp, VCRefDirective]});
|
||||
|
||||
@ -825,7 +822,7 @@ describe('ViewContainerRef', () => {
|
||||
// Invalid indices when detaching throws an exception in Ivy: FW-1330.
|
||||
ivyEnabled && expect(() => vcRefDir.vcref.detach(-1)).toThrow();
|
||||
ivyEnabled && expect(() => vcRefDir.vcref.detach(42)).toThrow();
|
||||
ivyEnabled && expect(ngDevMode !.rendererDestroyNode).toBe(0);
|
||||
ivyEnabled && expect(ngDevMode!.rendererDestroyNode).toBe(0);
|
||||
});
|
||||
|
||||
it('should detach the last embedded view when no index is specified', () => {
|
||||
@ -846,7 +843,7 @@ describe('ViewContainerRef', () => {
|
||||
fixture.detectChanges();
|
||||
expect(getElementHtml(fixture.nativeElement)).toEqual('<p vcref=""></p>ABCD');
|
||||
expect(viewE.destroyed).toBeFalsy();
|
||||
ivyEnabled && expect(ngDevMode !.rendererDestroyNode).toBe(0);
|
||||
ivyEnabled && expect(ngDevMode!.rendererDestroyNode).toBe(0);
|
||||
});
|
||||
});
|
||||
|
||||
@ -895,7 +892,7 @@ describe('ViewContainerRef', () => {
|
||||
// Invalid indices when detaching throws an exception in Ivy: FW-1330.
|
||||
ivyEnabled && expect(() => vcRefDir.vcref.remove(-1)).toThrow();
|
||||
ivyEnabled && expect(() => vcRefDir.vcref.remove(42)).toThrow();
|
||||
ivyEnabled && expect(ngDevMode !.rendererDestroyNode).toBe(2);
|
||||
ivyEnabled && expect(ngDevMode!.rendererDestroyNode).toBe(2);
|
||||
});
|
||||
|
||||
it('should remove the last embedded view when no index is specified', () => {
|
||||
@ -916,7 +913,7 @@ describe('ViewContainerRef', () => {
|
||||
fixture.detectChanges();
|
||||
expect(getElementHtml(fixture.nativeElement)).toEqual('<p vcref=""></p>ABCD');
|
||||
expect(viewE.destroyed).toBeTruthy();
|
||||
ivyEnabled && expect(ngDevMode !.rendererDestroyNode).toBe(1);
|
||||
ivyEnabled && expect(ngDevMode!.rendererDestroyNode).toBe(1);
|
||||
});
|
||||
|
||||
it('should throw when trying to insert a removed or destroyed view', () => {
|
||||
@ -1063,7 +1060,7 @@ describe('ViewContainerRef', () => {
|
||||
`
|
||||
})
|
||||
class TestComponent {
|
||||
@ViewChild(VCRefDirective, {static: true}) vcRef !: VCRefDirective;
|
||||
@ViewChild(VCRefDirective, {static: true}) vcRef!: VCRefDirective;
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule({declarations: [TestComponent, VCRefDirective]});
|
||||
@ -1087,8 +1084,8 @@ describe('ViewContainerRef', () => {
|
||||
expect(getElementHtml(fixture.nativeElement)).toEqual('YABC<footer></footer>');
|
||||
|
||||
// Invalid indices when detaching throws an exception in Ivy: FW-1330.
|
||||
ivyEnabled && expect(() => vcRef !.createView('Z', -1)).toThrow();
|
||||
ivyEnabled && expect(() => vcRef !.createView('Z', 5)).toThrow();
|
||||
ivyEnabled && expect(() => vcRef!.createView('Z', -1)).toThrow();
|
||||
ivyEnabled && expect(() => vcRef!.createView('Z', 5)).toThrow();
|
||||
});
|
||||
|
||||
it('should apply directives and pipes of the host view to the TemplateRef', () => {
|
||||
@ -1099,7 +1096,9 @@ describe('ViewContainerRef', () => {
|
||||
|
||||
@Pipe({name: 'starPipe'})
|
||||
class StarPipe implements PipeTransform {
|
||||
transform(value: any) { return `**${value}**`; }
|
||||
transform(value: any) {
|
||||
return `**${value}**`;
|
||||
}
|
||||
}
|
||||
|
||||
@Component({
|
||||
@ -1121,15 +1120,14 @@ describe('ViewContainerRef', () => {
|
||||
fixture.debugElement.query(By.directive(VCRefDirective)).injector.get(VCRefDirective);
|
||||
fixture.detectChanges();
|
||||
|
||||
vcRefDir.vcref.createEmbeddedView(vcRefDir.tplRef !);
|
||||
vcRefDir.vcref.createEmbeddedView(vcRefDir.tplRef !);
|
||||
vcRefDir.vcref.createEmbeddedView(vcRefDir.tplRef!);
|
||||
vcRefDir.vcref.createEmbeddedView(vcRefDir.tplRef!);
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(getElementHtml(fixture.nativeElement))
|
||||
.toEqual(
|
||||
'<child vcref="">**A**</child><child>**C**</child><child>**C**</child><child>**B**</child>');
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('createComponent', () => {
|
||||
@ -1140,9 +1138,13 @@ describe('ViewContainerRef', () => {
|
||||
it('should work without Injector and NgModuleRef', () => {
|
||||
@Component({selector: 'embedded-cmp', template: `foo`})
|
||||
class EmbeddedComponent implements DoCheck, OnInit {
|
||||
ngOnInit() { templateExecutionCounter++; }
|
||||
ngOnInit() {
|
||||
templateExecutionCounter++;
|
||||
}
|
||||
|
||||
ngDoCheck() { templateExecutionCounter++; }
|
||||
ngDoCheck() {
|
||||
templateExecutionCounter++;
|
||||
}
|
||||
}
|
||||
|
||||
@NgModule({entryComponents: [EmbeddedComponent], declarations: [EmbeddedComponent]})
|
||||
@ -1185,13 +1187,16 @@ describe('ViewContainerRef', () => {
|
||||
selector: 'embedded-cmp',
|
||||
template: `foo`,
|
||||
})
|
||||
class EmbeddedComponent implements DoCheck,
|
||||
OnInit {
|
||||
class EmbeddedComponent implements DoCheck, OnInit {
|
||||
constructor(public s: String) {}
|
||||
|
||||
ngOnInit() { templateExecutionCounter++; }
|
||||
ngOnInit() {
|
||||
templateExecutionCounter++;
|
||||
}
|
||||
|
||||
ngDoCheck() { templateExecutionCounter++; }
|
||||
ngDoCheck() {
|
||||
templateExecutionCounter++;
|
||||
}
|
||||
}
|
||||
|
||||
@NgModule({entryComponents: [EmbeddedComponent], declarations: [EmbeddedComponent]})
|
||||
@ -1423,7 +1428,7 @@ describe('ViewContainerRef', () => {
|
||||
const makeComponentFactory = (componentType: any) => ({
|
||||
create: () => TestBed.createComponent(componentType).componentRef,
|
||||
});
|
||||
this.viewContainerRef !.createComponent(makeComponentFactory(Child) as any);
|
||||
this.viewContainerRef!.createComponent(makeComponentFactory(Child) as any);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1493,8 +1498,8 @@ describe('ViewContainerRef', () => {
|
||||
`,
|
||||
})
|
||||
class LoopComp {
|
||||
@Input() tpl !: TemplateRef<any>;
|
||||
@Input() rows !: any[];
|
||||
@Input() tpl!: TemplateRef<any>;
|
||||
@Input() rows!: any[];
|
||||
name = 'Loop';
|
||||
}
|
||||
|
||||
@ -1715,7 +1720,6 @@ describe('ViewContainerRef', () => {
|
||||
});
|
||||
|
||||
describe('lifecycle hooks', () => {
|
||||
|
||||
// Angular 5 reference: https://stackblitz.com/edit/lifecycle-hooks-vcref
|
||||
const log: string[] = [];
|
||||
|
||||
@ -1723,19 +1727,37 @@ describe('ViewContainerRef', () => {
|
||||
class ComponentWithHooks {
|
||||
@Input() name: string|undefined;
|
||||
|
||||
private log(msg: string) { log.push(msg); }
|
||||
private log(msg: string) {
|
||||
log.push(msg);
|
||||
}
|
||||
|
||||
ngOnChanges() { this.log('onChanges-' + this.name); }
|
||||
ngOnInit() { this.log('onInit-' + this.name); }
|
||||
ngDoCheck() { this.log('doCheck-' + this.name); }
|
||||
ngOnChanges() {
|
||||
this.log('onChanges-' + this.name);
|
||||
}
|
||||
ngOnInit() {
|
||||
this.log('onInit-' + this.name);
|
||||
}
|
||||
ngDoCheck() {
|
||||
this.log('doCheck-' + this.name);
|
||||
}
|
||||
|
||||
ngAfterContentInit() { this.log('afterContentInit-' + this.name); }
|
||||
ngAfterContentChecked() { this.log('afterContentChecked-' + this.name); }
|
||||
ngAfterContentInit() {
|
||||
this.log('afterContentInit-' + this.name);
|
||||
}
|
||||
ngAfterContentChecked() {
|
||||
this.log('afterContentChecked-' + this.name);
|
||||
}
|
||||
|
||||
ngAfterViewInit() { this.log('afterViewInit-' + this.name); }
|
||||
ngAfterViewChecked() { this.log('afterViewChecked-' + this.name); }
|
||||
ngAfterViewInit() {
|
||||
this.log('afterViewInit-' + this.name);
|
||||
}
|
||||
ngAfterViewChecked() {
|
||||
this.log('afterViewChecked-' + this.name);
|
||||
}
|
||||
|
||||
ngOnDestroy() { this.log('onDestroy-' + this.name); }
|
||||
ngOnDestroy() {
|
||||
this.log('onDestroy-' + this.name);
|
||||
}
|
||||
}
|
||||
|
||||
@NgModule({
|
||||
@ -1784,7 +1806,7 @@ describe('ViewContainerRef', () => {
|
||||
]);
|
||||
|
||||
log.length = 0;
|
||||
vcRefDir.vcref.createEmbeddedView(vcRefDir.tplRef !);
|
||||
vcRefDir.vcref.createEmbeddedView(vcRefDir.tplRef!);
|
||||
expect(getElementHtml(fixture.nativeElement))
|
||||
.toEqual('<hooks vcref="">A</hooks><hooks></hooks><hooks>B</hooks>');
|
||||
expect(log).toEqual([]);
|
||||
@ -1815,7 +1837,7 @@ describe('ViewContainerRef', () => {
|
||||
]);
|
||||
|
||||
log.length = 0;
|
||||
vcRefDir.vcref.insert(viewRef !);
|
||||
vcRefDir.vcref.insert(viewRef!);
|
||||
fixture.detectChanges();
|
||||
expect(log).toEqual([
|
||||
'doCheck-A', 'doCheck-B', 'doCheck-C', 'afterContentChecked-C', 'afterViewChecked-C',
|
||||
@ -1898,7 +1920,7 @@ describe('ViewContainerRef', () => {
|
||||
]);
|
||||
|
||||
log.length = 0;
|
||||
vcRefDir.vcref.insert(viewRef !);
|
||||
vcRefDir.vcref.insert(viewRef!);
|
||||
fixture.detectChanges();
|
||||
expect(log).toEqual([
|
||||
'doCheck-A', 'doCheck-B', 'doCheck-D', 'afterContentChecked-D', 'afterViewChecked-D',
|
||||
@ -1916,7 +1938,6 @@ describe('ViewContainerRef', () => {
|
||||
});
|
||||
|
||||
describe('host bindings', () => {
|
||||
|
||||
it('should support host bindings on dynamically created components', () => {
|
||||
@Component(
|
||||
{selector: 'host-bindings', host: {'id': 'attribute', '[title]': 'title'}, template: ``})
|
||||
@ -1926,7 +1947,7 @@ describe('ViewContainerRef', () => {
|
||||
|
||||
@Component({template: `<ng-template vcref></ng-template>`})
|
||||
class TestComponent {
|
||||
@ViewChild(VCRefDirective, {static: true}) vcRefDir !: VCRefDirective;
|
||||
@ViewChild(VCRefDirective, {static: true}) vcRefDir!: VCRefDirective;
|
||||
}
|
||||
|
||||
@NgModule({declarations: [HostBindingCmpt], entryComponents: [HostBindingCmpt]})
|
||||
@ -1956,11 +1977,9 @@ describe('ViewContainerRef', () => {
|
||||
expect(fixture.nativeElement.children[0].getAttribute('id')).toBe('attribute');
|
||||
expect(fixture.nativeElement.children[0].getAttribute('title')).toBe('changed');
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('projection', () => {
|
||||
|
||||
it('should project the ViewContainerRef content along its host, in an element', () => {
|
||||
@Component({selector: 'child', template: '<div><ng-content></ng-content></div>'})
|
||||
class Child {
|
||||
@ -1990,7 +2009,7 @@ describe('ViewContainerRef', () => {
|
||||
expect(getElementHtml(fixture.nativeElement))
|
||||
.toEqual('<child><div><header vcref="">blah</header></div></child>');
|
||||
|
||||
vcRefDir.vcref.createEmbeddedView(vcRefDir.tplRef !);
|
||||
vcRefDir.vcref.createEmbeddedView(vcRefDir.tplRef!);
|
||||
fixture.detectChanges();
|
||||
expect(getElementHtml(fixture.nativeElement))
|
||||
.toEqual('<child><div><header vcref="">blah</header><span>bar</span></div></child>');
|
||||
@ -2031,7 +2050,7 @@ describe('ViewContainerRef', () => {
|
||||
.toEqual(
|
||||
'<child-with-view>Before (inside)- Before projected <header vcref="">blah</header> After projected -After (inside)</child-with-view>');
|
||||
|
||||
vcRefDir.vcref.createEmbeddedView(vcRefDir.tplRef !);
|
||||
vcRefDir.vcref.createEmbeddedView(vcRefDir.tplRef!);
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(getElementHtml(fixture.nativeElement))
|
||||
@ -2067,7 +2086,6 @@ describe('ViewContainerRef', () => {
|
||||
});
|
||||
|
||||
describe('with select', () => {
|
||||
|
||||
@Component({
|
||||
selector: 'child-with-selector',
|
||||
template: `
|
||||
@ -2105,7 +2123,7 @@ describe('ViewContainerRef', () => {
|
||||
.toEqual(
|
||||
'<child-with-selector><p class="a"><header vcref="">blah</header></p><p class="b"></p></child-with-selector>');
|
||||
|
||||
vcRefDir.vcref.createEmbeddedView(vcRefDir.tplRef !);
|
||||
vcRefDir.vcref.createEmbeddedView(vcRefDir.tplRef!);
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(getElementHtml(fixture.nativeElement))
|
||||
@ -2132,13 +2150,13 @@ describe('ViewContainerRef', () => {
|
||||
`
|
||||
})
|
||||
class MyComp {
|
||||
@ViewChild('source', {static: true})
|
||||
source !: TemplateRef<{}>;
|
||||
@ViewChild('source', {static: true}) source!: TemplateRef<{}>;
|
||||
|
||||
@ViewChild('target', {read: ViewContainerRef, static: true})
|
||||
target !: ViewContainerRef;
|
||||
@ViewChild('target', {read: ViewContainerRef, static: true}) target!: ViewContainerRef;
|
||||
|
||||
ngOnInit() { this.target.createEmbeddedView(this.source); }
|
||||
ngOnInit() {
|
||||
this.target.createEmbeddedView(this.source);
|
||||
}
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule({declarations: [MyComp, ContentComp]});
|
||||
@ -2175,7 +2193,7 @@ describe('ViewContainerRef', () => {
|
||||
.toEqual(
|
||||
'<child-with-selector><p class="a"></p><p class="b"><footer vcref="">blah</footer></p></child-with-selector>');
|
||||
|
||||
vcRefDir.vcref.createEmbeddedView(vcRefDir.tplRef !);
|
||||
vcRefDir.vcref.createEmbeddedView(vcRefDir.tplRef!);
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(getElementHtml(fixture.nativeElement))
|
||||
@ -2183,7 +2201,6 @@ describe('ViewContainerRef', () => {
|
||||
'<child-with-selector><p class="a"></p><p class="b"><footer vcref="">blah</footer><span>bar</span></p></child-with-selector>');
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('root view container ref', () => {
|
||||
@ -2204,7 +2221,7 @@ describe('ViewContainerRef', () => {
|
||||
|
||||
containerEl = document.createElement('div');
|
||||
document.body.appendChild(containerEl);
|
||||
containerEl !.appendChild(rootEl);
|
||||
containerEl!.appendChild(rootEl);
|
||||
}
|
||||
};
|
||||
}
|
||||
@ -2223,7 +2240,9 @@ describe('ViewContainerRef', () => {
|
||||
class DynamicCompWithBindings implements DoCheck {
|
||||
checkCount = 0;
|
||||
|
||||
ngDoCheck() { this.checkCount++; }
|
||||
ngDoCheck() {
|
||||
this.checkCount++;
|
||||
}
|
||||
}
|
||||
|
||||
@Component({template: ``})
|
||||
@ -2248,21 +2267,21 @@ describe('ViewContainerRef', () => {
|
||||
|
||||
// Ivy inserts a comment for the root view container ref instance. This is not
|
||||
// the case for view engine and we need to adjust the assertions.
|
||||
expect(containerEl !.childNodes.length).toBe(ivyEnabled ? 2 : 1);
|
||||
ivyEnabled && expect(containerEl !.childNodes[1].nodeType).toBe(Node.COMMENT_NODE);
|
||||
expect(containerEl!.childNodes.length).toBe(ivyEnabled ? 2 : 1);
|
||||
ivyEnabled && expect(containerEl!.childNodes[1].nodeType).toBe(Node.COMMENT_NODE);
|
||||
|
||||
expect((containerEl !.childNodes[0] as Element).tagName).toBe('DIV');
|
||||
expect((containerEl!.childNodes[0] as Element).tagName).toBe('DIV');
|
||||
|
||||
vcRef.createComponent(cfResolver.resolveComponentFactory(DynamicCompWithBindings));
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(containerEl !.childNodes.length).toBe(ivyEnabled ? 3 : 2);
|
||||
expect(containerEl !.childNodes[1].textContent).toBe('check count: 1');
|
||||
expect(containerEl!.childNodes.length).toBe(ivyEnabled ? 3 : 2);
|
||||
expect(containerEl!.childNodes[1].textContent).toBe('check count: 1');
|
||||
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(containerEl !.childNodes.length).toBe(ivyEnabled ? 3 : 2);
|
||||
expect(containerEl !.childNodes[1].textContent).toBe('check count: 2');
|
||||
expect(containerEl!.childNodes.length).toBe(ivyEnabled ? 3 : 2);
|
||||
expect(containerEl!.childNodes[1].textContent).toBe('check count: 2');
|
||||
});
|
||||
|
||||
it('should create deep DOM tree immediately for dynamically created components', () => {
|
||||
@ -2299,21 +2318,21 @@ describe('ViewContainerRef', () => {
|
||||
|
||||
// Ivy inserts a comment for the root view container ref instance. This is not
|
||||
// the case for view engine and we need to adjust the assertions.
|
||||
expect(containerEl !.childNodes.length).toBe(ivyEnabled ? 2 : 1);
|
||||
ivyEnabled && expect(containerEl !.childNodes[1].nodeType).toBe(Node.COMMENT_NODE);
|
||||
expect(containerEl!.childNodes.length).toBe(ivyEnabled ? 2 : 1);
|
||||
ivyEnabled && expect(containerEl!.childNodes[1].nodeType).toBe(Node.COMMENT_NODE);
|
||||
|
||||
expect((containerEl !.childNodes[0] as Element).tagName).toBe('DIV');
|
||||
expect((containerEl!.childNodes[0] as Element).tagName).toBe('DIV');
|
||||
|
||||
vcRef.createComponent(cfResolver.resolveComponentFactory(DynamicCompWithChildren));
|
||||
|
||||
expect(containerEl !.childNodes.length).toBe(ivyEnabled ? 3 : 2);
|
||||
expect(getElementHtml(containerEl !.childNodes[1] as Element))
|
||||
expect(containerEl!.childNodes.length).toBe(ivyEnabled ? 3 : 2);
|
||||
expect(getElementHtml(containerEl!.childNodes[1] as Element))
|
||||
.toBe('<child><div></div></child>');
|
||||
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(containerEl !.childNodes.length).toBe(ivyEnabled ? 3 : 2);
|
||||
expect(getElementHtml(containerEl !.childNodes[1] as Element))
|
||||
expect(containerEl!.childNodes.length).toBe(ivyEnabled ? 3 : 2);
|
||||
expect(getElementHtml(containerEl!.childNodes[1] as Element))
|
||||
.toBe(`<child><div>text</div></child>`);
|
||||
});
|
||||
});
|
||||
@ -2374,7 +2393,7 @@ class EmbeddedComponentWithNgZoneModule {
|
||||
`
|
||||
})
|
||||
class ViewContainerRefComp {
|
||||
@ViewChildren(TemplateRef) templates !: QueryList<TemplateRef<any>>;
|
||||
@ViewChildren(TemplateRef) templates!: QueryList<TemplateRef<any>>;
|
||||
|
||||
constructor(public vcr: ViewContainerRef) {}
|
||||
}
|
||||
@ -2386,21 +2405,25 @@ class ViewContainerRefComp {
|
||||
`
|
||||
})
|
||||
class ViewContainerRefApp {
|
||||
@ViewChild(ViewContainerRefComp) vcrComp !: ViewContainerRefComp;
|
||||
@ViewChild(ViewContainerRefComp) vcrComp!: ViewContainerRefComp;
|
||||
}
|
||||
|
||||
@Directive({selector: '[structDir]'})
|
||||
export class StructDir {
|
||||
constructor(private vcref: ViewContainerRef, private tplRef: TemplateRef<any>) {}
|
||||
|
||||
create() { this.vcref.createEmbeddedView(this.tplRef); }
|
||||
create() {
|
||||
this.vcref.createEmbeddedView(this.tplRef);
|
||||
}
|
||||
|
||||
destroy() { this.vcref.clear(); }
|
||||
destroy() {
|
||||
this.vcref.clear();
|
||||
}
|
||||
}
|
||||
|
||||
@Component({selector: 'destroy-cases', template: ` `})
|
||||
class DestroyCasesComp {
|
||||
@ViewChildren(StructDir) structDirs !: QueryList<StructDir>;
|
||||
@ViewChildren(StructDir) structDirs!: QueryList<StructDir>;
|
||||
}
|
||||
|
||||
@Directive({selector: '[constructorDir]'})
|
||||
@ -2419,7 +2442,7 @@ class ConstructorDir {
|
||||
`
|
||||
})
|
||||
class ConstructorApp {
|
||||
@ViewChild('foo', {static: true}) foo !: ElementRef;
|
||||
@ViewChild('foo', {static: true}) foo!: ElementRef;
|
||||
}
|
||||
|
||||
@Component({
|
||||
@ -2431,5 +2454,5 @@ class ConstructorApp {
|
||||
`
|
||||
})
|
||||
class ConstructorAppWithQueries {
|
||||
@ViewChild('foo', {static: true}) foo !: TemplateRef<any>;
|
||||
@ViewChild('foo', {static: true}) foo!: TemplateRef<any>;
|
||||
}
|
||||
|
@ -33,15 +33,14 @@ describe('view insertion', () => {
|
||||
})
|
||||
class App {
|
||||
@ViewChild('container', {read: ViewContainerRef, static: true})
|
||||
container: ViewContainerRef = null !;
|
||||
container: ViewContainerRef = null!;
|
||||
|
||||
@ViewChild('simple', {read: TemplateRef, static: true})
|
||||
simple: TemplateRef<any> = null !;
|
||||
@ViewChild('simple', {read: TemplateRef, static: true}) simple: TemplateRef<any> = null!;
|
||||
|
||||
view0: EmbeddedViewRef<any> = null !;
|
||||
view1: EmbeddedViewRef<any> = null !;
|
||||
view2: EmbeddedViewRef<any> = null !;
|
||||
view3: EmbeddedViewRef<any> = null !;
|
||||
view0: EmbeddedViewRef<any> = null!;
|
||||
view1: EmbeddedViewRef<any> = null!;
|
||||
view2: EmbeddedViewRef<any> = null!;
|
||||
view3: EmbeddedViewRef<any> = null!;
|
||||
|
||||
constructor(public changeDetector: ChangeDetectorRef) {}
|
||||
|
||||
@ -90,16 +89,14 @@ describe('view insertion', () => {
|
||||
`
|
||||
})
|
||||
class App {
|
||||
@ViewChild('container', {read: ViewContainerRef})
|
||||
container: ViewContainerRef = null !;
|
||||
@ViewChild('container', {read: ViewContainerRef}) container: ViewContainerRef = null!;
|
||||
|
||||
@ViewChild('empty', {read: TemplateRef})
|
||||
empty: TemplateRef<any> = null !;
|
||||
@ViewChild('empty', {read: TemplateRef}) empty: TemplateRef<any> = null!;
|
||||
|
||||
view0: EmbeddedViewRef<any> = null !;
|
||||
view1: EmbeddedViewRef<any> = null !;
|
||||
view2: EmbeddedViewRef<any> = null !;
|
||||
view3: EmbeddedViewRef<any> = null !;
|
||||
view0: EmbeddedViewRef<any> = null!;
|
||||
view1: EmbeddedViewRef<any> = null!;
|
||||
view2: EmbeddedViewRef<any> = null!;
|
||||
view3: EmbeddedViewRef<any> = null!;
|
||||
|
||||
ngAfterViewInit() {
|
||||
// insert at the front
|
||||
@ -140,16 +137,14 @@ describe('view insertion', () => {
|
||||
`
|
||||
})
|
||||
class Comp {
|
||||
@ViewChild('container', {read: ViewContainerRef})
|
||||
container: ViewContainerRef = null !;
|
||||
@ViewChild('container', {read: ViewContainerRef}) container: ViewContainerRef = null!;
|
||||
|
||||
@ViewChild('projection', {read: TemplateRef})
|
||||
projection: TemplateRef<any> = null !;
|
||||
@ViewChild('projection', {read: TemplateRef}) projection: TemplateRef<any> = null!;
|
||||
|
||||
view0: EmbeddedViewRef<any> = null !;
|
||||
view1: EmbeddedViewRef<any> = null !;
|
||||
view2: EmbeddedViewRef<any> = null !;
|
||||
view3: EmbeddedViewRef<any> = null !;
|
||||
view0: EmbeddedViewRef<any> = null!;
|
||||
view1: EmbeddedViewRef<any> = null!;
|
||||
view2: EmbeddedViewRef<any> = null!;
|
||||
view3: EmbeddedViewRef<any> = null!;
|
||||
|
||||
ngAfterViewInit() {
|
||||
// insert at the front
|
||||
@ -201,16 +196,14 @@ describe('view insertion', () => {
|
||||
`
|
||||
})
|
||||
class App {
|
||||
@ViewChild('container', {read: ViewContainerRef})
|
||||
container: ViewContainerRef = null !;
|
||||
@ViewChild('container', {read: ViewContainerRef}) container: ViewContainerRef = null!;
|
||||
|
||||
@ViewChild('subContainer', {read: TemplateRef})
|
||||
subContainer: TemplateRef<any> = null !;
|
||||
@ViewChild('subContainer', {read: TemplateRef}) subContainer: TemplateRef<any> = null!;
|
||||
|
||||
view0: EmbeddedViewRef<any> = null !;
|
||||
view1: EmbeddedViewRef<any> = null !;
|
||||
view2: EmbeddedViewRef<any> = null !;
|
||||
view3: EmbeddedViewRef<any> = null !;
|
||||
view0: EmbeddedViewRef<any> = null!;
|
||||
view1: EmbeddedViewRef<any> = null!;
|
||||
view2: EmbeddedViewRef<any> = null!;
|
||||
view3: EmbeddedViewRef<any> = null!;
|
||||
|
||||
constructor(public changeDetectorRef: ChangeDetectorRef) {}
|
||||
|
||||
@ -265,9 +258,9 @@ describe('view insertion', () => {
|
||||
describe('before embedded view', () => {
|
||||
@Component({selector: 'test-cmpt', template: ''})
|
||||
class TestCmpt {
|
||||
@ViewChild('before', {static: true}) beforeTpl !: TemplateRef<{}>;
|
||||
@ViewChild('insert', {static: true}) insertTpl !: TemplateRef<{}>;
|
||||
@ViewChild('vi', {static: true}) viewInsertingDir !: ViewInsertingDir;
|
||||
@ViewChild('before', {static: true}) beforeTpl!: TemplateRef<{}>;
|
||||
@ViewChild('insert', {static: true}) insertTpl!: TemplateRef<{}>;
|
||||
@ViewChild('vi', {static: true}) viewInsertingDir!: ViewInsertingDir;
|
||||
|
||||
minutes = 10;
|
||||
|
||||
@ -303,8 +296,9 @@ describe('view insertion', () => {
|
||||
}
|
||||
|
||||
|
||||
it('should insert before a view with the text node as the first root node',
|
||||
() => { expect(createAndInsertViews('|before').textContent).toBe('insert|before'); });
|
||||
it('should insert before a view with the text node as the first root node', () => {
|
||||
expect(createAndInsertViews('|before').textContent).toBe('insert|before');
|
||||
});
|
||||
|
||||
it('should insert before a view with the element as the first root node', () => {
|
||||
expect(createAndInsertViews('<span>|before</span>').textContent).toBe('insert|before');
|
||||
@ -336,13 +330,11 @@ describe('view insertion', () => {
|
||||
it('should insert before a view with a container as the first root node', () => {
|
||||
expect(createAndInsertViews(`<ng-template [ngIf]="true">|before</ng-template>`).textContent)
|
||||
.toBe('insert|before');
|
||||
|
||||
});
|
||||
|
||||
it('should insert before a view with an empty container as the first root node', () => {
|
||||
expect(createAndInsertViews(`<ng-template [ngIf]="true"></ng-template>|before`).textContent)
|
||||
.toBe('insert|before');
|
||||
|
||||
});
|
||||
|
||||
onlyInIvy('VE incorrectly inserts views before ng-container content')
|
||||
@ -353,7 +345,6 @@ describe('view insertion', () => {
|
||||
<ng-template #after>|after</ng-template>
|
||||
`).textContent)
|
||||
.toBe('insert|before|after');
|
||||
|
||||
});
|
||||
|
||||
|
||||
@ -363,7 +354,6 @@ describe('view insertion', () => {
|
||||
<ng-template #after>|after</ng-template>
|
||||
`).textContent)
|
||||
.toBe('insert|before|after');
|
||||
|
||||
});
|
||||
|
||||
it('should insert before a view with an empty projection as the first root node', () => {
|
||||
@ -414,11 +404,9 @@ describe('view insertion', () => {
|
||||
fixture.detectChanges();
|
||||
expect(fixture.debugElement.nativeElement.textContent).toBe('start|testtest|end');
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('before embedded view with projection', () => {
|
||||
|
||||
@Component({
|
||||
selector: 'with-content',
|
||||
template: `
|
||||
@ -428,9 +416,9 @@ describe('view insertion', () => {
|
||||
`
|
||||
})
|
||||
class WithContentCmpt {
|
||||
@ViewChild('insert', {static: true}) insertTpl !: TemplateRef<{}>;
|
||||
@ViewChild('before', {static: true}) beforeTpl !: TemplateRef<{}>;
|
||||
@ViewChild('vi', {static: true}) viewInsertingDir !: ViewInsertingDir;
|
||||
@ViewChild('insert', {static: true}) insertTpl!: TemplateRef<{}>;
|
||||
@ViewChild('before', {static: true}) beforeTpl!: TemplateRef<{}>;
|
||||
@ViewChild('vi', {static: true}) viewInsertingDir!: ViewInsertingDir;
|
||||
|
||||
insert() {
|
||||
const beforeView = this.beforeTpl.createEmbeddedView({});
|
||||
@ -442,7 +430,7 @@ describe('view insertion', () => {
|
||||
|
||||
@Component({selector: 'test-cmpt', template: ''})
|
||||
class TestCmpt {
|
||||
@ViewChild('wc', {static: true}) withContentCmpt !: WithContentCmpt;
|
||||
@ViewChild('wc', {static: true}) withContentCmpt!: WithContentCmpt;
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
@ -476,7 +464,6 @@ describe('view insertion', () => {
|
||||
|
||||
expect(fixture.nativeElement.textContent).toBe('insert|before');
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('before component view', () => {
|
||||
@ -503,8 +490,8 @@ describe('view insertion', () => {
|
||||
`
|
||||
})
|
||||
class TestCmpt {
|
||||
@ViewChild('insert', {static: true}) insertTpl !: TemplateRef<{}>;
|
||||
@ViewChild('vi', {static: true}) viewInsertingDir !: ViewInsertingDir;
|
||||
@ViewChild('insert', {static: true}) insertTpl!: TemplateRef<{}>;
|
||||
@ViewChild('vi', {static: true}) viewInsertingDir!: ViewInsertingDir;
|
||||
|
||||
constructor(private _cfr: ComponentFactoryResolver, private _injector: Injector) {}
|
||||
|
||||
@ -537,16 +524,13 @@ describe('view insertion', () => {
|
||||
|
||||
expect(fixture.nativeElement.textContent).toBe('insert|before');
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
describe('non-regression', () => {
|
||||
|
||||
// https://github.com/angular/angular/issues/31971
|
||||
it('should insert component views into ViewContainerRef injected by querying <ng-container>',
|
||||
() => {
|
||||
|
||||
@Component({selector: 'dynamic-cmpt', template: 'dynamic'})
|
||||
class DynamicComponent {
|
||||
}
|
||||
@ -562,8 +546,7 @@ describe('view insertion', () => {
|
||||
`
|
||||
})
|
||||
class AppComponent {
|
||||
@ViewChild('container', {read: ViewContainerRef, static: true})
|
||||
vcr !: ViewContainerRef;
|
||||
@ViewChild('container', {read: ViewContainerRef, static: true}) vcr!: ViewContainerRef;
|
||||
|
||||
constructor(private _cfr: ComponentFactoryResolver) {}
|
||||
|
||||
@ -595,7 +578,6 @@ describe('view insertion', () => {
|
||||
// https://github.com/angular/angular/issues/33679
|
||||
it('should insert embedded views into ViewContainerRef injected by querying <ng-container>',
|
||||
() => {
|
||||
|
||||
@Component({
|
||||
selector: 'app-root',
|
||||
template: `
|
||||
@ -608,12 +590,13 @@ describe('view insertion', () => {
|
||||
`
|
||||
})
|
||||
class AppComponent {
|
||||
@ViewChild('container', {read: ViewContainerRef, static: true})
|
||||
vcr !: ViewContainerRef;
|
||||
@ViewChild('container', {read: ViewContainerRef, static: true}) vcr!: ViewContainerRef;
|
||||
|
||||
@ViewChild('template', {read: TemplateRef, static: true}) template !: TemplateRef<any>;
|
||||
|
||||
click() { this.vcr.createEmbeddedView(this.template, undefined, 0); }
|
||||
click() {
|
||||
this.vcr.createEmbeddedView(this.template, undefined, 0);
|
||||
}
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule({
|
||||
@ -659,6 +642,5 @@ describe('view insertion', () => {
|
||||
|
||||
expect(fixture.nativeElement.textContent.trim()).toContain('2 1');
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
|
@ -13,7 +13,6 @@ import {TestBed} from '@angular/core/testing';
|
||||
|
||||
describe('ViewRef', () => {
|
||||
it('should remove nodes from DOM when the view is detached from app ref', () => {
|
||||
|
||||
@Component({selector: 'dynamic-cpt', template: '<div></div>'})
|
||||
class DynamicComponent {
|
||||
constructor(public elRef: ElementRef) {}
|
||||
@ -21,7 +20,7 @@ describe('ViewRef', () => {
|
||||
|
||||
@Component({template: `<span></span>`})
|
||||
class App {
|
||||
componentRef !: ComponentRef<DynamicComponent>;
|
||||
componentRef!: ComponentRef<DynamicComponent>;
|
||||
constructor(
|
||||
public appRef: ApplicationRef, private cfr: ComponentFactoryResolver,
|
||||
private injector: Injector) {}
|
||||
@ -33,7 +32,9 @@ describe('ViewRef', () => {
|
||||
document.body.appendChild(this.componentRef.instance.elRef.nativeElement);
|
||||
}
|
||||
|
||||
destroy() { (this.componentRef.hostView as InternalViewRef).detachFromAppRef(); }
|
||||
destroy() {
|
||||
(this.componentRef.hostView as InternalViewRef).detachFromAppRef();
|
||||
}
|
||||
}
|
||||
|
||||
@NgModule({declarations: [App, DynamicComponent], entryComponents: [DynamicComponent]})
|
||||
|
Reference in New Issue
Block a user