Skip to content

Commit

Permalink
feat(core): support flags option in NodeInjector.get
Browse files Browse the repository at this point in the history
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
  • Loading branch information
vthinkxie committed Apr 29, 2021
1 parent 55d9713 commit cbb39e5
Show file tree
Hide file tree
Showing 4 changed files with 99 additions and 5 deletions.
2 changes: 1 addition & 1 deletion goldens/public-api/core/core.d.ts
Expand Up @@ -449,7 +449,7 @@ export declare class InjectionToken<T> {

export declare abstract class Injector {
abstract get<T>(token: ProviderToken<T>, 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;
Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/di/injector.ts
Expand Up @@ -71,7 +71,7 @@ export abstract class Injector {
* @deprecated from v4.0.0 use ProviderToken<T>
* @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)
Expand Down
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
96 changes: 95 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, 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';
Expand Down Expand Up @@ -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: '<div dirA></div>'})
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: '<child></child>',
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: '<div dirString></div>',
viewProviders: [{provide: String, useValue: 'Foo'}]
})
class MyComp {
@ViewChild(DirectiveString) dirString!: DirectiveString;
}

@Component({template: '<my-comp></my-comp>'})
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');
});
});
});

Expand Down

0 comments on commit cbb39e5

Please sign in to comment.