diff --git a/packages/core/test/acceptance/di_spec.ts b/packages/core/test/acceptance/di_spec.ts
index 195f3964c1..b7c2090d93 100644
--- a/packages/core/test/acceptance/di_spec.ts
+++ b/packages/core/test/acceptance/di_spec.ts
@@ -7,59 +7,463 @@
*/
import {CommonModule} from '@angular/common';
-import {Attribute, ChangeDetectorRef, Component, Directive, EventEmitter, Inject, Input, LOCALE_ID, Optional, Output, Pipe, PipeTransform, SkipSelf, ViewChild} from '@angular/core';
+import {Attribute, ChangeDetectorRef, Component, Directive, ElementRef, EventEmitter, INJECTOR, Inject, Injector, Input, LOCALE_ID, Optional, Output, Pipe, PipeTransform, SkipSelf, TemplateRef, ViewChild, ViewContainerRef} from '@angular/core';
import {ViewRef} from '@angular/core/src/render3/view_ref';
import {TestBed} from '@angular/core/testing';
+import {onlyInIvy} from '@angular/private/testing';
describe('di', () => {
- describe('ChangeDetectorRef', () => {
- it('should inject host component ChangeDetectorRef into directives on templates', () => {
- let pipeInstance: MyPipe;
+ describe('Special tokens', () => {
- @Pipe({name: 'pipe'})
- class MyPipe implements PipeTransform {
- constructor(public cdr: ChangeDetectorRef) { pipeInstance = this; }
+ describe('Injector', () => {
- transform(value: any): any { return value; }
- }
+ it('should inject the injector', () => {
+ @Directive({selector: '[injectorDir]'})
+ class InjectorDir {
+ constructor(public injector: Injector) {}
+ }
- @Component({
- selector: 'my-app',
- template: `
Visible
`,
- })
- class MyApp {
- showing = true;
+ @Directive({selector: '[otherInjectorDir]'})
+ class OtherInjectorDir {
+ constructor(public otherDir: InjectorDir, public injector: Injector) {}
+ }
- constructor(public cdr: ChangeDetectorRef) {}
- }
+ @Component({template: ''})
+ class MyComp {
+ @ViewChild(InjectorDir) injectorDir !: InjectorDir;
+ @ViewChild(OtherInjectorDir) otherInjectorDir !: OtherInjectorDir;
+ }
- TestBed.configureTestingModule({declarations: [MyApp, MyPipe], imports: [CommonModule]});
- const fixture = TestBed.createComponent(MyApp);
- fixture.detectChanges();
- expect((pipeInstance !.cdr as ViewRef).context).toBe(fixture.componentInstance);
+ TestBed.configureTestingModule({declarations: [InjectorDir, OtherInjectorDir, MyComp]});
+ const fixture = TestBed.createComponent(MyComp);
+ fixture.detectChanges();
+
+ const divElement = fixture.nativeElement.querySelector('div');
+ const injectorDir = fixture.componentInstance.injectorDir;
+ const otherInjectorDir = fixture.componentInstance.otherInjectorDir;
+
+ expect(injectorDir.injector.get(ElementRef).nativeElement).toBe(divElement);
+ expect(otherInjectorDir.injector.get(ElementRef).nativeElement).toBe(divElement);
+ expect(otherInjectorDir.injector.get(InjectorDir)).toBe(injectorDir);
+ expect(injectorDir.injector).not.toBe(otherInjectorDir.injector);
+ });
+
+ it('should inject INJECTOR', () => {
+ @Directive({selector: '[injectorDir]'})
+ class InjectorDir {
+ constructor(@Inject(INJECTOR) public injector: Injector) {}
+ }
+
+ @Component({template: ''})
+ class MyComp {
+ @ViewChild(InjectorDir) injectorDir !: InjectorDir;
+ }
+
+ TestBed.configureTestingModule({declarations: [InjectorDir, MyComp]});
+ const fixture = TestBed.createComponent(MyComp);
+ fixture.detectChanges();
+
+ const divElement = fixture.nativeElement.querySelector('div');
+ const injectorDir = fixture.componentInstance.injectorDir;
+
+ expect(injectorDir.injector.get(ElementRef).nativeElement).toBe(divElement);
+ expect(injectorDir.injector.get(Injector).get(ElementRef).nativeElement).toBe(divElement);
+ expect(injectorDir.injector.get(INJECTOR).get(ElementRef).nativeElement).toBe(divElement);
+ });
});
- it('should inject host component ChangeDetectorRef into directives on ng-container', () => {
- let dirInstance: MyDirective;
+ describe('ElementRef', () => {
- @Directive({selector: '[getCDR]'})
- class MyDirective {
- constructor(public cdr: ChangeDetectorRef) { dirInstance = this; }
+ it('should create directive with ElementRef dependencies', () => {
+ @Directive({selector: '[dir]'})
+ class MyDir {
+ value: string;
+ constructor(public elementRef: ElementRef) {
+ this.value = (elementRef.constructor as any).name;
+ }
+ }
+
+ @Directive({selector: '[otherDir]'})
+ class MyOtherDir {
+ isSameInstance: boolean;
+ constructor(public elementRef: ElementRef, public directive: MyDir) {
+ this.isSameInstance = elementRef === directive.elementRef;
+ }
+ }
+
+ @Component({template: ''})
+ class MyComp {
+ @ViewChild(MyDir) directive !: MyDir;
+ @ViewChild(MyOtherDir) otherDirective !: MyOtherDir;
+ }
+
+ TestBed.configureTestingModule({declarations: [MyDir, MyOtherDir, MyComp]});
+ const fixture = TestBed.createComponent(MyComp);
+ fixture.detectChanges();
+
+ const divElement = fixture.nativeElement.querySelector('div');
+ const directive = fixture.componentInstance.directive;
+ const otherDirective = fixture.componentInstance.otherDirective;
+
+ expect(directive.value).toContain('ElementRef');
+ expect(directive.elementRef.nativeElement).toEqual(divElement);
+ expect(otherDirective.elementRef.nativeElement).toEqual(divElement);
+
+ // Each ElementRef instance should be unique
+ expect(otherDirective.isSameInstance).toBe(false);
+ });
+
+ it('should create ElementRef with comment if requesting directive is on node',
+ () => {
+ @Directive({selector: '[dir]'})
+ class MyDir {
+ value: string;
+ constructor(public elementRef: ElementRef) {
+ this.value = (elementRef.constructor as any).name;
+ }
+ }
+
+ @Component({template: ''})
+ class MyComp {
+ @ViewChild(MyDir) directive !: MyDir;
+ }
+
+ TestBed.configureTestingModule({declarations: [MyDir, MyComp]});
+ const fixture = TestBed.createComponent(MyComp);
+ fixture.detectChanges();
+
+ const directive = fixture.componentInstance.directive;
+
+ expect(directive.value).toContain('ElementRef');
+ // the nativeElement should be a comment
+ expect(directive.elementRef.nativeElement.nodeType).toEqual(Node.COMMENT_NODE);
+ });
+ });
+
+ describe('TemplateRef', () => {
+
+ @Directive({selector: '[dir]', exportAs: 'dir'})
+ class MyDir {
+ value: string;
+ constructor(public templateRef: TemplateRef) {
+ this.value = (templateRef.constructor as any).name;
+ }
}
+ onlyInIvy('Ivy creates a unique instance of TemplateRef for each directive')
+ .it('should create directive with TemplateRef dependencies', () => {
+ @Directive({selector: '[otherDir]', exportAs: 'otherDir'})
+ class MyOtherDir {
+ isSameInstance: boolean;
+ constructor(public templateRef: TemplateRef, public directive: MyDir) {
+ this.isSameInstance = templateRef === directive.templateRef;
+ }
+ }
- @Component({
- selector: 'my-app',
- template: `Visible`,
- })
- class MyApp {
+ @Component({
+ template: ''
+ })
+ class MyComp {
+ @ViewChild(MyDir) directive !: MyDir;
+ @ViewChild(MyOtherDir) otherDirective !: MyOtherDir;
+ }
+
+ TestBed.configureTestingModule({declarations: [MyDir, MyOtherDir, MyComp]});
+ const fixture = TestBed.createComponent(MyComp);
+ fixture.detectChanges();
+
+ const directive = fixture.componentInstance.directive;
+ const otherDirective = fixture.componentInstance.otherDirective;
+
+ expect(directive.value).toContain('TemplateRef');
+ expect(directive.templateRef).not.toBeNull();
+ expect(otherDirective.templateRef).not.toBeNull();
+
+ // Each TemplateRef instance should be unique
+ expect(otherDirective.isSameInstance).toBe(false);
+ });
+
+ it('should throw if injected on an element', () => {
+ @Component({template: ''})
+ class MyComp {
+ }
+
+ TestBed.configureTestingModule({declarations: [MyDir, MyComp]});
+ expect(() => TestBed.createComponent(MyComp)).toThrowError(/No provider for TemplateRef/);
+ });
+
+ it('should throw if injected on an ng-container', () => {
+ @Component({template: ''})
+ class MyComp {
+ }
+
+ TestBed.configureTestingModule({declarations: [MyDir, MyComp]});
+ expect(() => TestBed.createComponent(MyComp)).toThrowError(/No provider for TemplateRef/);
+ });
+
+ it('should NOT throw if optional and injected on an element', () => {
+ @Directive({selector: '[optionalDir]', exportAs: 'optionalDir'})
+ class OptionalDir {
+ constructor(@Optional() public templateRef: TemplateRef) {}
+ }
+ @Component({template: ''})
+ class MyComp {
+ @ViewChild(OptionalDir) directive !: OptionalDir;
+ }
+
+ TestBed.configureTestingModule({declarations: [OptionalDir, MyComp]});
+ const fixture = TestBed.createComponent(MyComp);
+ fixture.detectChanges();
+ expect(fixture.componentInstance.directive.templateRef).toBeNull();
+ });
+ });
+
+ describe('ViewContainerRef', () => {
+ onlyInIvy('Ivy creates a unique instance of ViewContainerRef for each directive')
+ .it('should create directive with ViewContainerRef dependencies', () => {
+ @Directive({selector: '[dir]', exportAs: 'dir'})
+ class MyDir {
+ value: string;
+ constructor(public viewContainerRef: ViewContainerRef) {
+ this.value = (viewContainerRef.constructor as any).name;
+ }
+ }
+ @Directive({selector: '[otherDir]', exportAs: 'otherDir'})
+ class MyOtherDir {
+ isSameInstance: boolean;
+ constructor(public viewContainerRef: ViewContainerRef, public directive: MyDir) {
+ this.isSameInstance = viewContainerRef === directive.viewContainerRef;
+ }
+ }
+ @Component({template: ''})
+ class MyComp {
+ @ViewChild(MyDir) directive !: MyDir;
+ @ViewChild(MyOtherDir) otherDirective !: MyOtherDir;
+ }
+
+ TestBed.configureTestingModule({declarations: [MyDir, MyOtherDir, MyComp]});
+ const fixture = TestBed.createComponent(MyComp);
+ fixture.detectChanges();
+
+ const directive = fixture.componentInstance.directive;
+ const otherDirective = fixture.componentInstance.otherDirective;
+
+ expect(directive.value).toContain('ViewContainerRef');
+ expect(directive.viewContainerRef).not.toBeNull();
+ expect(otherDirective.viewContainerRef).not.toBeNull();
+
+ // Each ViewContainerRef instance should be unique
+ expect(otherDirective.isSameInstance).toBe(false);
+ });
+ });
+
+ describe('ChangeDetectorRef', () => {
+
+ @Directive({selector: '[dir]', exportAs: 'dir'})
+ class MyDir {
+ value: string;
+ constructor(public cdr: ChangeDetectorRef) { this.value = (cdr.constructor as any).name; }
+ }
+ @Directive({selector: '[otherDir]', exportAs: 'otherDir'})
+ class MyOtherDir {
+ constructor(public cdr: ChangeDetectorRef) {}
+ }
+ @Component({selector: 'my-comp', template: ''})
+ class MyComp {
constructor(public cdr: ChangeDetectorRef) {}
}
- TestBed.configureTestingModule({declarations: [MyApp, MyDirective]});
- const fixture = TestBed.createComponent(MyApp);
+ it('should inject host component ChangeDetectorRef into directives on templates', () => {
+ let pipeInstance: MyPipe;
+
+ @Pipe({name: 'pipe'})
+ class MyPipe implements PipeTransform {
+ constructor(public cdr: ChangeDetectorRef) { pipeInstance = this; }
+
+ transform(value: any): any { return value; }
+ }
+
+ @Component({
+ selector: 'my-app',
+ template: `Visible
`,
+ })
+ class MyApp {
+ showing = true;
+
+ constructor(public cdr: ChangeDetectorRef) {}
+ }
+
+ TestBed.configureTestingModule({declarations: [MyApp, MyPipe], imports: [CommonModule]});
+ const fixture = TestBed.createComponent(MyApp);
+ fixture.detectChanges();
+ expect((pipeInstance !.cdr as ViewRef).context).toBe(fixture.componentInstance);
+ });
+
+ it('should inject current component ChangeDetectorRef into directives on the same node as components',
+ () => {
+ @Component({selector: 'my-app', template: ''})
+ class MyApp {
+ @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).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);
+ });
+
+ it('should inject host component ChangeDetectorRef into directives on normal elements',
+ () => {
+ @Component({selector: 'my-comp', template: ''})
+ class MyComp {
+ constructor(public cdr: ChangeDetectorRef) {}
+ @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).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);
+ });
+
+ it('should inject host component ChangeDetectorRef into directives in a component\'s ContentChildren',
+ () => {
+ @Component({
+ selector: 'my-app',
+ template: `
+
+
+ `
+ })
+ class MyApp {
+ constructor(public cdr: ChangeDetectorRef) {}
+ @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).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);
+ });
+
+ it('should inject host component ChangeDetectorRef into directives in embedded views', () => {
+ @Component({
+ selector: 'my-comp',
+ template: `
+
+ `
+ })
+ class MyComp {
+ showing = true;
+ constructor(public cdr: ChangeDetectorRef) {}
+ @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).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);
+ });
+
+ it('should inject host component ChangeDetectorRef into directives on containers', () => {
+ @Component(
+ {selector: 'my-comp', template: ''})
+ class MyComp {
+ showing = true;
+ constructor(public cdr: ChangeDetectorRef) {}
+ @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).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);
+ });
+
+ it('should inject host component ChangeDetectorRef into directives on ng-container', () => {
+ let dirInstance: MyDirective;
+
+ @Directive({selector: '[getCDR]'})
+ class MyDirective {
+ constructor(public cdr: ChangeDetectorRef) { dirInstance = this; }
+ }
+
+ @Component({
+ selector: 'my-app',
+ template: `Visible`,
+ })
+ class MyApp {
+ constructor(public cdr: ChangeDetectorRef) {}
+ }
+
+ TestBed.configureTestingModule({declarations: [MyApp, MyDirective]});
+ const fixture = TestBed.createComponent(MyApp);
+ fixture.detectChanges();
+ expect((dirInstance !.cdr as ViewRef).context).toBe(fixture.componentInstance);
+ });
+ });
+ });
+
+ describe('string tokens', () => {
+ it('should be able to provide a string token', () => {
+ @Directive({selector: '[injectorDir]', providers: [{provide: 'test', useValue: 'provided'}]})
+ class InjectorDir {
+ constructor(@Inject('test') public value: string) {}
+ }
+
+ @Component({template: ''})
+ class MyComp {
+ @ViewChild(InjectorDir) injectorDirInstance !: InjectorDir;
+ }
+
+ TestBed.configureTestingModule({declarations: [InjectorDir, MyComp]});
+ const fixture = TestBed.createComponent(MyComp);
fixture.detectChanges();
- expect((dirInstance !.cdr as ViewRef).context).toBe(fixture.componentInstance);
+
+ const injectorDir = fixture.componentInstance.injectorDirInstance;
+
+ expect(injectorDir.value).toBe('provided');
});
});
diff --git a/packages/core/test/render3/di_spec.ts b/packages/core/test/render3/di_spec.ts
index 78c91a54ee..0426e06777 100644
--- a/packages/core/test/render3/di_spec.ts
+++ b/packages/core/test/render3/di_spec.ts
@@ -6,21 +6,19 @@
* found in the LICENSE file at https://angular.io/license
*/
-import {ChangeDetectorRef, ElementRef, Host, INJECTOR, Inject, InjectFlags, Injector, Optional, Renderer2, Self, SkipSelf, TemplateRef, ViewContainerRef, ɵɵdefineInjectable, ɵɵdefineInjector} from '@angular/core';
+import {ChangeDetectorRef, Host, Inject, InjectFlags, Injector, Optional, Renderer2, Self, SkipSelf, TemplateRef, ViewContainerRef, ɵɵdefineInjectable, ɵɵdefineInjector} 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 {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, ɵɵallocHostVars, ɵɵbind, ɵɵcontainer, ɵɵcontainerRefreshEnd, ɵɵcontainerRefreshStart, ɵɵdefineDirective, ɵɵdirectiveInject, ɵɵelement, ɵɵelementContainerEnd, ɵɵelementContainerStart, ɵɵelementEnd, ɵɵelementProperty, ɵɵelementStart, ɵɵembeddedViewEnd, ɵɵembeddedViewStart, ɵɵinterpolation2, ɵɵload, ɵɵprojection, ɵɵprojectionDef, ɵɵreference, ɵɵtemplate, ɵɵtemplateRefExtractor, ɵɵtext, ɵɵtextBinding} from '../../src/render3/index';
-import {LContainer, NATIVE} from '../../src/render3/interfaces/container';
+import {ɵɵProvidersFeature, ɵɵallocHostVars, ɵɵbind, ɵɵcontainer, ɵɵcontainerRefreshEnd, ɵɵcontainerRefreshStart, ɵɵdefineDirective, ɵɵdirectiveInject, ɵɵelement, ɵɵelementEnd, ɵɵelementProperty, ɵɵelementStart, ɵɵembeddedViewEnd, ɵɵembeddedViewStart, ɵɵinterpolation2, ɵɵprojection, ɵɵprojectionDef, ɵɵreference, ɵɵtemplate, ɵɵtemplateRefExtractor, ɵɵtext, ɵɵtextBinding} from '../../src/render3/index';
import {TNODE} from '../../src/render3/interfaces/injector';
import {AttributeMarker, TNodeType} from '../../src/render3/interfaces/node';
-import {RElement, isProceduralRenderer} from '../../src/render3/interfaces/renderer';
+import {isProceduralRenderer} from '../../src/render3/interfaces/renderer';
import {LViewFlags} from '../../src/render3/interfaces/view';
-import {enterView, getLView, leaveView} from '../../src/render3/state';
-import {getNativeByIndex} from '../../src/render3/util/view_utils';
+import {enterView, leaveView} from '../../src/render3/state';
import {ViewRef} from '../../src/render3/view_ref';
import {NgIf} from './common_with_def';
@@ -1419,326 +1417,6 @@ describe('di', () => {
describe('Special tokens', () => {
- describe('Injector', () => {
-
- it('should inject the injector', () => {
- let injectorDir !: InjectorDir;
- let otherInjectorDir !: OtherInjectorDir;
- let divElement !: HTMLElement;
-
- class InjectorDir {
- constructor(public injector: Injector) {}
-
- static ngDirectiveDef = ɵɵdefineDirective({
- type: InjectorDir,
- selectors: [['', 'injectorDir', '']],
- factory: () => injectorDir = new InjectorDir(ɵɵdirectiveInject(Injector as any))
- });
- }
-
- class OtherInjectorDir {
- constructor(public otherDir: InjectorDir, public injector: Injector) {}
-
- static ngDirectiveDef = ɵɵdefineDirective({
- type: OtherInjectorDir,
- selectors: [['', 'otherInjectorDir', '']],
- factory: () => otherInjectorDir = new OtherInjectorDir(
- ɵɵdirectiveInject(InjectorDir), ɵɵdirectiveInject(Injector as any))
- });
- }
-
-
- /** */
- const App = createComponent('app', (rf: RenderFlags, ctx: any) => {
- if (rf & RenderFlags.Create) {
- ɵɵelement(0, 'div', ['injectorDir', '', 'otherInjectorDir', '']);
- }
- // testing only
- divElement = ɵɵload(0);
- }, 1, 0, [InjectorDir, OtherInjectorDir]);
-
- const fixture = new ComponentFixture(App);
- expect(injectorDir.injector.get(ElementRef).nativeElement).toBe(divElement);
- expect(otherInjectorDir.injector.get(ElementRef).nativeElement).toBe(divElement);
- expect(otherInjectorDir.injector.get(InjectorDir)).toBe(injectorDir);
- expect(injectorDir.injector).not.toBe(otherInjectorDir.injector);
- });
-
- it('should inject INJECTOR', () => {
- let injectorDir !: INJECTORDir;
- let divElement !: HTMLElement;
-
- class INJECTORDir {
- constructor(public injector: Injector) {}
-
- static ngDirectiveDef = ɵɵdefineDirective({
- type: INJECTORDir,
- selectors: [['', 'injectorDir', '']],
- factory: () => injectorDir = new INJECTORDir(ɵɵdirectiveInject(INJECTOR as any))
- });
- }
-
-
- /** */
- const App = createComponent('app', (rf: RenderFlags, ctx: any) => {
- if (rf & RenderFlags.Create) {
- ɵɵelement(0, 'div', ['injectorDir', '']);
- }
- // testing only
- divElement = ɵɵload(0);
- }, 1, 0, [INJECTORDir]);
-
- const fixture = new ComponentFixture(App);
- expect(injectorDir.injector.get(ElementRef).nativeElement).toBe(divElement);
- expect(injectorDir.injector.get(Injector).get(ElementRef).nativeElement).toBe(divElement);
- expect(injectorDir.injector.get(INJECTOR).get(ElementRef).nativeElement).toBe(divElement);
- });
-
- });
-
- describe('ElementRef', () => {
-
- it('should create directive with ElementRef dependencies', () => {
- let dir !: Directive;
- let dirSameInstance !: DirectiveSameInstance;
- let div !: RElement;
-
- class Directive {
- value: string;
- constructor(public elementRef: ElementRef) {
- this.value = (elementRef.constructor as any).name;
- }
- static ngDirectiveDef = ɵɵdefineDirective({
- type: Directive,
- selectors: [['', 'dir', '']],
- factory: () => dir = new Directive(ɵɵdirectiveInject(ElementRef)),
- exportAs: ['dir']
- });
- }
-
- class DirectiveSameInstance {
- isSameInstance: boolean;
- constructor(public elementRef: ElementRef, directive: Directive) {
- this.isSameInstance = elementRef === directive.elementRef;
- }
- static ngDirectiveDef = ɵɵdefineDirective({
- type: DirectiveSameInstance,
- selectors: [['', 'dirSame', '']],
- factory: () => dirSameInstance = new DirectiveSameInstance(
- ɵɵdirectiveInject(ElementRef), ɵɵdirectiveInject(Directive)),
- exportAs: ['dirSame']
- });
- }
-
- /** */
- const App = createComponent('app', function(rf: RenderFlags, ctx: any) {
- if (rf & RenderFlags.Create) {
- ɵɵelementStart(0, 'div', ['dir', '', 'dirSame', '']);
- ɵɵelementEnd();
- div = getNativeByIndex(0, getLView()) as RElement;
- }
- }, 1, 0, [Directive, DirectiveSameInstance]);
-
- const fixture = new ComponentFixture(App);
- expect(dir.value).toContain('ElementRef');
- expect(dir.elementRef.nativeElement).toEqual(div);
- expect(dirSameInstance.elementRef.nativeElement).toEqual(div);
-
- // Each ElementRef instance should be unique
- expect(dirSameInstance.isSameInstance).toBe(false);
- });
-
- it('should create ElementRef with comment if requesting directive is on node',
- () => {
- let dir !: Directive;
- let lContainer !: LContainer;
-
- class Directive {
- value: string;
- constructor(public elementRef: ElementRef) {
- this.value = (elementRef.constructor as any).name;
- }
- static ngDirectiveDef = ɵɵdefineDirective({
- type: Directive,
- selectors: [['', 'dir', '']],
- factory: () => dir = new Directive(ɵɵdirectiveInject(ElementRef)),
- exportAs: ['dir']
- });
- }
-
- /** */
- const App = createComponent('app', function(rf: RenderFlags, ctx: any) {
- if (rf & RenderFlags.Create) {
- ɵɵtemplate(0, () => {}, 0, 0, 'ng-template', ['dir', '']);
- lContainer = ɵɵload(0) as any;
- }
- }, 1, 0, [Directive]);
-
- const fixture = new ComponentFixture(App);
- expect(dir.value).toContain('ElementRef');
- expect(dir.elementRef.nativeElement).toEqual(lContainer[NATIVE]);
- });
- });
-
- describe('TemplateRef', () => {
- class Directive {
- value: string;
- constructor(public templateRef: TemplateRef) {
- this.value = (templateRef.constructor as any).name;
- }
- static ngDirectiveDef = ɵɵdefineDirective({
- type: Directive,
- selectors: [['', 'dir', '']],
- factory: () => new Directive(ɵɵdirectiveInject(TemplateRef as any)),
- exportAs: ['dir']
- });
- }
-
- it('should create directive with TemplateRef dependencies', () => {
- class DirectiveSameInstance {
- isSameInstance: boolean;
- constructor(templateRef: TemplateRef, directive: Directive) {
- this.isSameInstance = templateRef === directive.templateRef;
- }
- static ngDirectiveDef = ɵɵdefineDirective({
- type: DirectiveSameInstance,
- selectors: [['', 'dirSame', '']],
- factory: () => new DirectiveSameInstance(
- ɵɵdirectiveInject(TemplateRef as any), ɵɵdirectiveInject(Directive)),
- exportAs: ['dirSame']
- });
- }
-
- /**
- *
- * {{ dir.value }} - {{ dirSame.value }}
- *
- */
- const App = createComponent('app', function(rf: RenderFlags, ctx: any) {
- if (rf & RenderFlags.Create) {
- ɵɵtemplate(
- 0, function() {}, 0, 0, 'ng-template', ['dir', '', 'dirSame', ''],
- ['dir', 'dir', 'dirSame', 'dirSame']);
- ɵɵtext(3);
- }
- if (rf & RenderFlags.Update) {
- const tmp1 = ɵɵreference(1) as any;
- const tmp2 = ɵɵreference(2) as any;
- ɵɵtextBinding(3, ɵɵinterpolation2('', tmp1.value, '-', tmp2.isSameInstance, ''));
- }
- }, 4, 2, [Directive, DirectiveSameInstance]);
-
- const fixture = new ComponentFixture(App);
- // Each TemplateRef instance should be unique
- expect(fixture.html).toContain('TemplateRef');
- expect(fixture.html).toContain('false');
- });
-
- it('should throw if injected on an element', () => {
- /** */
- const App = createComponent('app', (rf: RenderFlags, ctx: any) => {
- if (rf & RenderFlags.Create) {
- ɵɵelement(0, 'div', ['dir', '']);
- }
- }, 1, 0, [Directive]);
-
- expect(() => new ComponentFixture(App)).toThrowError(/No provider for TemplateRef/);
- });
-
- it('should throw if injected on an ng-container', () => {
- /** */
- const App = createComponent('app', (rf: RenderFlags, ctx: any) => {
- if (rf & RenderFlags.Create) {
- ɵɵelementContainerStart(0, ['dir', '']);
- ɵɵelementContainerEnd();
- }
- }, 1, 0, [Directive]);
-
- expect(() => new ComponentFixture(App)).toThrowError(/No provider for TemplateRef/);
- });
-
- it('should NOT throw if optional and injected on an element', () => {
- let dir !: OptionalDirective;
- class OptionalDirective {
- constructor(@Optional() public templateRef: TemplateRef) {}
-
- static ngDirectiveDef = ɵɵdefineDirective({
- type: OptionalDirective,
- selectors: [['', 'dir', '']],
- factory: () => dir = new OptionalDirective(
- ɵɵdirectiveInject(TemplateRef as any, InjectFlags.Optional)),
- exportAs: ['dir']
- });
- }
-
- /** */
- const App = createComponent('app', (rf: RenderFlags, ctx: any) => {
- if (rf & RenderFlags.Create) {
- ɵɵelement(0, 'div', ['dir', '']);
- }
- }, 1, 0, [OptionalDirective]);
-
- expect(() => new ComponentFixture(App)).not.toThrow();
- expect(dir.templateRef).toBeNull();
- });
-
- });
-
- describe('ViewContainerRef', () => {
- it('should create directive with ViewContainerRef dependencies', () => {
- class Directive {
- value: string;
- constructor(public viewContainerRef: ViewContainerRef) {
- this.value = (viewContainerRef.constructor as any).name;
- }
- static ngDirectiveDef = ɵɵdefineDirective({
- type: Directive,
- selectors: [['', 'dir', '']],
- factory: () => new Directive(ɵɵdirectiveInject(ViewContainerRef as any)),
- exportAs: ['dir']
- });
- }
-
- class DirectiveSameInstance {
- isSameInstance: boolean;
- constructor(viewContainerRef: ViewContainerRef, directive: Directive) {
- this.isSameInstance = viewContainerRef === directive.viewContainerRef;
- }
- static ngDirectiveDef = ɵɵdefineDirective({
- type: DirectiveSameInstance,
- selectors: [['', 'dirSame', '']],
- factory: () => new DirectiveSameInstance(
- ɵɵdirectiveInject(ViewContainerRef as any), ɵɵdirectiveInject(Directive)),
- exportAs: ['dirSame']
- });
- }
-
- /**
- *
- * {{ dir.value }} - {{ dirSame.value }}
- *
- */
- const App = createComponent('app', function(rf: RenderFlags, ctx: any) {
- if (rf & RenderFlags.Create) {
- ɵɵelementStart(
- 0, 'div', ['dir', '', 'dirSame', ''], ['dir', 'dir', 'dirSame', 'dirSame']);
- { ɵɵtext(3); }
- ɵɵelementEnd();
- }
- if (rf & RenderFlags.Update) {
- const tmp1 = ɵɵreference(1) as any;
- const tmp2 = ɵɵreference(2) as any;
- ɵɵtextBinding(3, ɵɵinterpolation2('', tmp1.value, '-', tmp2.isSameInstance, ''));
- }
- }, 4, 2, [Directive, DirectiveSameInstance]);
-
- const fixture = new ComponentFixture(App);
- // Each ViewContainerRef instance should be unique
- expect(fixture.html).toContain('ViewContainerRef');
- expect(fixture.html).toContain('false');
- });
- });
-
describe('ChangeDetectorRef', () => {
let dir: Directive;
let dirSameInstance: DirectiveSameInstance;
@@ -1788,109 +1466,10 @@ describe('di', () => {
const directives = [MyComp, Directive, DirectiveSameInstance, NgIf];
- it('should inject current component ChangeDetectorRef into directives on the same node as components',
- () => {
- /** {{ dir.value }} */
- const MyApp = createComponent('my-app', function(rf: RenderFlags, ctx: any) {
- if (rf & RenderFlags.Create) {
- ɵɵelement(0, 'my-comp', ['dir', '', 'dirSame', ''], ['dir', 'dir']);
- ɵɵtext(2);
- }
- if (rf & RenderFlags.Update) {
- const tmp = ɵɵreference(1) as any;
- ɵɵtextBinding(2, ɵɵbind(tmp.value));
- }
- }, 3, 1, directives);
-
- const app = renderComponent(MyApp);
- // ChangeDetectorRef is the token, ViewRef has historically been the constructor
- expect(toHtml(app)).toEqual('ViewRef');
- expect((comp !.cdr as ViewRef).context).toBe(comp);
-
- // Each ChangeDetectorRef instance should be unique
- expect(dir !.cdr).not.toBe(comp !.cdr);
- expect(dir !.cdr).not.toBe(dirSameInstance !.cdr);
- });
-
- it('should inject host component ChangeDetectorRef into directives on normal elements',
- () => {
-
- class MyApp {
- constructor(public cdr: ChangeDetectorRef) {}
-
- static ngComponentDef = ɵɵdefineComponent({
- type: MyApp,
- selectors: [['my-app']],
- consts: 3,
- vars: 1,
- factory: () => new MyApp(ɵɵdirectiveInject(ChangeDetectorRef as any)),
- /** {{ dir.value }}
*/
- template: function(rf: RenderFlags, ctx: any) {
- if (rf & RenderFlags.Create) {
- ɵɵelementStart(0, 'div', ['dir', '', 'dirSame', ''], ['dir', 'dir']);
- { ɵɵtext(2); }
- ɵɵelementEnd();
- }
- if (rf & RenderFlags.Update) {
- const tmp = ɵɵreference(1) as any;
- ɵɵtextBinding(2, ɵɵbind(tmp.value));
- }
- },
- directives: directives
- });
- }
-
- const app = renderComponent(MyApp);
- expect(toHtml(app)).toEqual('ViewRef
');
- expect((app !.cdr as ViewRef).context).toBe(app);
-
- // Each ChangeDetectorRef instance should be unique
- expect(dir !.cdr).not.toBe(app.cdr);
- expect(dir !.cdr).not.toBe(dirSameInstance !.cdr);
- });
-
- it('should inject host component ChangeDetectorRef into directives in a component\'s ContentChildren',
- () => {
- class MyApp {
- constructor(public cdr: ChangeDetectorRef) {}
-
- static ngComponentDef = ɵɵdefineComponent({
- type: MyApp,
- selectors: [['my-app']],
- consts: 4,
- vars: 1,
- factory: () => new MyApp(ɵɵdirectiveInject(ChangeDetectorRef as any)),
- /**
- *
- *
- *
- * {{ dir.value }}
- */
- template: function(rf: RenderFlags, ctx: any) {
- if (rf & RenderFlags.Create) {
- ɵɵelementStart(0, 'my-comp');
- { ɵɵelement(1, 'div', ['dir', '', 'dirSame', ''], ['dir', 'dir']); }
- ɵɵelementEnd();
- ɵɵtext(3);
- }
- if (rf & RenderFlags.Update) {
- const tmp = ɵɵreference(2) as any;
- ɵɵtextBinding(3, ɵɵbind(tmp.value));
- }
- },
- directives: directives
- });
- }
-
- const app = renderComponent(MyApp);
- expect(toHtml(app)).toEqual('ViewRef');
- expect((app !.cdr as ViewRef).context).toBe(app);
-
- // Each ChangeDetectorRef instance should be unique
- expect(dir !.cdr).not.toBe(app !.cdr);
- expect(dir !.cdr).not.toBe(dirSameInstance !.cdr);
- });
-
+ /**
+ * This test needs to be moved to acceptance/di_spec.ts
+ * when Ivy compiler supports inline views.
+ */
it('should inject host component ChangeDetectorRef into directives in embedded views', () => {
class MyApp {
@@ -1906,8 +1485,8 @@ describe('di', () => {
vars: 0,
/**
* % if (showing) {
- * {{ dir.value }}
- * % }
+ * {{ dir.value }}
+ * % }
*/
template: function(rf: RenderFlags, ctx: MyApp) {
if (rf & RenderFlags.Create) {
@@ -1945,84 +1524,6 @@ describe('di', () => {
expect(dir !.cdr).not.toBe(app.cdr);
expect(dir !.cdr).not.toBe(dirSameInstance !.cdr);
});
-
- it('should inject host component ChangeDetectorRef into directives on containers', () => {
- function C1(rf1: RenderFlags, ctx1: any) {
- if (rf1 & RenderFlags.Create) {
- ɵɵelementStart(0, 'div', ['dir', '', 'dirSame', ''], ['dir', 'dir']);
- { ɵɵtext(2); }
- ɵɵelementEnd();
- }
- if (rf1 & RenderFlags.Update) {
- const tmp = ɵɵreference(1) as any;
- ɵɵtextBinding(2, ɵɵbind(tmp.value));
- }
- }
-
- class MyApp {
- showing = true;
-
- constructor(public cdr: ChangeDetectorRef) {}
-
- static ngComponentDef = ɵɵdefineComponent({
- type: MyApp,
- selectors: [['my-app']],
- factory: () => new MyApp(ɵɵdirectiveInject(ChangeDetectorRef as any)),
- consts: 1,
- vars: 0,
- /** {{ dir.value }}
*/
- template: function(rf: RenderFlags, ctx: MyApp) {
- if (rf & RenderFlags.Create) {
- ɵɵtemplate(
- 0, C1, 3, 1, 'div',
- ['dir', '', 'dirSame', '', AttributeMarker.Template, 'ngIf']);
- }
- if (rf & RenderFlags.Update) {
- ɵɵelementProperty(0, 'ngIf', ɵɵbind(ctx.showing));
- }
- },
- directives: directives
- });
- }
-
- const app = renderComponent(MyApp);
- expect(toHtml(app)).toEqual('ViewRef
');
- expect((app !.cdr as ViewRef).context).toBe(app);
-
- // Each ChangeDetectorRef instance should be unique
- expect(dir !.cdr).not.toBe(app.cdr);
- expect(dir !.cdr).not.toBe(dirSameInstance !.cdr);
- });
- });
- });
-
- describe('string tokens', () => {
- it('should be able to provide a string token', () => {
- let injectorDir !: InjectorDir;
- let divElement !: HTMLElement;
-
- class InjectorDir {
- constructor(public value: string) {}
-
- static ngDirectiveDef = ɵɵdefineDirective({
- type: InjectorDir,
- selectors: [['', 'injectorDir', '']],
- factory: () => injectorDir = new InjectorDir(ɵɵdirectiveInject('test' as any)),
- features: [ɵɵProvidersFeature([{provide: 'test', useValue: 'provided'}])],
- });
- }
-
- /** */
- const App = createComponent('app', (rf: RenderFlags, ctx: any) => {
- if (rf & RenderFlags.Create) {
- ɵɵelement(0, 'div', ['injectorDir', '']);
- }
- // testing only
- divElement = ɵɵload(0);
- }, 1, 0, [InjectorDir]);
-
- const fixture = new ComponentFixture(App);
- expect(injectorDir.value).toBe('provided');
});
});