diff --git a/packages/core/test/acceptance/di_spec.ts b/packages/core/test/acceptance/di_spec.ts
index 6df8bb54a7..2c4f516c10 100644
--- a/packages/core/test/acceptance/di_spec.ts
+++ b/packages/core/test/acceptance/di_spec.ts
@@ -7,7 +7,7 @@
*/
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 {TestBed} from '@angular/core/testing';
import {onlyInIvy} from '@angular/private/testing';
@@ -38,6 +38,7 @@ describe('di', () => {
@Directive({selector: '[dirB]', exportAs: 'dirB'})
class DirectiveB {
@Input() value = 'DirB';
+
constructor() { log.push(this.value); }
}
@@ -48,11 +49,14 @@ describe('di', () => {
class DirectiveA {
value = 'DirA';
}
+
@Directive({selector: '[dirC]', exportAs: 'dirC'})
class DirectiveC {
value: string;
+
constructor(dirA: DirectiveA, dirB: DirectiveB) { this.value = dirA.value + dirB.value; }
}
+
@Component({
template: `
@@ -62,6 +66,7 @@ describe('di', () => {
})
class MyComp {
}
+
TestBed.configureTestingModule({declarations: [DirectiveA, DirectiveB, DirectiveC, MyComp]});
const fixture = TestBed.createComponent(MyComp);
fixture.detectChanges();
@@ -74,11 +79,14 @@ describe('di', () => {
@Directive({selector: '[dirA]'})
class DirectiveA {
value = 'dirA';
+
constructor(dirB: DirectiveB) { log.push(`DirA (dep: ${dirB.value})`); }
}
+
@Component({template: '
'})
class MyComp {
}
+
TestBed.configureTestingModule({declarations: [DirectiveA, DirectiveB, MyComp]});
const fixture = TestBed.createComponent(MyComp);
fixture.detectChanges();
@@ -90,14 +98,17 @@ describe('di', () => {
@Directive({selector: '[dirA]'})
class DirectiveA {
value = 'dirA';
+
constructor(dirB: DirectiveB) { log.push(`DirA (dep: ${dirB.value})`); }
}
+
// - dirB is know to the node injectors
// - 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
@Component({template: '
'})
class MyComp {
}
+
TestBed.configureTestingModule({
declarations: [DirectiveA, DirectiveB, MyComp],
providers: [{provide: DirectiveB, useValue: {value: 'module'}}]
@@ -113,9 +124,11 @@ describe('di', () => {
class MyComp {
constructor(dirB: DirectiveB) { log.push(`Comp (dep: ${dirB.value})`); }
}
+
@Component({template: '
'})
class MyApp {
}
+
TestBed.configureTestingModule({declarations: [DirectiveB, MyComp, MyApp]});
const fixture = TestBed.createComponent(MyApp);
fixture.detectChanges();
@@ -128,10 +141,12 @@ describe('di', () => {
class DirectiveA {
constructor(dir: DirectiveB) { log.push(`DirA (dep: ${dir.value})`); }
}
+
@Component({template: '
'})
class MyComp {
array = [1, 2, 3];
}
+
TestBed.configureTestingModule({declarations: [DirectiveA, DirectiveB, MyComp]});
const fixture = TestBed.createComponent(MyComp);
fixture.detectChanges();
@@ -144,22 +159,28 @@ describe('di', () => {
@Directive({selector: '[dirA]'})
class DirectiveA {
value = 'DirA';
+
constructor() { log.push(this.value); }
}
+
@Directive({selector: '[dirC]'})
class DirectiveC {
value = 'DirC';
+
constructor() { log.push(this.value); }
}
+
@Directive({selector: '[dirB]'})
class DirectiveB {
constructor(dirA: DirectiveA, dirC: DirectiveC) {
log.push(`DirB (deps: ${dirA.value} and ${dirC.value})`);
}
}
+
@Component({template: '
'})
class MyComp {
}
+
TestBed.configureTestingModule({declarations: [DirectiveA, DirectiveB, DirectiveC, MyComp]});
const fixture = TestBed.createComponent(MyComp);
fixture.detectChanges();
@@ -171,25 +192,33 @@ describe('di', () => {
@Directive({selector: '[dirC]'})
class DirectiveC {
value = 'DirC';
+
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})`); }
}
+
@Directive({selector: '[dirD]'})
class DirectiveD {
value = 'DirD';
+
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})`); }
}
+
@Component({template: '
'})
class MyApp {
}
+
TestBed.configureTestingModule(
{declarations: [DirectiveA, DirectiveB, DirectiveC, DirectiveD, MyComp, MyApp]});
const fixture = TestBed.createComponent(MyApp);
@@ -204,12 +233,14 @@ describe('di', () => {
class MyApp {
value = 'App';
}
+
@Directive({selector: '[dirA]'})
class DirectiveA {
constructor(dirB: DirectiveB, app: MyApp) {
log.push(`DirA (deps: ${dirB.value} and ${app.value})`);
}
}
+
TestBed.configureTestingModule({declarations: [DirectiveA, DirectiveB, MyApp]});
const fixture = TestBed.createComponent(MyApp);
fixture.detectChanges();
@@ -219,24 +250,30 @@ describe('di', () => {
it('should not use a parent when peer dep is available', () => {
let count = 1;
+
@Directive({selector: '[dirB]'})
class DirectiveB {
count: number;
+
constructor() {
log.push(`DirB`);
this.count = count++;
}
}
+
@Directive({selector: '[dirA]'})
class DirectiveA {
constructor(dirB: DirectiveB) { log.push(`DirA (dep: DirB - ${dirB.count})`); }
}
+
@Component({selector: 'my-comp', template: '
'})
class MyComp {
}
+
@Component({template: '
'})
class MyApp {
}
+
TestBed.configureTestingModule({declarations: [DirectiveA, DirectiveB, MyComp, MyApp]});
const fixture = TestBed.createComponent(MyApp);
fixture.detectChanges();
@@ -249,6 +286,7 @@ describe('di', () => {
@Directive({selector: '[dirA]', exportAs: 'dirA'})
class DirectiveA {
injector: Injector;
+
constructor(public dirB: DirectiveB, public vcr: ViewContainerRef) {
this.injector = vcr.injector;
}
@@ -263,6 +301,7 @@ describe('di', () => {
@Component({template: '
'})
class MyApp {
}
+
TestBed.configureTestingModule({declarations: [DirectiveA, DirectiveB, MyComp, MyApp]});
const fixture = TestBed.createComponent(MyApp);
fixture.detectChanges();
@@ -282,6 +321,7 @@ describe('di', () => {
class MyApp {
showing = false;
}
+
TestBed.configureTestingModule({declarations: [DirectiveA, DirectiveB, MyComp, MyApp]});
const fixture = TestBed.createComponent(MyApp);
fixture.componentInstance.showing = true;
@@ -305,6 +345,7 @@ describe('di', () => {
skipContent = false;
skipContent2 = false;
}
+
TestBed.configureTestingModule({declarations: [DirectiveA, DirectiveB, MyApp]});
const fixture = TestBed.createComponent(MyApp);
fixture.detectChanges();
@@ -317,10 +358,12 @@ describe('di', () => {
@Directive({selector: '[structuralDir]'})
class StructuralDirective {
@Input() tmp !: TemplateRef
;
+
constructor(public vcr: ViewContainerRef) {}
create() { this.vcr.createEmbeddedView(this.tmp); }
}
+
@Component({
template: `
@@ -336,6 +379,7 @@ describe('di', () => {
class MyComp {
@ViewChild(StructuralDirective) structuralDir !: StructuralDirective;
}
+
TestBed.configureTestingModule(
{declarations: [StructuralDirective, DirectiveA, DirectiveB, MyComp]});
const fixture = TestBed.createComponent(MyComp);
@@ -356,6 +400,7 @@ describe('di', () => {
})
class MyApp {
}
+
TestBed.configureTestingModule({declarations: [DirectiveA, DirectiveB, MyComp, MyApp]});
const fixture = TestBed.createComponent(MyApp);
fixture.detectChanges();
@@ -379,6 +424,7 @@ describe('di', () => {
@ViewChild(HostBindingDirective) hostBindingDir !: HostBindingDirective;
@ViewChild(DirectiveA) dirA !: DirectiveA;
}
+
TestBed.configureTestingModule(
{declarations: [DirectiveA, DirectiveB, HostBindingDirective, MyApp]});
const fixture = TestBed.createComponent(MyApp);
@@ -403,13 +449,16 @@ describe('di', () => {
class DirectiveB {
constructor() {}
}
+
@Directive({selector: '[dirA]'})
class DirectiveA {
constructor(siblingDir: DirectiveB) {}
}
+
@Component({template: ''})
class MyComp {
}
+
TestBed.configureTestingModule({declarations: [DirectiveA, DirectiveB, MyComp]});
expect(() => TestBed.createComponent(MyComp)).toThrowError(/No provider for DirectiveB/);
});
@@ -419,13 +468,16 @@ describe('di', () => {
class DirectiveB {
constructor() {}
}
+
@Directive({selector: '[dirA]'})
class DirectiveA {
constructor(siblingDir: DirectiveB) {}
}
+
@Component({template: ''})
class MyComp {
}
+
TestBed.configureTestingModule({declarations: [DirectiveA, DirectiveB, MyComp]});
expect(() => TestBed.createComponent(MyComp)).toThrowError(/No provider for DirectiveB/);
});
@@ -436,13 +488,16 @@ describe('di', () => {
class DirectiveB {
constructor(@Inject(forwardRef(() => DirectiveA)) siblingDir: DirectiveA) {}
}
+
@Directive({selector: '[dirA]'})
class DirectiveA {
constructor(siblingDir: DirectiveB) {}
}
+
@Component({template: ''})
class MyComp {
}
+
TestBed.configureTestingModule({declarations: [DirectiveA, DirectiveB, MyComp]});
expect(() => TestBed.createComponent(MyComp)).toThrowError(/Circular dep for/);
});
@@ -453,12 +508,336 @@ describe('di', () => {
class DirectiveA {
constructor(siblingDir: DirectiveA) {}
}
+
@Component({template: ''})
class MyComp {
}
+
TestBed.configureTestingModule({declarations: [DirectiveA, DirectiveB, MyComp]});
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: ''})
+ 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: ''})
+ 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: ''})
+ 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: ''})
+ class MyComp {
+ @ViewChild(DirectiveA) dirA !: DirectiveA;
+ }
+
+ @Component({template: ''})
+ 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: ''})
+ 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: '',
+ viewProviders: [{provide: String, useValue: 'Foo'}]
+ })
+ class MyComp {
+ @ViewChild(DirectiveString) dirString !: DirectiveString;
+ }
+
+ @Component({template: ''})
+ 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: ''})
+ class MyComp {
+ @ViewChild(DirectiveComp) dirComp !: DirectiveComp;
+ }
+
+ @Component({template: ''})
+ 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: '',
+ providers: [{provide: String, useValue: 'Foo'}]
+ })
+ class MyComp {
+ }
+
+ @Component({template: ''})
+ 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: ''})
+ class MyComp {
+ }
+
+ @Component({template: ''})
+ 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: ''
+ })
+ class MyComp {
+ showing = false;
+ }
+
+ @Component({template: ''})
+ 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: ''})
+ 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: ''})
+ class MyComp {
+ }
+
+ @Component({template: ''})
+ 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: '',
+ viewProviders: [{provide: ControlContainer, useExisting: GroupDirective}]
+ })
+ class MyComp {
+ }
+
+ @Component({
+ template: `
+
+
+
+ `
+ })
+ class MyApp {
+ }
+
+ TestBed.configureTestingModule(
+ {declarations: [GroupDirective, ControlDirective, MyComp, MyApp]});
+ const fixture = TestBed.createComponent(MyApp);
+ expect(fixture.nativeElement.innerHTML)
+ .toBe('
');
+ expect(controlContainers).toEqual([injectedControlContainer !]);
+ });
+ });
+ });
+ });
});
describe('service injection', () => {
diff --git a/packages/core/test/render3/di_spec.ts b/packages/core/test/render3/di_spec.ts
index 3aa1d037df..b57087169f 100644
--- a/packages/core/test/render3/di_spec.ts
+++ b/packages/core/test/render3/di_spec.ts
@@ -6,22 +6,20 @@
* 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 {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 {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 {AttributeMarker, TNodeType} from '../../src/render3/interfaces/node';
+import {TNodeType} from '../../src/render3/interfaces/node';
import {isProceduralRenderer} from '../../src/render3/interfaces/renderer';
import {LViewFlags} from '../../src/render3/interfaces/view';
import {enterView, leaveView} from '../../src/render3/state';
import {ViewRef} from '../../src/render3/view_ref';
-import {NgIf} from './common_with_def';
import {getRendererFactory2} from './imported_renderer2';
import {ComponentFixture, createComponent, createDirective, getDirectiveOnNode, renderComponent, toHtml} from './render_util';
@@ -207,142 +205,6 @@ describe('di', () => {
expect(() => { new ComponentFixture(App); }).not.toThrow();
expect(dirA !.dirB).toEqual(null);
});
-
- it('should not throw if dependency is @Optional (module injector)', () => {
- class SomeModule {
- static ngInjectorDef = ɵɵdefineInjector({factory: () => new SomeModule()});
- }
-
- /** */
- 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))
- });
- }
-
- /** */
- 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))
- });
- }
-
- /**
- *
- *
- */
- 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))
- });
- }
-
- /** */
- const Comp = createComponent('comp', function(rf: RenderFlags, ctx: any) {
- if (rf & RenderFlags.Create) {
- ɵɵelement(0, 'div', ['dirA', '', 'dirB', 'self']);
- }
- }, 1, 0, [DirA, DirB]);
-
- /* */
- 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))
- });
- }
-
- /**
- *
- */
- 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', () => {
@@ -382,12 +244,8 @@ describe('di', () => {
describe('@Host', () => {
let dirA: DirA|null = null;
- let dirString: DirString|null = null;
- beforeEach(() => {
- dirA = null;
- dirString = null;
- });
+ beforeEach(() => { dirA = null; });
class DirA {
constructor(@Host() public dirB: DirB) {}
@@ -399,106 +257,10 @@ describe('di', () => {
});
}
- class DirString {
- constructor(@Host() public s: String) {}
-
- static ngDirectiveDef = ɵɵdefineDirective({
- type: DirString,
- selectors: [['', 'dirString', '']],
- factory: () => dirString = new DirString(ɵɵdirectiveInject(String, InjectFlags.Host))
- });
- }
-
- it('should find viewProviders on the host itself', () => {
- /** */
- 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'}]);
-
- /* */
- 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))
- });
- }
-
- /** */
- const Comp = createComponent('comp', function(rf: RenderFlags, ctx: any) {
- if (rf & RenderFlags.Create) {
- ɵɵelement(0, 'div', ['dirCmp', '']);
- }
- }, 1, 0, [DirComp]);
-
- /* */
- 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', () => {
- /** */
- 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'}]);
-
- /* */
- 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', () => {
- /** */
- const Comp = createComponent('comp', function(rf: RenderFlags, ctx: any) {
- if (rf & RenderFlags.Create) {
- ɵɵelement(0, 'div', ['dirA', '']);
- }
- }, 1, 0, [DirA]);
-
- /* */
- 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\]/);
- });
-
+ /**
+ * This test needs to be moved to acceptance/di_spec.ts
+ * when Ivy compiler supports inline views.
+ */
it('should not find providers on the host itself if in inline view', () => {
let comp !: any;
@@ -542,192 +304,6 @@ describe('di', () => {
fixture.update();
}).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', '']);
- }
- }
-
- /**
- *
- */
- 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))
- });
- }
-
- /** */
- const Comp = createComponent('comp', function(rf: RenderFlags, ctx: any) {
- if (rf & RenderFlags.Create) {
- ɵɵelement(0, 'div', ['dirCmp', '']);
- }
- }, 1, 0, [DirComp]);
-
- /* */
- 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: `
-
- `,
- 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: `
-
-
-
- `
- })
- */
- 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);
- expect(fixture.html)
- .toEqual(
- '
');
-
- 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