Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(core): support InjectFlags argument in NodeInjector.get() #41592

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 2 additions & 2 deletions packages/core/src/render3/di.ts
Expand Up @@ -688,8 +688,8 @@ export class NodeInjector implements Injector {
private _tNode: TElementNode|TContainerNode|TElementContainerNode|null,
private _lView: LView) {}

get(token: any, notFoundValue?: any): any {
return getOrCreateInjectable(this._tNode, this._lView, token, undefined, notFoundValue);
get(token: any, notFoundValue?: any, flags?: InjectFlags): any {
return getOrCreateInjectable(this._tNode, this._lView, token, flags, notFoundValue);
}
}

Expand Down
122 changes: 121 additions & 1 deletion packages/core/test/acceptance/di_spec.ts
Expand Up @@ -7,7 +7,7 @@
*/

import {CommonModule} from '@angular/common';
import {Attribute, ChangeDetectorRef, Component, ComponentFactoryResolver, ComponentRef, Directive, ElementRef, EventEmitter, forwardRef, Host, HostBinding, Inject, Injectable, InjectionToken, INJECTOR, Injector, Input, LOCALE_ID, NgModule, NgZone, Optional, Output, Pipe, PipeTransform, Self, SkipSelf, TemplateRef, ViewChild, ViewContainerRef, ViewRef, ɵDEFAULT_LOCALE_ID as DEFAULT_LOCALE_ID} from '@angular/core';
import {Attribute, ChangeDetectorRef, Component, ComponentFactoryResolver, ComponentRef, Directive, ElementRef, EventEmitter, forwardRef, Host, HostBinding, Inject, Injectable, InjectFlags, InjectionToken, INJECTOR, Injector, Input, LOCALE_ID, NgModule, NgZone, Optional, Output, Pipe, PipeTransform, Self, SkipSelf, TemplateRef, ViewChild, ViewContainerRef, ViewRef, ɵDEFAULT_LOCALE_ID as DEFAULT_LOCALE_ID} from '@angular/core';
import {ɵINJECTOR_SCOPE} from '@angular/core/src/core';
import {ViewRef as ViewRefInternal} from '@angular/core/src/render3/view_ref';
import {TestBed} from '@angular/core/testing';
Expand Down Expand Up @@ -2006,6 +2006,126 @@ describe('di', () => {
});
});
});

onlyInIvy('Ivy supports `InjectFlags` in NodeInjector')
.describe('`InjectFlags` support in NodeInjector', () => {
it('should support Optional flag in NodeInjector', () => {
const NON_EXISTING_PROVIDER = new InjectionToken<string>('non-existing');
@Component({template: '...'})
class MyComp {
tokenViaInjector =
this.injector.get(NON_EXISTING_PROVIDER, null, InjectFlags.Optional);
constructor(
public injector: Injector,
@Inject(NON_EXISTING_PROVIDER) @Optional() public tokenViaConstructor: string) {
}
}
TestBed.configureTestingModule({declarations: [MyComp]});
const fixture = TestBed.createComponent(MyComp);
fixture.detectChanges();
expect(fixture.componentInstance.tokenViaInjector).toBe(null);
expect(fixture.componentInstance.tokenViaInjector)
.toBe(fixture.componentInstance.tokenViaConstructor);
});
it('should support SkipSelf flag in NodeInjector', () => {
const TOKEN = new InjectionToken<string>('token');
@Component({
selector: 'parent',
template: '<child></child>',
providers: [{
provide: TOKEN,
useValue: 'PARENT',
}]
})
class ParentComponent {
}

@Component({
selector: 'child',
template: '...',
providers: [{
provide: TOKEN,
useValue: 'CHILD',
}]
})
class ChildComponent {
tokenViaInjector = this.injector.get(TOKEN, null, InjectFlags.SkipSelf);
constructor(
public injector: Injector,
@Inject(TOKEN) @SkipSelf() public tokenViaConstructor: string) {}
}

TestBed.configureTestingModule({
declarations: [ParentComponent, ChildComponent],
});
const fixture = TestBed.createComponent(ParentComponent);
fixture.detectChanges();

const childComponent =
fixture.debugElement.query(By.directive(ChildComponent)).componentInstance;
expect(childComponent.tokenViaInjector).toBe('PARENT');
expect(childComponent.tokenViaConstructor).toBe(childComponent.tokenViaInjector);
});
it('should support Host flag in NodeInjector', () => {
const TOKEN = new InjectionToken<string>('token');
@Directive({selector: '[dirString]'})
class DirectiveString {
tokenViaInjector = this.injector.get(TOKEN, null, InjectFlags.Host);
constructor(
public injector: Injector,
@Inject(TOKEN) @Host() public tokenViaConstructor: string) {}
}

@Component({
template: '<div dirString></div>',
viewProviders: [{provide: TOKEN, useValue: 'Foo'}]
})
class MyComp {
@ViewChild(DirectiveString) dirString!: DirectiveString;
}

TestBed.configureTestingModule({declarations: [DirectiveString, MyComp]});
const fixture = TestBed.createComponent(MyComp);
fixture.detectChanges();

const dirString = fixture.componentInstance.dirString;
expect(dirString.tokenViaConstructor).toBe('Foo');
expect(dirString.tokenViaConstructor).toBe(dirString.tokenViaInjector!);
});
it('should support multiple flags in NodeInjector', () => {
@Directive({selector: '[dirA]'})
class DirectiveA {
}
@Directive({selector: '[dirB]'})
class DirectiveB {
public tokenSelfViaInjector =
this.injector.get(DirectiveA, null, InjectFlags.Self|InjectFlags.Optional);
public tokenHostViaInjector =
this.injector.get(DirectiveA, null, InjectFlags.Host|InjectFlags.Optional);
constructor(
public injector: Injector,
@Inject(DirectiveA) @Self() @Optional() public tokenSelfViaConstructor:
DirectiveA,
@Inject(DirectiveA) @Host() @Optional() public tokenHostViaConstructor:
DirectiveA) {}
}

@Component({template: '<div dirB></div>'})
class MyComp {
@ViewChild(DirectiveB) dirB!: DirectiveB;
}

TestBed.configureTestingModule({declarations: [DirectiveB, MyComp]});
const fixture = TestBed.createComponent(MyComp);
fixture.detectChanges();

const dirB = fixture.componentInstance.dirB;
expect(dirB.tokenSelfViaInjector).toBeNull();
expect(dirB.tokenSelfViaInjector).toBe(dirB.tokenSelfViaConstructor);
expect(dirB.tokenHostViaInjector).toBeNull();
expect(dirB.tokenHostViaInjector).toBe(dirB.tokenHostViaConstructor);
});
});
});
});

Expand Down