From 0abe20f961888c970374c9ca289475cd4aea9f35 Mon Sep 17 00:00:00 2001 From: vthinkxie Date: Tue, 13 Apr 2021 10:10:35 +0800 Subject: [PATCH] feat(core): support flags option in NodeInjector.get close #31776 the InectFlags was defined in both getOrCreateInjectable and Injector, but was ignored in NodeInjector this PR support getting parent token via injector.get in NodeInjector --- packages/core/src/render3/di.ts | 4 +- packages/core/test/acceptance/di_spec.ts | 122 ++++++++++++++++++++++- 2 files changed, 123 insertions(+), 3 deletions(-) diff --git a/packages/core/src/render3/di.ts b/packages/core/src/render3/di.ts index 352d4baf801630..87cea749c2d011 100644 --- a/packages/core/src/render3/di.ts +++ b/packages/core/src/render3/di.ts @@ -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); } } diff --git a/packages/core/test/acceptance/di_spec.ts b/packages/core/test/acceptance/di_spec.ts index 69cda5ce1bac90..d73f9c8b87c60e 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, ComponentFactoryResolver, ComponentRef, ContentChild, 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'; @@ -1919,6 +1919,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('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('token'); + @Component({ + selector: 'parent', + template: '', + 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('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: '
', + 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: '
'}) + 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); + }); + }); }); });