refactor(ivy): move di tests for flags to acceptance (#29299)
Including tests for `@Optional`, `@Self`, `@SkipSelf` and `@Host`. PR Close #29299
This commit is contained in:
parent
1b0be8d656
commit
0151ad432b
|
@ -7,7 +7,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {CommonModule} from '@angular/common';
|
import {CommonModule} from '@angular/common';
|
||||||
import {Attribute, ChangeDetectorRef, Component, Directive, ElementRef, EventEmitter, HostBinding, INJECTOR, Inject, Injectable, Injector, Input, LOCALE_ID, Optional, Output, Pipe, PipeTransform, SkipSelf, TemplateRef, ViewChild, ViewContainerRef, forwardRef} from '@angular/core';
|
import {Attribute, ChangeDetectorRef, Component, Directive, ElementRef, EventEmitter, Host, HostBinding, INJECTOR, Inject, Injectable, Injector, Input, LOCALE_ID, Optional, Output, Pipe, PipeTransform, Self, SkipSelf, TemplateRef, ViewChild, ViewContainerRef, forwardRef} from '@angular/core';
|
||||||
import {ViewRef} from '@angular/core/src/render3/view_ref';
|
import {ViewRef} from '@angular/core/src/render3/view_ref';
|
||||||
import {TestBed} from '@angular/core/testing';
|
import {TestBed} from '@angular/core/testing';
|
||||||
import {onlyInIvy} from '@angular/private/testing';
|
import {onlyInIvy} from '@angular/private/testing';
|
||||||
|
@ -38,6 +38,7 @@ describe('di', () => {
|
||||||
@Directive({selector: '[dirB]', exportAs: 'dirB'})
|
@Directive({selector: '[dirB]', exportAs: 'dirB'})
|
||||||
class DirectiveB {
|
class DirectiveB {
|
||||||
@Input() value = 'DirB';
|
@Input() value = 'DirB';
|
||||||
|
|
||||||
constructor() { log.push(this.value); }
|
constructor() { log.push(this.value); }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -48,11 +49,14 @@ describe('di', () => {
|
||||||
class DirectiveA {
|
class DirectiveA {
|
||||||
value = 'DirA';
|
value = 'DirA';
|
||||||
}
|
}
|
||||||
|
|
||||||
@Directive({selector: '[dirC]', exportAs: 'dirC'})
|
@Directive({selector: '[dirC]', exportAs: 'dirC'})
|
||||||
class DirectiveC {
|
class DirectiveC {
|
||||||
value: string;
|
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({
|
@Component({
|
||||||
template: `
|
template: `
|
||||||
<div dirA>
|
<div dirA>
|
||||||
|
@ -62,6 +66,7 @@ describe('di', () => {
|
||||||
})
|
})
|
||||||
class MyComp {
|
class MyComp {
|
||||||
}
|
}
|
||||||
|
|
||||||
TestBed.configureTestingModule({declarations: [DirectiveA, DirectiveB, DirectiveC, MyComp]});
|
TestBed.configureTestingModule({declarations: [DirectiveA, DirectiveB, DirectiveC, MyComp]});
|
||||||
const fixture = TestBed.createComponent(MyComp);
|
const fixture = TestBed.createComponent(MyComp);
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
|
@ -74,11 +79,14 @@ describe('di', () => {
|
||||||
@Directive({selector: '[dirA]'})
|
@Directive({selector: '[dirA]'})
|
||||||
class DirectiveA {
|
class DirectiveA {
|
||||||
value = 'dirA';
|
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>'})
|
@Component({template: '<div dirA dirB></div>'})
|
||||||
class MyComp {
|
class MyComp {
|
||||||
}
|
}
|
||||||
|
|
||||||
TestBed.configureTestingModule({declarations: [DirectiveA, DirectiveB, MyComp]});
|
TestBed.configureTestingModule({declarations: [DirectiveA, DirectiveB, MyComp]});
|
||||||
const fixture = TestBed.createComponent(MyComp);
|
const fixture = TestBed.createComponent(MyComp);
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
|
@ -90,14 +98,17 @@ describe('di', () => {
|
||||||
@Directive({selector: '[dirA]'})
|
@Directive({selector: '[dirA]'})
|
||||||
class DirectiveA {
|
class DirectiveA {
|
||||||
value = 'dirA';
|
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
|
// - dirB is know to the node injectors
|
||||||
// - then when dirA tries to inject dirB, it will check the node injector first tree
|
// - then when dirA tries to inject dirB, it will check the node injector first tree
|
||||||
// - if not found, it will check the module injector tree
|
// - if not found, it will check the module injector tree
|
||||||
@Component({template: '<div dirB></div><div dirA></div>'})
|
@Component({template: '<div dirB></div><div dirA></div>'})
|
||||||
class MyComp {
|
class MyComp {
|
||||||
}
|
}
|
||||||
|
|
||||||
TestBed.configureTestingModule({
|
TestBed.configureTestingModule({
|
||||||
declarations: [DirectiveA, DirectiveB, MyComp],
|
declarations: [DirectiveA, DirectiveB, MyComp],
|
||||||
providers: [{provide: DirectiveB, useValue: {value: 'module'}}]
|
providers: [{provide: DirectiveB, useValue: {value: 'module'}}]
|
||||||
|
@ -113,9 +124,11 @@ describe('di', () => {
|
||||||
class MyComp {
|
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>'})
|
@Component({template: '<my-comp dirB></my-comp>'})
|
||||||
class MyApp {
|
class MyApp {
|
||||||
}
|
}
|
||||||
|
|
||||||
TestBed.configureTestingModule({declarations: [DirectiveB, MyComp, MyApp]});
|
TestBed.configureTestingModule({declarations: [DirectiveB, MyComp, MyApp]});
|
||||||
const fixture = TestBed.createComponent(MyApp);
|
const fixture = TestBed.createComponent(MyApp);
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
|
@ -128,10 +141,12 @@ describe('di', () => {
|
||||||
class DirectiveA {
|
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>'})
|
@Component({template: '<div dirA dirB *ngFor="let i of array"></div>'})
|
||||||
class MyComp {
|
class MyComp {
|
||||||
array = [1, 2, 3];
|
array = [1, 2, 3];
|
||||||
}
|
}
|
||||||
|
|
||||||
TestBed.configureTestingModule({declarations: [DirectiveA, DirectiveB, MyComp]});
|
TestBed.configureTestingModule({declarations: [DirectiveA, DirectiveB, MyComp]});
|
||||||
const fixture = TestBed.createComponent(MyComp);
|
const fixture = TestBed.createComponent(MyComp);
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
|
@ -144,22 +159,28 @@ describe('di', () => {
|
||||||
@Directive({selector: '[dirA]'})
|
@Directive({selector: '[dirA]'})
|
||||||
class DirectiveA {
|
class DirectiveA {
|
||||||
value = 'DirA';
|
value = 'DirA';
|
||||||
|
|
||||||
constructor() { log.push(this.value); }
|
constructor() { log.push(this.value); }
|
||||||
}
|
}
|
||||||
|
|
||||||
@Directive({selector: '[dirC]'})
|
@Directive({selector: '[dirC]'})
|
||||||
class DirectiveC {
|
class DirectiveC {
|
||||||
value = 'DirC';
|
value = 'DirC';
|
||||||
|
|
||||||
constructor() { log.push(this.value); }
|
constructor() { log.push(this.value); }
|
||||||
}
|
}
|
||||||
|
|
||||||
@Directive({selector: '[dirB]'})
|
@Directive({selector: '[dirB]'})
|
||||||
class DirectiveB {
|
class DirectiveB {
|
||||||
constructor(dirA: DirectiveA, dirC: DirectiveC) {
|
constructor(dirA: DirectiveA, dirC: DirectiveC) {
|
||||||
log.push(`DirB (deps: ${dirA.value} and ${dirC.value})`);
|
log.push(`DirB (deps: ${dirA.value} and ${dirC.value})`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Component({template: '<div dirA dirB dirC></div>'})
|
@Component({template: '<div dirA dirB dirC></div>'})
|
||||||
class MyComp {
|
class MyComp {
|
||||||
}
|
}
|
||||||
|
|
||||||
TestBed.configureTestingModule({declarations: [DirectiveA, DirectiveB, DirectiveC, MyComp]});
|
TestBed.configureTestingModule({declarations: [DirectiveA, DirectiveB, DirectiveC, MyComp]});
|
||||||
const fixture = TestBed.createComponent(MyComp);
|
const fixture = TestBed.createComponent(MyComp);
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
|
@ -171,25 +192,33 @@ describe('di', () => {
|
||||||
@Directive({selector: '[dirC]'})
|
@Directive({selector: '[dirC]'})
|
||||||
class DirectiveC {
|
class DirectiveC {
|
||||||
value = 'DirC';
|
value = 'DirC';
|
||||||
|
|
||||||
constructor(dirB: DirectiveB) { log.push(`DirC (dep: ${dirB.value})`); }
|
constructor(dirB: DirectiveB) { log.push(`DirC (dep: ${dirB.value})`); }
|
||||||
}
|
}
|
||||||
|
|
||||||
@Directive({selector: '[dirA]'})
|
@Directive({selector: '[dirA]'})
|
||||||
class DirectiveA {
|
class DirectiveA {
|
||||||
value = 'DirA';
|
value = 'DirA';
|
||||||
|
|
||||||
constructor(dirC: DirectiveC) { log.push(`DirA (dep: ${dirC.value})`); }
|
constructor(dirC: DirectiveC) { log.push(`DirA (dep: ${dirC.value})`); }
|
||||||
}
|
}
|
||||||
|
|
||||||
@Directive({selector: '[dirD]'})
|
@Directive({selector: '[dirD]'})
|
||||||
class DirectiveD {
|
class DirectiveD {
|
||||||
value = 'DirD';
|
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: ''})
|
@Component({selector: 'my-comp', template: ''})
|
||||||
class MyComp {
|
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>'})
|
@Component({template: '<my-comp dirA dirB dirC dirD></my-comp>'})
|
||||||
class MyApp {
|
class MyApp {
|
||||||
}
|
}
|
||||||
|
|
||||||
TestBed.configureTestingModule(
|
TestBed.configureTestingModule(
|
||||||
{declarations: [DirectiveA, DirectiveB, DirectiveC, DirectiveD, MyComp, MyApp]});
|
{declarations: [DirectiveA, DirectiveB, DirectiveC, DirectiveD, MyComp, MyApp]});
|
||||||
const fixture = TestBed.createComponent(MyApp);
|
const fixture = TestBed.createComponent(MyApp);
|
||||||
|
@ -204,12 +233,14 @@ describe('di', () => {
|
||||||
class MyApp {
|
class MyApp {
|
||||||
value = 'App';
|
value = 'App';
|
||||||
}
|
}
|
||||||
|
|
||||||
@Directive({selector: '[dirA]'})
|
@Directive({selector: '[dirA]'})
|
||||||
class DirectiveA {
|
class DirectiveA {
|
||||||
constructor(dirB: DirectiveB, app: MyApp) {
|
constructor(dirB: DirectiveB, app: MyApp) {
|
||||||
log.push(`DirA (deps: ${dirB.value} and ${app.value})`);
|
log.push(`DirA (deps: ${dirB.value} and ${app.value})`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TestBed.configureTestingModule({declarations: [DirectiveA, DirectiveB, MyApp]});
|
TestBed.configureTestingModule({declarations: [DirectiveA, DirectiveB, MyApp]});
|
||||||
const fixture = TestBed.createComponent(MyApp);
|
const fixture = TestBed.createComponent(MyApp);
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
|
@ -219,24 +250,30 @@ describe('di', () => {
|
||||||
|
|
||||||
it('should not use a parent when peer dep is available', () => {
|
it('should not use a parent when peer dep is available', () => {
|
||||||
let count = 1;
|
let count = 1;
|
||||||
|
|
||||||
@Directive({selector: '[dirB]'})
|
@Directive({selector: '[dirB]'})
|
||||||
class DirectiveB {
|
class DirectiveB {
|
||||||
count: number;
|
count: number;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
log.push(`DirB`);
|
log.push(`DirB`);
|
||||||
this.count = count++;
|
this.count = count++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Directive({selector: '[dirA]'})
|
@Directive({selector: '[dirA]'})
|
||||||
class DirectiveA {
|
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>'})
|
@Component({selector: 'my-comp', template: '<div dirA dirB></div>'})
|
||||||
class MyComp {
|
class MyComp {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Component({template: '<my-comp dirB></my-comp>'})
|
@Component({template: '<my-comp dirB></my-comp>'})
|
||||||
class MyApp {
|
class MyApp {
|
||||||
}
|
}
|
||||||
|
|
||||||
TestBed.configureTestingModule({declarations: [DirectiveA, DirectiveB, MyComp, MyApp]});
|
TestBed.configureTestingModule({declarations: [DirectiveA, DirectiveB, MyComp, MyApp]});
|
||||||
const fixture = TestBed.createComponent(MyApp);
|
const fixture = TestBed.createComponent(MyApp);
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
|
@ -249,6 +286,7 @@ describe('di', () => {
|
||||||
@Directive({selector: '[dirA]', exportAs: 'dirA'})
|
@Directive({selector: '[dirA]', exportAs: 'dirA'})
|
||||||
class DirectiveA {
|
class DirectiveA {
|
||||||
injector: Injector;
|
injector: Injector;
|
||||||
|
|
||||||
constructor(public dirB: DirectiveB, public vcr: ViewContainerRef) {
|
constructor(public dirB: DirectiveB, public vcr: ViewContainerRef) {
|
||||||
this.injector = vcr.injector;
|
this.injector = vcr.injector;
|
||||||
}
|
}
|
||||||
|
@ -263,6 +301,7 @@ describe('di', () => {
|
||||||
@Component({template: '<my-comp dirB></my-comp>'})
|
@Component({template: '<my-comp dirB></my-comp>'})
|
||||||
class MyApp {
|
class MyApp {
|
||||||
}
|
}
|
||||||
|
|
||||||
TestBed.configureTestingModule({declarations: [DirectiveA, DirectiveB, MyComp, MyApp]});
|
TestBed.configureTestingModule({declarations: [DirectiveA, DirectiveB, MyComp, MyApp]});
|
||||||
const fixture = TestBed.createComponent(MyApp);
|
const fixture = TestBed.createComponent(MyApp);
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
|
@ -282,6 +321,7 @@ describe('di', () => {
|
||||||
class MyApp {
|
class MyApp {
|
||||||
showing = false;
|
showing = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
TestBed.configureTestingModule({declarations: [DirectiveA, DirectiveB, MyComp, MyApp]});
|
TestBed.configureTestingModule({declarations: [DirectiveA, DirectiveB, MyComp, MyApp]});
|
||||||
const fixture = TestBed.createComponent(MyApp);
|
const fixture = TestBed.createComponent(MyApp);
|
||||||
fixture.componentInstance.showing = true;
|
fixture.componentInstance.showing = true;
|
||||||
|
@ -305,6 +345,7 @@ describe('di', () => {
|
||||||
skipContent = false;
|
skipContent = false;
|
||||||
skipContent2 = false;
|
skipContent2 = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
TestBed.configureTestingModule({declarations: [DirectiveA, DirectiveB, MyApp]});
|
TestBed.configureTestingModule({declarations: [DirectiveA, DirectiveB, MyApp]});
|
||||||
const fixture = TestBed.createComponent(MyApp);
|
const fixture = TestBed.createComponent(MyApp);
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
|
@ -317,10 +358,12 @@ describe('di', () => {
|
||||||
@Directive({selector: '[structuralDir]'})
|
@Directive({selector: '[structuralDir]'})
|
||||||
class StructuralDirective {
|
class StructuralDirective {
|
||||||
@Input() tmp !: TemplateRef<any>;
|
@Input() tmp !: TemplateRef<any>;
|
||||||
|
|
||||||
constructor(public vcr: ViewContainerRef) {}
|
constructor(public vcr: ViewContainerRef) {}
|
||||||
|
|
||||||
create() { this.vcr.createEmbeddedView(this.tmp); }
|
create() { this.vcr.createEmbeddedView(this.tmp); }
|
||||||
}
|
}
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
template: `<div dirB value="declaration">
|
template: `<div dirB value="declaration">
|
||||||
<ng-template #foo>
|
<ng-template #foo>
|
||||||
|
@ -336,6 +379,7 @@ describe('di', () => {
|
||||||
class MyComp {
|
class MyComp {
|
||||||
@ViewChild(StructuralDirective) structuralDir !: StructuralDirective;
|
@ViewChild(StructuralDirective) structuralDir !: StructuralDirective;
|
||||||
}
|
}
|
||||||
|
|
||||||
TestBed.configureTestingModule(
|
TestBed.configureTestingModule(
|
||||||
{declarations: [StructuralDirective, DirectiveA, DirectiveB, MyComp]});
|
{declarations: [StructuralDirective, DirectiveA, DirectiveB, MyComp]});
|
||||||
const fixture = TestBed.createComponent(MyComp);
|
const fixture = TestBed.createComponent(MyComp);
|
||||||
|
@ -356,6 +400,7 @@ describe('di', () => {
|
||||||
})
|
})
|
||||||
class MyApp {
|
class MyApp {
|
||||||
}
|
}
|
||||||
|
|
||||||
TestBed.configureTestingModule({declarations: [DirectiveA, DirectiveB, MyComp, MyApp]});
|
TestBed.configureTestingModule({declarations: [DirectiveA, DirectiveB, MyComp, MyApp]});
|
||||||
const fixture = TestBed.createComponent(MyApp);
|
const fixture = TestBed.createComponent(MyApp);
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
|
@ -379,6 +424,7 @@ describe('di', () => {
|
||||||
@ViewChild(HostBindingDirective) hostBindingDir !: HostBindingDirective;
|
@ViewChild(HostBindingDirective) hostBindingDir !: HostBindingDirective;
|
||||||
@ViewChild(DirectiveA) dirA !: DirectiveA;
|
@ViewChild(DirectiveA) dirA !: DirectiveA;
|
||||||
}
|
}
|
||||||
|
|
||||||
TestBed.configureTestingModule(
|
TestBed.configureTestingModule(
|
||||||
{declarations: [DirectiveA, DirectiveB, HostBindingDirective, MyApp]});
|
{declarations: [DirectiveA, DirectiveB, HostBindingDirective, MyApp]});
|
||||||
const fixture = TestBed.createComponent(MyApp);
|
const fixture = TestBed.createComponent(MyApp);
|
||||||
|
@ -403,13 +449,16 @@ describe('di', () => {
|
||||||
class DirectiveB {
|
class DirectiveB {
|
||||||
constructor() {}
|
constructor() {}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Directive({selector: '[dirA]'})
|
@Directive({selector: '[dirA]'})
|
||||||
class DirectiveA {
|
class DirectiveA {
|
||||||
constructor(siblingDir: DirectiveB) {}
|
constructor(siblingDir: DirectiveB) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Component({template: '<div dirA></div>'})
|
@Component({template: '<div dirA></div>'})
|
||||||
class MyComp {
|
class MyComp {
|
||||||
}
|
}
|
||||||
|
|
||||||
TestBed.configureTestingModule({declarations: [DirectiveA, DirectiveB, MyComp]});
|
TestBed.configureTestingModule({declarations: [DirectiveA, DirectiveB, MyComp]});
|
||||||
expect(() => TestBed.createComponent(MyComp)).toThrowError(/No provider for DirectiveB/);
|
expect(() => TestBed.createComponent(MyComp)).toThrowError(/No provider for DirectiveB/);
|
||||||
});
|
});
|
||||||
|
@ -419,13 +468,16 @@ describe('di', () => {
|
||||||
class DirectiveB {
|
class DirectiveB {
|
||||||
constructor() {}
|
constructor() {}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Directive({selector: '[dirA]'})
|
@Directive({selector: '[dirA]'})
|
||||||
class DirectiveA {
|
class DirectiveA {
|
||||||
constructor(siblingDir: DirectiveB) {}
|
constructor(siblingDir: DirectiveB) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Component({template: '<div dirA></div><div dirB></div>'})
|
@Component({template: '<div dirA></div><div dirB></div>'})
|
||||||
class MyComp {
|
class MyComp {
|
||||||
}
|
}
|
||||||
|
|
||||||
TestBed.configureTestingModule({declarations: [DirectiveA, DirectiveB, MyComp]});
|
TestBed.configureTestingModule({declarations: [DirectiveA, DirectiveB, MyComp]});
|
||||||
expect(() => TestBed.createComponent(MyComp)).toThrowError(/No provider for DirectiveB/);
|
expect(() => TestBed.createComponent(MyComp)).toThrowError(/No provider for DirectiveB/);
|
||||||
});
|
});
|
||||||
|
@ -436,13 +488,16 @@ describe('di', () => {
|
||||||
class DirectiveB {
|
class DirectiveB {
|
||||||
constructor(@Inject(forwardRef(() => DirectiveA)) siblingDir: DirectiveA) {}
|
constructor(@Inject(forwardRef(() => DirectiveA)) siblingDir: DirectiveA) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Directive({selector: '[dirA]'})
|
@Directive({selector: '[dirA]'})
|
||||||
class DirectiveA {
|
class DirectiveA {
|
||||||
constructor(siblingDir: DirectiveB) {}
|
constructor(siblingDir: DirectiveB) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Component({template: '<div dirA dirB></div>'})
|
@Component({template: '<div dirA dirB></div>'})
|
||||||
class MyComp {
|
class MyComp {
|
||||||
}
|
}
|
||||||
|
|
||||||
TestBed.configureTestingModule({declarations: [DirectiveA, DirectiveB, MyComp]});
|
TestBed.configureTestingModule({declarations: [DirectiveA, DirectiveB, MyComp]});
|
||||||
expect(() => TestBed.createComponent(MyComp)).toThrowError(/Circular dep for/);
|
expect(() => TestBed.createComponent(MyComp)).toThrowError(/Circular dep for/);
|
||||||
});
|
});
|
||||||
|
@ -453,12 +508,336 @@ describe('di', () => {
|
||||||
class DirectiveA {
|
class DirectiveA {
|
||||||
constructor(siblingDir: DirectiveA) {}
|
constructor(siblingDir: DirectiveA) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Component({template: '<div dirA></div>'})
|
@Component({template: '<div dirA></div>'})
|
||||||
class MyComp {
|
class MyComp {
|
||||||
}
|
}
|
||||||
|
|
||||||
TestBed.configureTestingModule({declarations: [DirectiveA, DirectiveB, MyComp]});
|
TestBed.configureTestingModule({declarations: [DirectiveA, DirectiveB, MyComp]});
|
||||||
expect(() => TestBed.createComponent(MyComp)).toThrowError(/Circular dep for/);
|
expect(() => TestBed.createComponent(MyComp)).toThrowError(/Circular dep for/);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
TestBed.configureTestingModule({declarations: [DirectiveA, DirectiveB, MyComp]});
|
||||||
|
const fixture = TestBed.createComponent(MyComp);
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
const dirA = fixture.componentInstance.dirA;
|
||||||
|
expect(dirA.dirB).toBeNull();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return null if @Optional dependency has @Self flag', () => {
|
||||||
|
|
||||||
|
@Directive({selector: '[dirC]'})
|
||||||
|
class DirectiveC {
|
||||||
|
constructor(@Optional() @Self() public dirB: DirectiveB) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component({template: '<div dirC></div>'})
|
||||||
|
class MyComp {
|
||||||
|
@ViewChild(DirectiveC) dirC !: DirectiveC;
|
||||||
|
}
|
||||||
|
|
||||||
|
TestBed.configureTestingModule({declarations: [DirectiveC, MyComp]});
|
||||||
|
const fixture = TestBed.createComponent(MyComp);
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
const dirC = fixture.componentInstance.dirC;
|
||||||
|
expect(dirC.dirB).toBeNull();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not throw if dependency is @Optional but defined elsewhere', () => {
|
||||||
|
|
||||||
|
@Directive({selector: '[dirC]'})
|
||||||
|
class DirectiveC {
|
||||||
|
constructor(@Optional() public dirB: DirectiveB) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component({template: '<div dirB></div><div dirC></div>'})
|
||||||
|
class MyComp {
|
||||||
|
@ViewChild(DirectiveC) dirC !: DirectiveC;
|
||||||
|
}
|
||||||
|
|
||||||
|
TestBed.configureTestingModule({declarations: [DirectiveB, DirectiveC, MyComp]});
|
||||||
|
const fixture = TestBed.createComponent(MyComp);
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
const dirC = fixture.componentInstance.dirC;
|
||||||
|
expect(dirC.dirB).toBeNull();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should skip the current node with @SkipSelf', () => {
|
||||||
|
|
||||||
|
@Directive({selector: '[dirA]'})
|
||||||
|
class DirectiveA {
|
||||||
|
constructor(@SkipSelf() public dirB: DirectiveB) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component({selector: 'my-comp', template: '<div dirA dirB="self"></div>'})
|
||||||
|
class MyComp {
|
||||||
|
@ViewChild(DirectiveA) dirA !: DirectiveA;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component({template: '<my-comp dirB="parent"></my-comp>'})
|
||||||
|
class MyApp {
|
||||||
|
@ViewChild(MyComp) myComp !: MyComp;
|
||||||
|
}
|
||||||
|
|
||||||
|
TestBed.configureTestingModule({declarations: [DirectiveA, DirectiveB, MyComp, MyApp]});
|
||||||
|
const fixture = TestBed.createComponent(MyApp);
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
const dirA = fixture.componentInstance.myComp.dirA;
|
||||||
|
expect(dirA.dirB.value).toEqual('parent');
|
||||||
|
});
|
||||||
|
|
||||||
|
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) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component({template: '<div dirB><div dirA></div></div>'})
|
||||||
|
class MyComp {
|
||||||
|
}
|
||||||
|
TestBed.configureTestingModule({declarations: [DirectiveA, DirectiveB, MyComp]});
|
||||||
|
expect(() => TestBed.createComponent(MyComp))
|
||||||
|
.toThrowError(/NodeInjector: NOT_FOUND \[DirectiveB]/);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('@Host', () => {
|
||||||
|
@Directive({selector: '[dirA]'})
|
||||||
|
class DirectiveA {
|
||||||
|
constructor(@Host() public dirB: DirectiveB) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Directive({selector: '[dirString]'})
|
||||||
|
class DirectiveString {
|
||||||
|
constructor(@Host() public s: String) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
it('should find viewProviders on the host itself', () => {
|
||||||
|
@Component({
|
||||||
|
selector: 'my-comp',
|
||||||
|
template: '<div dirString></div>',
|
||||||
|
viewProviders: [{provide: String, useValue: 'Foo'}]
|
||||||
|
})
|
||||||
|
class MyComp {
|
||||||
|
@ViewChild(DirectiveString) dirString !: DirectiveString;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component({template: '<my-comp></my-comp>'})
|
||||||
|
class MyApp {
|
||||||
|
@ViewChild(MyComp) myComp !: MyComp;
|
||||||
|
}
|
||||||
|
|
||||||
|
TestBed.configureTestingModule({declarations: [DirectiveString, MyComp, MyApp]});
|
||||||
|
const fixture = TestBed.createComponent(MyApp);
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
const dirString = fixture.componentInstance.myComp.dirString;
|
||||||
|
expect(dirString.s).toBe('Foo');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should find host component on the host itself', () => {
|
||||||
|
@Directive({selector: '[dirComp]'})
|
||||||
|
class DirectiveComp {
|
||||||
|
constructor(@Inject(forwardRef(() => MyComp)) @Host() public comp: MyComp) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component({selector: 'my-comp', template: '<div dirComp></div>'})
|
||||||
|
class MyComp {
|
||||||
|
@ViewChild(DirectiveComp) dirComp !: DirectiveComp;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component({template: '<my-comp></my-comp>'})
|
||||||
|
class MyApp {
|
||||||
|
@ViewChild(MyComp) myComp !: MyComp;
|
||||||
|
}
|
||||||
|
|
||||||
|
TestBed.configureTestingModule({declarations: [DirectiveComp, MyComp, MyApp]});
|
||||||
|
const fixture = TestBed.createComponent(MyApp);
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
const myComp = fixture.componentInstance.myComp;
|
||||||
|
const dirComp = myComp.dirComp;
|
||||||
|
expect(dirComp.comp).toBe(myComp);
|
||||||
|
});
|
||||||
|
|
||||||
|
onlyInIvy('Ivy has different error message when dependency is not found')
|
||||||
|
.it('should not find providers on the host itself', () => {
|
||||||
|
@Component({
|
||||||
|
selector: 'my-comp',
|
||||||
|
template: '<div dirString></div>',
|
||||||
|
providers: [{provide: String, useValue: 'Foo'}]
|
||||||
|
})
|
||||||
|
class MyComp {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component({template: '<my-comp></my-comp>'})
|
||||||
|
class MyApp {
|
||||||
|
}
|
||||||
|
|
||||||
|
TestBed.configureTestingModule({declarations: [DirectiveString, MyComp, MyApp]});
|
||||||
|
expect(() => TestBed.createComponent(MyApp))
|
||||||
|
.toThrowError(/NodeInjector: NOT_FOUND \[String]/);
|
||||||
|
});
|
||||||
|
|
||||||
|
onlyInIvy('Ivy has different error message when dependency is not found')
|
||||||
|
.it('should not find other directives on the host itself', () => {
|
||||||
|
@Component({selector: 'my-comp', template: '<div dirA></div>'})
|
||||||
|
class MyComp {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component({template: '<my-comp dirB></my-comp>'})
|
||||||
|
class MyApp {
|
||||||
|
}
|
||||||
|
|
||||||
|
TestBed.configureTestingModule(
|
||||||
|
{declarations: [DirectiveA, DirectiveB, MyComp, MyApp]});
|
||||||
|
expect(() => TestBed.createComponent(MyApp))
|
||||||
|
.toThrowError(/NodeInjector: NOT_FOUND \[DirectiveB]/);
|
||||||
|
});
|
||||||
|
|
||||||
|
onlyInIvy('Ivy has different error message when dependency is not found')
|
||||||
|
.it('should not find providers on the host itself if in inline view', () => {
|
||||||
|
@Component({
|
||||||
|
selector: 'my-comp',
|
||||||
|
template: '<ng-container *ngIf="showing"><div dirA></div></ng-container>'
|
||||||
|
})
|
||||||
|
class MyComp {
|
||||||
|
showing = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component({template: '<my-comp dirB></my-comp>'})
|
||||||
|
class MyApp {
|
||||||
|
@ViewChild(MyComp) myComp !: MyComp;
|
||||||
|
}
|
||||||
|
|
||||||
|
TestBed.configureTestingModule(
|
||||||
|
{declarations: [DirectiveA, DirectiveB, MyComp, MyApp]});
|
||||||
|
const fixture = TestBed.createComponent(MyApp);
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(() => {
|
||||||
|
fixture.componentInstance.myComp.showing = true;
|
||||||
|
fixture.detectChanges();
|
||||||
|
}).toThrowError(/NodeInjector: NOT_FOUND \[DirectiveB]/);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should find providers across embedded views if not passing component boundary', () => {
|
||||||
|
@Component({template: '<div dirB><div *ngIf="showing" dirA></div></div>'})
|
||||||
|
class MyApp {
|
||||||
|
showing = false;
|
||||||
|
@ViewChild(DirectiveA) dirA !: DirectiveA;
|
||||||
|
@ViewChild(DirectiveB) dirB !: DirectiveB;
|
||||||
|
}
|
||||||
|
|
||||||
|
TestBed.configureTestingModule({declarations: [DirectiveA, DirectiveB, MyApp]});
|
||||||
|
const fixture = TestBed.createComponent(MyApp);
|
||||||
|
fixture.detectChanges();
|
||||||
|
fixture.componentInstance.showing = true;
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
const dirA = fixture.componentInstance.dirA;
|
||||||
|
const dirB = fixture.componentInstance.dirB;
|
||||||
|
expect(dirA.dirB).toBe(dirB);
|
||||||
|
});
|
||||||
|
|
||||||
|
onlyInIvy('Ivy has different error message when dependency is not found')
|
||||||
|
.it('should not find component above the host', () => {
|
||||||
|
@Directive({selector: '[dirComp]'})
|
||||||
|
class DirectiveComp {
|
||||||
|
constructor(@Inject(forwardRef(() => MyApp)) @Host() public comp: MyApp) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component({selector: 'my-comp', template: '<div dirComp></div>'})
|
||||||
|
class MyComp {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component({template: '<my-comp></my-comp>'})
|
||||||
|
class MyApp {
|
||||||
|
}
|
||||||
|
|
||||||
|
TestBed.configureTestingModule({declarations: [DirectiveComp, MyComp, MyApp]});
|
||||||
|
expect(() => TestBed.createComponent(MyApp))
|
||||||
|
.toThrowError(/NodeInjector: NOT_FOUND \[MyApp]/);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('regression', () => {
|
||||||
|
// based on https://stackblitz.com/edit/angular-riss8k?file=src/app/app.component.ts
|
||||||
|
it('should allow directives with Host flag to inject view providers from containing component',
|
||||||
|
() => {
|
||||||
|
class ControlContainer {}
|
||||||
|
let controlContainers: ControlContainer[] = [];
|
||||||
|
let injectedControlContainer: ControlContainer|null = null;
|
||||||
|
|
||||||
|
@Directive({
|
||||||
|
selector: '[group]',
|
||||||
|
providers: [{provide: ControlContainer, useExisting: GroupDirective}]
|
||||||
|
})
|
||||||
|
class GroupDirective {
|
||||||
|
constructor() { controlContainers.push(this); }
|
||||||
|
}
|
||||||
|
|
||||||
|
@Directive({selector: '[control]'})
|
||||||
|
class ControlDirective {
|
||||||
|
constructor(@Host() @SkipSelf() @Inject(ControlContainer) parent:
|
||||||
|
ControlContainer) {
|
||||||
|
injectedControlContainer = parent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'my-comp',
|
||||||
|
template: '<input control>',
|
||||||
|
viewProviders: [{provide: ControlContainer, useExisting: GroupDirective}]
|
||||||
|
})
|
||||||
|
class MyComp {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
template: `
|
||||||
|
<div group>
|
||||||
|
<my-comp></my-comp>
|
||||||
|
</div>
|
||||||
|
`
|
||||||
|
})
|
||||||
|
class MyApp {
|
||||||
|
}
|
||||||
|
|
||||||
|
TestBed.configureTestingModule(
|
||||||
|
{declarations: [GroupDirective, ControlDirective, MyComp, MyApp]});
|
||||||
|
const fixture = TestBed.createComponent(MyApp);
|
||||||
|
expect(fixture.nativeElement.innerHTML)
|
||||||
|
.toBe('<div group=""><my-comp><input control=""></my-comp></div>');
|
||||||
|
expect(controlContainers).toEqual([injectedControlContainer !]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('service injection', () => {
|
describe('service injection', () => {
|
||||||
|
|
|
@ -6,22 +6,20 @@
|
||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {ChangeDetectorRef, Host, Inject, InjectFlags, Injector, Optional, Renderer2, Self, SkipSelf, ViewContainerRef, ɵɵdefineInjector} from '@angular/core';
|
import {ChangeDetectorRef, Host, InjectFlags, Injector, Optional, Renderer2, Self, ViewContainerRef} from '@angular/core';
|
||||||
import {createLView, createNodeAtIndex, createTView} from '@angular/core/src/render3/instructions/shared';
|
import {createLView, createNodeAtIndex, createTView} from '@angular/core/src/render3/instructions/shared';
|
||||||
import {ComponentType, RenderFlags} from '@angular/core/src/render3/interfaces/definition';
|
import {RenderFlags} from '@angular/core/src/render3/interfaces/definition';
|
||||||
|
|
||||||
import {createInjector} from '../../src/di/r3_injector';
|
|
||||||
import {ɵɵdefineComponent} from '../../src/render3/definition';
|
import {ɵɵdefineComponent} from '../../src/render3/definition';
|
||||||
import {bloomAdd, bloomHasToken, bloomHashBitOrFactory as bloomHash, getOrCreateNodeInjectorForNode} from '../../src/render3/di';
|
import {bloomAdd, bloomHasToken, bloomHashBitOrFactory as bloomHash, getOrCreateNodeInjectorForNode} from '../../src/render3/di';
|
||||||
import {ɵɵProvidersFeature, ɵɵbind, ɵɵcontainer, ɵɵcontainerRefreshEnd, ɵɵcontainerRefreshStart, ɵɵdefineDirective, ɵɵdirectiveInject, ɵɵelement, ɵɵelementEnd, ɵɵelementProperty, ɵɵelementStart, ɵɵembeddedViewEnd, ɵɵembeddedViewStart, ɵɵinterpolation2, ɵɵprojection, ɵɵprojectionDef, ɵɵreference, ɵɵtemplate, ɵɵtext, ɵɵtextBinding} from '../../src/render3/index';
|
import {ɵɵbind, ɵɵcontainer, ɵɵcontainerRefreshEnd, ɵɵcontainerRefreshStart, ɵɵdefineDirective, ɵɵdirectiveInject, ɵɵelement, ɵɵelementEnd, ɵɵelementStart, ɵɵembeddedViewEnd, ɵɵembeddedViewStart, ɵɵinterpolation2, ɵɵprojection, ɵɵprojectionDef, ɵɵreference, ɵɵtext, ɵɵtextBinding} from '../../src/render3/index';
|
||||||
import {TNODE} from '../../src/render3/interfaces/injector';
|
import {TNODE} from '../../src/render3/interfaces/injector';
|
||||||
import {AttributeMarker, TNodeType} from '../../src/render3/interfaces/node';
|
import {TNodeType} from '../../src/render3/interfaces/node';
|
||||||
import {isProceduralRenderer} from '../../src/render3/interfaces/renderer';
|
import {isProceduralRenderer} from '../../src/render3/interfaces/renderer';
|
||||||
import {LViewFlags} from '../../src/render3/interfaces/view';
|
import {LViewFlags} from '../../src/render3/interfaces/view';
|
||||||
import {enterView, leaveView} from '../../src/render3/state';
|
import {enterView, leaveView} from '../../src/render3/state';
|
||||||
import {ViewRef} from '../../src/render3/view_ref';
|
import {ViewRef} from '../../src/render3/view_ref';
|
||||||
|
|
||||||
import {NgIf} from './common_with_def';
|
|
||||||
import {getRendererFactory2} from './imported_renderer2';
|
import {getRendererFactory2} from './imported_renderer2';
|
||||||
import {ComponentFixture, createComponent, createDirective, getDirectiveOnNode, renderComponent, toHtml} from './render_util';
|
import {ComponentFixture, createComponent, createDirective, getDirectiveOnNode, renderComponent, toHtml} from './render_util';
|
||||||
|
|
||||||
|
@ -207,142 +205,6 @@ describe('di', () => {
|
||||||
expect(() => { new ComponentFixture(App); }).not.toThrow();
|
expect(() => { new ComponentFixture(App); }).not.toThrow();
|
||||||
expect(dirA !.dirB).toEqual(null);
|
expect(dirA !.dirB).toEqual(null);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not throw if dependency is @Optional (module injector)', () => {
|
|
||||||
class SomeModule {
|
|
||||||
static ngInjectorDef = ɵɵdefineInjector({factory: () => new SomeModule()});
|
|
||||||
}
|
|
||||||
|
|
||||||
/** <div dirA></div> */
|
|
||||||
const App = createComponent('app', function(rf: RenderFlags, ctx: any) {
|
|
||||||
if (rf & RenderFlags.Create) {
|
|
||||||
ɵɵelement(0, 'div', ['dirA', '']);
|
|
||||||
}
|
|
||||||
}, 1, 0, [DirA, DirB]);
|
|
||||||
|
|
||||||
expect(() => {
|
|
||||||
const injector = createInjector(SomeModule);
|
|
||||||
new ComponentFixture(App, {injector});
|
|
||||||
}).not.toThrow();
|
|
||||||
expect(dirA !.dirB).toEqual(null);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should return null if @Optional dependency has @Self flag', () => {
|
|
||||||
let dirC !: DirC;
|
|
||||||
|
|
||||||
class DirC {
|
|
||||||
constructor(@Optional() @Self() public dirB: DirB|null) {}
|
|
||||||
|
|
||||||
static ngDirectiveDef = ɵɵdefineDirective({
|
|
||||||
type: DirC,
|
|
||||||
selectors: [['', 'dirC', '']],
|
|
||||||
factory: () => dirC =
|
|
||||||
new DirC(ɵɵdirectiveInject(DirB, InjectFlags.Optional|InjectFlags.Self))
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/** <div dirC></div> */
|
|
||||||
const App = createComponent('app', function(rf: RenderFlags, ctx: any) {
|
|
||||||
if (rf & RenderFlags.Create) {
|
|
||||||
ɵɵelement(0, 'div', ['dirC', '']);
|
|
||||||
}
|
|
||||||
}, 1, 0, [DirC, DirB]);
|
|
||||||
|
|
||||||
expect(() => { new ComponentFixture(App); }).not.toThrow();
|
|
||||||
expect(dirC !.dirB).toEqual(null);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should not throw if dependency is @Optional but defined elsewhere', () => {
|
|
||||||
let dirA: DirA;
|
|
||||||
|
|
||||||
class DirA {
|
|
||||||
constructor(@Optional() public dirB: DirB|null) {}
|
|
||||||
|
|
||||||
static ngDirectiveDef = ɵɵdefineDirective({
|
|
||||||
type: DirA,
|
|
||||||
selectors: [['', 'dirA', '']],
|
|
||||||
factory: () => dirA = new DirA(ɵɵdirectiveInject(DirB, InjectFlags.Optional))
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* <div dirB></div>
|
|
||||||
* <div dirA></div>
|
|
||||||
*/
|
|
||||||
const App = createComponent('app', function(rf: RenderFlags, ctx: any) {
|
|
||||||
if (rf & RenderFlags.Create) {
|
|
||||||
ɵɵelement(0, 'div', ['dirB', '']);
|
|
||||||
ɵɵelement(1, 'div', ['dirA', '']);
|
|
||||||
}
|
|
||||||
}, 2, 0, [DirA, DirB]);
|
|
||||||
|
|
||||||
expect(() => {
|
|
||||||
new ComponentFixture(App);
|
|
||||||
expect(dirA !.dirB).toEqual(null);
|
|
||||||
}).not.toThrow();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should skip the current node with @SkipSelf', () => {
|
|
||||||
let dirA: DirA;
|
|
||||||
|
|
||||||
class DirA {
|
|
||||||
constructor(@SkipSelf() public dirB: DirB) {}
|
|
||||||
|
|
||||||
static ngDirectiveDef = ɵɵdefineDirective({
|
|
||||||
type: DirA,
|
|
||||||
selectors: [['', 'dirA', '']],
|
|
||||||
factory: () => dirA = new DirA(ɵɵdirectiveInject(DirB, InjectFlags.SkipSelf))
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/** <div dirA dirB="self"></div> */
|
|
||||||
const Comp = createComponent('comp', function(rf: RenderFlags, ctx: any) {
|
|
||||||
if (rf & RenderFlags.Create) {
|
|
||||||
ɵɵelement(0, 'div', ['dirA', '', 'dirB', 'self']);
|
|
||||||
}
|
|
||||||
}, 1, 0, [DirA, DirB]);
|
|
||||||
|
|
||||||
/* <comp dirB="parent"></comp> */
|
|
||||||
const App = createComponent('app', function(rf: RenderFlags, ctx: any) {
|
|
||||||
if (rf & RenderFlags.Create) {
|
|
||||||
ɵɵelement(0, 'comp', ['dirB', 'parent']);
|
|
||||||
}
|
|
||||||
}, 1, 0, [Comp, DirB]);
|
|
||||||
|
|
||||||
new ComponentFixture(App);
|
|
||||||
expect(dirA !.dirB.value).toEqual('parent');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should check only the current node with @Self', () => {
|
|
||||||
let dirA: DirA;
|
|
||||||
|
|
||||||
class DirA {
|
|
||||||
constructor(@Self() public dirB: DirB) {}
|
|
||||||
|
|
||||||
static ngDirectiveDef = ɵɵdefineDirective({
|
|
||||||
type: DirA,
|
|
||||||
selectors: [['', 'dirA', '']],
|
|
||||||
factory: () => dirA = new DirA(ɵɵdirectiveInject(DirB, InjectFlags.Self))
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* <div dirB>
|
|
||||||
* <div dirA></div>
|
|
||||||
* </div>
|
|
||||||
*/
|
|
||||||
const App = createComponent('app', function(rf: RenderFlags, ctx: any) {
|
|
||||||
if (rf & RenderFlags.Create) {
|
|
||||||
ɵɵelementStart(0, 'div', ['dirB', '']);
|
|
||||||
ɵɵelement(1, 'div', ['dirA', '']);
|
|
||||||
ɵɵelementEnd();
|
|
||||||
}
|
|
||||||
}, 2, 0, [DirA, DirB]);
|
|
||||||
|
|
||||||
expect(() => {
|
|
||||||
new ComponentFixture(App);
|
|
||||||
}).toThrowError(/NodeInjector: NOT_FOUND \[DirB\]/);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should check only the current node with @Self even with false positive', () => {
|
it('should check only the current node with @Self even with false positive', () => {
|
||||||
|
@ -382,12 +244,8 @@ describe('di', () => {
|
||||||
|
|
||||||
describe('@Host', () => {
|
describe('@Host', () => {
|
||||||
let dirA: DirA|null = null;
|
let dirA: DirA|null = null;
|
||||||
let dirString: DirString|null = null;
|
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => { dirA = null; });
|
||||||
dirA = null;
|
|
||||||
dirString = null;
|
|
||||||
});
|
|
||||||
|
|
||||||
class DirA {
|
class DirA {
|
||||||
constructor(@Host() public dirB: DirB) {}
|
constructor(@Host() public dirB: DirB) {}
|
||||||
|
@ -399,106 +257,10 @@ describe('di', () => {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
class DirString {
|
/**
|
||||||
constructor(@Host() public s: String) {}
|
* This test needs to be moved to acceptance/di_spec.ts
|
||||||
|
* when Ivy compiler supports inline views.
|
||||||
static ngDirectiveDef = ɵɵdefineDirective({
|
*/
|
||||||
type: DirString,
|
|
||||||
selectors: [['', 'dirString', '']],
|
|
||||||
factory: () => dirString = new DirString(ɵɵdirectiveInject(String, InjectFlags.Host))
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
it('should find viewProviders on the host itself', () => {
|
|
||||||
/** <div dirString></div> */
|
|
||||||
const Comp = createComponent('comp', function(rf: RenderFlags, ctx: any) {
|
|
||||||
if (rf & RenderFlags.Create) {
|
|
||||||
ɵɵelement(0, 'div', ['dirString', '']);
|
|
||||||
}
|
|
||||||
}, 1, 0, [DirString], [], null, [], [{provide: String, useValue: 'Foo'}]);
|
|
||||||
|
|
||||||
/* <comp></comp> */
|
|
||||||
const App = createComponent('app', function(rf: RenderFlags, ctx: any) {
|
|
||||||
if (rf & RenderFlags.Create) {
|
|
||||||
ɵɵelement(0, 'comp');
|
|
||||||
}
|
|
||||||
}, 1, 0, [Comp]);
|
|
||||||
|
|
||||||
new ComponentFixture(App);
|
|
||||||
expect(dirString !.s).toEqual('Foo');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should find host component on the host itself', () => {
|
|
||||||
let dirComp: DirComp|null = null;
|
|
||||||
|
|
||||||
class DirComp {
|
|
||||||
constructor(@Host() public comp: any) {}
|
|
||||||
|
|
||||||
static ngDirectiveDef = ɵɵdefineDirective({
|
|
||||||
type: DirComp,
|
|
||||||
selectors: [['', 'dirCmp', '']],
|
|
||||||
factory: () => dirComp = new DirComp(ɵɵdirectiveInject(Comp, InjectFlags.Host))
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/** <div dirCmp></div> */
|
|
||||||
const Comp = createComponent('comp', function(rf: RenderFlags, ctx: any) {
|
|
||||||
if (rf & RenderFlags.Create) {
|
|
||||||
ɵɵelement(0, 'div', ['dirCmp', '']);
|
|
||||||
}
|
|
||||||
}, 1, 0, [DirComp]);
|
|
||||||
|
|
||||||
/* <comp></comp> */
|
|
||||||
const App = createComponent('app', function(rf: RenderFlags, ctx: any) {
|
|
||||||
if (rf & RenderFlags.Create) {
|
|
||||||
ɵɵelement(0, 'comp');
|
|
||||||
}
|
|
||||||
}, 1, 0, [Comp]);
|
|
||||||
|
|
||||||
new ComponentFixture(App);
|
|
||||||
expect(dirComp !.comp instanceof Comp).toBeTruthy();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should not find providers on the host itself', () => {
|
|
||||||
/** <div dirString></div> */
|
|
||||||
const Comp = createComponent('comp', function(rf: RenderFlags, ctx: any) {
|
|
||||||
if (rf & RenderFlags.Create) {
|
|
||||||
ɵɵelement(0, 'div', ['dirString', '']);
|
|
||||||
}
|
|
||||||
}, 1, 0, [DirString], [], null, [{provide: String, useValue: 'Foo'}]);
|
|
||||||
|
|
||||||
/* <comp></comp> */
|
|
||||||
const App = createComponent('app', function(rf: RenderFlags, ctx: any) {
|
|
||||||
if (rf & RenderFlags.Create) {
|
|
||||||
ɵɵelement(0, 'comp');
|
|
||||||
}
|
|
||||||
}, 1, 0, [Comp]);
|
|
||||||
|
|
||||||
expect(() => {
|
|
||||||
new ComponentFixture(App);
|
|
||||||
}).toThrowError(/NodeInjector: NOT_FOUND \[String\]/);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should not find other directives on the host itself', () => {
|
|
||||||
/** <div dirA></div> */
|
|
||||||
const Comp = createComponent('comp', function(rf: RenderFlags, ctx: any) {
|
|
||||||
if (rf & RenderFlags.Create) {
|
|
||||||
ɵɵelement(0, 'div', ['dirA', '']);
|
|
||||||
}
|
|
||||||
}, 1, 0, [DirA]);
|
|
||||||
|
|
||||||
/* <comp dirB></comp> */
|
|
||||||
const App = createComponent('app', function(rf: RenderFlags, ctx: any) {
|
|
||||||
if (rf & RenderFlags.Create) {
|
|
||||||
ɵɵelement(0, 'comp', ['dirB', '']);
|
|
||||||
}
|
|
||||||
}, 1, 0, [Comp, DirB]);
|
|
||||||
|
|
||||||
expect(() => {
|
|
||||||
new ComponentFixture(App);
|
|
||||||
}).toThrowError(/NodeInjector: NOT_FOUND \[DirB\]/);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should not find providers on the host itself if in inline view', () => {
|
it('should not find providers on the host itself if in inline view', () => {
|
||||||
let comp !: any;
|
let comp !: any;
|
||||||
|
|
||||||
|
@ -542,192 +304,6 @@ describe('di', () => {
|
||||||
fixture.update();
|
fixture.update();
|
||||||
}).toThrowError(/NodeInjector: NOT_FOUND \[DirB\]/);
|
}).toThrowError(/NodeInjector: NOT_FOUND \[DirB\]/);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should find providers across embedded views if not passing component boundary', () => {
|
|
||||||
let dirB !: DirB;
|
|
||||||
|
|
||||||
function IfTemplate(rf: RenderFlags, ctx: any) {
|
|
||||||
if (rf & RenderFlags.Create) {
|
|
||||||
ɵɵelement(0, 'div', ['dirA', '']);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* <div dirB>
|
|
||||||
* <div *ngIf="showing" dirA></div>
|
|
||||||
* </div>
|
|
||||||
*/
|
|
||||||
const App = createComponent('app', function(rf: RenderFlags, ctx: any) {
|
|
||||||
if (rf & RenderFlags.Create) {
|
|
||||||
ɵɵelementStart(0, 'div', ['dirB', '']);
|
|
||||||
{
|
|
||||||
ɵɵtemplate(
|
|
||||||
1, IfTemplate, 1, 0, 'div', ['dirA', '', AttributeMarker.Template, 'ngIf']);
|
|
||||||
}
|
|
||||||
ɵɵelementEnd();
|
|
||||||
}
|
|
||||||
if (rf & RenderFlags.Update) {
|
|
||||||
ɵɵelementProperty(1, 'ngIf', ɵɵbind(ctx.showing));
|
|
||||||
|
|
||||||
// testing only
|
|
||||||
dirB = getDirectiveOnNode(0);
|
|
||||||
}
|
|
||||||
}, 2, 1, [NgIf, DirA, DirB]);
|
|
||||||
|
|
||||||
const fixture = new ComponentFixture(App);
|
|
||||||
fixture.component.showing = true;
|
|
||||||
fixture.update();
|
|
||||||
|
|
||||||
expect(dirA !.dirB).toEqual(dirB);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should not find component above the host', () => {
|
|
||||||
let dirComp: DirComp|null = null;
|
|
||||||
|
|
||||||
class DirComp {
|
|
||||||
constructor(@Host() public comp: any) {}
|
|
||||||
|
|
||||||
static ngDirectiveDef = ɵɵdefineDirective({
|
|
||||||
type: DirComp,
|
|
||||||
selectors: [['', 'dirCmp', '']],
|
|
||||||
factory: () => dirComp = new DirComp(ɵɵdirectiveInject(App, InjectFlags.Host))
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/** <div dirCmp></div> */
|
|
||||||
const Comp = createComponent('comp', function(rf: RenderFlags, ctx: any) {
|
|
||||||
if (rf & RenderFlags.Create) {
|
|
||||||
ɵɵelement(0, 'div', ['dirCmp', '']);
|
|
||||||
}
|
|
||||||
}, 1, 0, [DirComp]);
|
|
||||||
|
|
||||||
/* <comp></comp> */
|
|
||||||
class App {
|
|
||||||
static ngComponentDef = ɵɵdefineComponent({
|
|
||||||
type: App,
|
|
||||||
selectors: [['app']],
|
|
||||||
consts: 1,
|
|
||||||
vars: 0,
|
|
||||||
factory: () => new App,
|
|
||||||
template: function(rf: RenderFlags, ctx: any) {
|
|
||||||
if (rf & RenderFlags.Create) {
|
|
||||||
ɵɵelement(0, 'comp');
|
|
||||||
}
|
|
||||||
},
|
|
||||||
directives: [Comp],
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
expect(() => {
|
|
||||||
new ComponentFixture(App);
|
|
||||||
}).toThrowError(/NodeInjector: NOT_FOUND \[App\]/);
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('regression', () => {
|
|
||||||
// based on https://stackblitz.com/edit/angular-riss8k?file=src/app/app.component.ts
|
|
||||||
it('should allow directives with Host flag to inject view providers from containing component',
|
|
||||||
() => {
|
|
||||||
let controlContainers: ControlContainer[] = [];
|
|
||||||
let injectedControlContainer: ControlContainer|null = null;
|
|
||||||
|
|
||||||
class ControlContainer {}
|
|
||||||
|
|
||||||
/*
|
|
||||||
@Directive({
|
|
||||||
selector: '[group]',
|
|
||||||
providers: [{provide: ControlContainer, useExisting: GroupDirective}]
|
|
||||||
})
|
|
||||||
*/
|
|
||||||
class GroupDirective {
|
|
||||||
constructor() { controlContainers.push(this); }
|
|
||||||
|
|
||||||
static ngDirectiveDef = ɵɵdefineDirective({
|
|
||||||
type: GroupDirective,
|
|
||||||
selectors: [['', 'group', '']],
|
|
||||||
factory: () => new GroupDirective(),
|
|
||||||
features: [ɵɵProvidersFeature(
|
|
||||||
[{provide: ControlContainer, useExisting: GroupDirective}])],
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// @Directive({selector: '[controlName]'})
|
|
||||||
class ControlNameDirective {
|
|
||||||
constructor(@Host() @SkipSelf() @Inject(ControlContainer) parent:
|
|
||||||
ControlContainer) {
|
|
||||||
injectedControlContainer = parent;
|
|
||||||
}
|
|
||||||
|
|
||||||
static ngDirectiveDef = ɵɵdefineDirective({
|
|
||||||
type: ControlNameDirective,
|
|
||||||
selectors: [['', 'controlName', '']],
|
|
||||||
factory: () => new ControlNameDirective(ɵɵdirectiveInject(
|
|
||||||
ControlContainer, InjectFlags.Host|InjectFlags.SkipSelf))
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
@Component({
|
|
||||||
selector: 'child',
|
|
||||||
template: `
|
|
||||||
<input controlName type="text">
|
|
||||||
`,
|
|
||||||
viewProviders: [{provide: ControlContainer, useExisting: GroupDirective}]
|
|
||||||
})
|
|
||||||
*/
|
|
||||||
class ChildComponent {
|
|
||||||
static ngComponentDef = ɵɵdefineComponent({
|
|
||||||
type: ChildComponent,
|
|
||||||
selectors: [['child']],
|
|
||||||
consts: 1,
|
|
||||||
vars: 0,
|
|
||||||
factory: () => new ChildComponent(),
|
|
||||||
template: function(rf: RenderFlags, ctx: ChildComponent) {
|
|
||||||
if (rf & RenderFlags.Create) {
|
|
||||||
ɵɵelement(0, 'input', ['controlName', '', 'type', 'text']);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
directives: [ControlNameDirective],
|
|
||||||
features: [ɵɵProvidersFeature(
|
|
||||||
[], [{provide: ControlContainer, useExisting: GroupDirective}])],
|
|
||||||
});
|
|
||||||
}
|
|
||||||
/*
|
|
||||||
@Component({
|
|
||||||
selector: 'my-app',
|
|
||||||
template: `
|
|
||||||
<div group>
|
|
||||||
<child></child>
|
|
||||||
</div>
|
|
||||||
`
|
|
||||||
})
|
|
||||||
*/
|
|
||||||
class AppComponent {
|
|
||||||
static ngComponentDef = ɵɵdefineComponent({
|
|
||||||
type: AppComponent,
|
|
||||||
selectors: [['my-app']],
|
|
||||||
consts: 2,
|
|
||||||
vars: 0,
|
|
||||||
factory: () => new AppComponent(),
|
|
||||||
template: function(rf: RenderFlags, ctx: AppComponent) {
|
|
||||||
if (rf & RenderFlags.Create) {
|
|
||||||
ɵɵelementStart(0, 'div', ['group', '']);
|
|
||||||
ɵɵelement(1, 'child');
|
|
||||||
ɵɵelementEnd();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
directives: [ChildComponent, GroupDirective]
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const fixture = new ComponentFixture(AppComponent as ComponentType<AppComponent>);
|
|
||||||
expect(fixture.html)
|
|
||||||
.toEqual(
|
|
||||||
'<div group=""><child><input controlname="" type="text"></child></div>');
|
|
||||||
|
|
||||||
expect(controlContainers).toEqual([injectedControlContainer !]);
|
|
||||||
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -781,7 +357,7 @@ describe('di', () => {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const directives = [MyComp, Directive, DirectiveSameInstance, NgIf];
|
const directives = [MyComp, Directive, DirectiveSameInstance];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This test needs to be moved to acceptance/di_spec.ts
|
* This test needs to be moved to acceptance/di_spec.ts
|
||||||
|
|
Loading…
Reference in New Issue