Skip to content

Commit

Permalink
fix(core): support InjectFlags argument in NodeInjector.get() (#4…
Browse files Browse the repository at this point in the history
…1592)

The `InjectFlags` argument was defined for both `getOrCreateInjectable()` and `Injector`, but was ignored in `NodeInjector`.
This PR adds support for getting parent token via `NodeInjector.get()`.

close #31776

PR Close #41592
  • Loading branch information
vthinkxie authored and alxhub committed Oct 28, 2021
1 parent 515ba60 commit 8f402c9
Show file tree
Hide file tree
Showing 2 changed files with 123 additions and 3 deletions.
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

0 comments on commit 8f402c9

Please sign in to comment.