diff --git a/goldens/public-api/core/core.d.ts b/goldens/public-api/core/core.d.ts index 21137006a197a0..659ff7b1e5dec9 100644 --- a/goldens/public-api/core/core.d.ts +++ b/goldens/public-api/core/core.d.ts @@ -449,7 +449,7 @@ export declare class InjectionToken { export declare abstract class Injector { abstract get(token: ProviderToken, notFoundValue?: T, flags?: InjectFlags): T; - /** @deprecated */ abstract get(token: any, notFoundValue?: any): any; + /** @deprecated */ abstract get(token: any, notFoundValue?: any, flags?: InjectFlags): any; static NULL: Injector; static THROW_IF_NOT_FOUND: {}; static ɵprov: unknown; diff --git a/packages/core/src/di/injector.ts b/packages/core/src/di/injector.ts index a299d9a66410d2..ce999ec8efb3c8 100644 --- a/packages/core/src/di/injector.ts +++ b/packages/core/src/di/injector.ts @@ -71,7 +71,7 @@ export abstract class Injector { * @deprecated from v4.0.0 use ProviderToken * @suppress {duplicate} */ - abstract get(token: any, notFoundValue?: any): any; + abstract get(token: any, notFoundValue?: any, flags?: InjectFlags): any; /** * @deprecated from v5 use the new signature Injector.create(options) 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..3a4d952e2aa147 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,100 @@ describe('di', () => { }); }); }); + + onlyInIvy('Ivy support InjectFlags in Injector') + .it('should support Optional flag in Injector', () => { + @Directive({selector: '[dirA]'}) + class DirectiveA { + dirB = this.injector.get(DirectiveB, null, InjectFlags.Optional); + constructor(public injector: 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(); + }); + + onlyInIvy('Ivy support InjectFlags in Injector') + .it('should support SkipSelf flag in Injector', () => { + @Component({ + selector: 'parent', + template: '', + providers: [{ + provide: 'token', + useValue: 'PARENT', + }] + }) + class ParentComponent { + } + + @Component({ + selector: 'child', + template: '...', + providers: [{ + provide: 'token', + useValue: 'CHILD', + }] + }) + class ChildComponent { + constructor(public injector: Injector, @SkipSelf() public parentInjector: Injector) {} + } + + TestBed.configureTestingModule({ + declarations: [ParentComponent, ChildComponent], + }); + const fixture = TestBed.createComponent(ParentComponent); + fixture.detectChanges(); + + const childComponent = + fixture.debugElement.query(By.directive(ChildComponent)).componentInstance; + expect(childComponent.injector.get('token')).toBe('CHILD'); + expect(childComponent.injector.get('token', null, InjectFlags.SkipSelf)).toBe('PARENT'); + expect(childComponent.parentInjector.get('token')).toBe('PARENT'); + }); + + onlyInIvy('Ivy support InjectFlags in Injector') + .it('should support Host flag in Injector', () => { + @Directive({selector: '[dirA]'}) + class DirectiveA { + dirB = this.injector.get(DirectiveB, null, InjectFlags.Host); + constructor(public injector: Injector) {} + } + + @Directive({selector: '[dirString]'}) + class DirectiveString { + s = this.injector.get(String, null, InjectFlags.Host); + constructor(public injector: Injector) {} + } + + @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'); + }); }); });