diff --git a/goldens/public-api/core/core.md b/goldens/public-api/core/core.md index aba184d60f2..3757104ee98 100644 --- a/goldens/public-api/core/core.md +++ b/goldens/public-api/core/core.md @@ -1238,7 +1238,7 @@ export type StaticProvider = ValueProvider | ExistingProvider | StaticClassProvi // @public export abstract class TemplateRef { - abstract createEmbeddedView(context: C, injector?: Injector): EmbeddedViewRef; + abstract createEmbeddedView(context: C): EmbeddedViewRef; abstract readonly elementRef: ElementRef; } @@ -1384,10 +1384,6 @@ export abstract class ViewContainerRef { }): ComponentRef; // @deprecated abstract createComponent(componentFactory: ComponentFactory, index?: number, injector?: Injector, projectableNodes?: any[][], ngModuleRef?: NgModuleRef): ComponentRef; - abstract createEmbeddedView(templateRef: TemplateRef, context?: C, options?: { - index?: number; - injector?: Injector; - }): EmbeddedViewRef; abstract createEmbeddedView(templateRef: TemplateRef, context?: C, index?: number): EmbeddedViewRef; abstract detach(index?: number): ViewRef | null; abstract get element(): ElementRef; diff --git a/packages/core/src/linker/template_ref.ts b/packages/core/src/linker/template_ref.ts index aae7da7e6ff..c7ecd07751c 100644 --- a/packages/core/src/linker/template_ref.ts +++ b/packages/core/src/linker/template_ref.ts @@ -6,12 +6,10 @@ * found in the LICENSE file at https://angular.io/license */ -import {Injector} from '../di/injector'; import {assertLContainer} from '../render3/assert'; -import {ChainedInjector} from '../render3/chained_injector'; import {createLView, renderView} from '../render3/instructions/shared'; import {TContainerNode, TNode, TNodeType} from '../render3/interfaces/node'; -import {DECLARATION_LCONTAINER, INJECTOR, LView, LViewFlags, QUERIES, TView} from '../render3/interfaces/view'; +import {DECLARATION_LCONTAINER, LView, LViewFlags, QUERIES, TView} from '../render3/interfaces/view'; import {getCurrentTNode, getLView} from '../render3/state'; import {ViewRef as R3_ViewRef} from '../render3/view_ref'; import {assertDefined} from '../util/assert'; @@ -57,10 +55,9 @@ export abstract class TemplateRef { * and attaches it to the view container. * @param context The data-binding context of the embedded view, as declared * in the `` usage. - * @param injector Injector to be used within the embedded view. * @returns The new embedded view object. */ - abstract createEmbeddedView(context: C, injector?: Injector): EmbeddedViewRef; + abstract createEmbeddedView(context: C): EmbeddedViewRef; /** * @internal @@ -80,12 +77,11 @@ const R3TemplateRef = class TemplateRef extends ViewEngineTemplateRef { super(); } - override createEmbeddedView(context: T, injector?: Injector): EmbeddedViewRef { + override createEmbeddedView(context: T): EmbeddedViewRef { const embeddedTView = this._declarationTContainer.tViews as TView; const embeddedLView = createLView( this._declarationLView, embeddedTView, context, LViewFlags.CheckAlways, null, - embeddedTView.declTNode, null, null, null, - createEmbeddedViewInjector(injector, this._declarationLView[INJECTOR])); + embeddedTView.declTNode, null, null, null, null); const declarationLContainer = this._declarationLView[this._declarationTContainer.index]; ngDevMode && assertLContainer(declarationLContainer); @@ -102,18 +98,6 @@ const R3TemplateRef = class TemplateRef extends ViewEngineTemplateRef { } }; -function createEmbeddedViewInjector( - embeddedViewInjector: Injector|undefined, declarationViewInjector: Injector|null): Injector| - null { - if (!embeddedViewInjector) { - return null; - } - - return declarationViewInjector ? - new ChainedInjector(embeddedViewInjector, declarationViewInjector) : - embeddedViewInjector; -} - /** * Creates a TemplateRef given a node. * diff --git a/packages/core/src/linker/view_container_ref.ts b/packages/core/src/linker/view_container_ref.ts index 45bb73d1e1d..3c54f2eff09 100644 --- a/packages/core/src/linker/view_container_ref.ts +++ b/packages/core/src/linker/view_container_ref.ts @@ -90,24 +90,6 @@ export abstract class ViewContainerRef { */ abstract get length(): number; - /** - * Instantiates an embedded view and inserts it - * into this container. - * @param templateRef The HTML template that defines the view. - * @param context The data-binding context of the embedded view, as declared - * in the `` usage. - * @param options Extra configuration for the created view. Includes: - * * index: The 0-based index at which to insert the new view into this container. - * If not specified, appends the new view as the last entry. - * * injector: Injector to be used within the embedded view. - * - * @returns The `ViewRef` instance for the newly created view. - */ - abstract createEmbeddedView(templateRef: TemplateRef, context?: C, options?: { - index?: number, - injector?: Injector - }): EmbeddedViewRef; - /** * Instantiates an embedded view and inserts it * into this container. @@ -276,27 +258,9 @@ const R3ViewContainerRef = class ViewContainerRef extends VE_ViewContainerRef { return this._lContainer.length - CONTAINER_HEADER_OFFSET; } - override createEmbeddedView(templateRef: TemplateRef, context?: C, options?: { - index?: number, - injector?: Injector - }): EmbeddedViewRef; override createEmbeddedView(templateRef: TemplateRef, context?: C, index?: number): - EmbeddedViewRef; - override createEmbeddedView(templateRef: TemplateRef, context?: C, indexOrOptions?: number|{ - index?: number, - injector?: Injector - }): EmbeddedViewRef { - let index: number|undefined; - let injector: Injector|undefined; - - if (typeof indexOrOptions === 'number') { - index = indexOrOptions; - } else if (indexOrOptions != null) { - index = indexOrOptions.index; - injector = indexOrOptions.injector; - } - - const viewRef = templateRef.createEmbeddedView(context || {}, injector); + EmbeddedViewRef { + const viewRef = templateRef.createEmbeddedView(context || {}); this.insert(viewRef, index); return viewRef; } diff --git a/packages/core/src/render3/chained_injector.ts b/packages/core/src/render3/chained_injector.ts deleted file mode 100644 index 7a1b841156b..00000000000 --- a/packages/core/src/render3/chained_injector.ts +++ /dev/null @@ -1,36 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.io/license - */ - -import {Injector} from '../di/injector'; -import {InjectFlags} from '../di/interface/injector'; -import {ProviderToken} from '../di/provider_token'; -import {NOT_FOUND_CHECK_ONLY_ELEMENT_INJECTOR} from '../view/provider_flags'; - -/** - * Injector that looks up a value using a specific injector, before falling back to the module - * injector. Used primarily when creating components or embedded views dynamically. - */ -export class ChainedInjector implements Injector { - constructor(private injector: Injector, private parentInjector: Injector) {} - - get(token: ProviderToken, notFoundValue?: T, flags?: InjectFlags): T { - const value = this.injector.get(token, NOT_FOUND_CHECK_ONLY_ELEMENT_INJECTOR as T, flags); - - if (value !== NOT_FOUND_CHECK_ONLY_ELEMENT_INJECTOR || - notFoundValue === NOT_FOUND_CHECK_ONLY_ELEMENT_INJECTOR) { - // Return the value from the root element injector when - // - it provides it - // (value !== NOT_FOUND_CHECK_ONLY_ELEMENT_INJECTOR) - // - the module injector should not be checked - // (notFoundValue === NOT_FOUND_CHECK_ONLY_ELEMENT_INJECTOR) - return value; - } - - return this.parentInjector.get(token, notFoundValue, flags); - } -} diff --git a/packages/core/src/render3/component_ref.ts b/packages/core/src/render3/component_ref.ts index 488a46f0e38..131303d61ca 100644 --- a/packages/core/src/render3/component_ref.ts +++ b/packages/core/src/render3/component_ref.ts @@ -9,6 +9,8 @@ import {ChangeDetectorRef as ViewEngine_ChangeDetectorRef} from '../change_detection/change_detector_ref'; import {InjectionToken} from '../di/injection_token'; import {Injector} from '../di/injector'; +import {InjectFlags} from '../di/interface/injector'; +import {ProviderToken} from '../di/provider_token'; import {Type} from '../interface/type'; import {ComponentFactory as viewEngine_ComponentFactory, ComponentRef as viewEngine_ComponentRef} from '../linker/component_factory'; import {ComponentFactoryResolver as viewEngine_ComponentFactoryResolver} from '../linker/component_factory_resolver'; @@ -17,9 +19,9 @@ import {NgModuleRef as viewEngine_NgModuleRef} from '../linker/ng_module_factory import {RendererFactory2} from '../render/api'; import {Sanitizer} from '../sanitization/sanitizer'; import {VERSION} from '../version'; +import {NOT_FOUND_CHECK_ONLY_ELEMENT_INJECTOR} from '../view/provider_flags'; import {assertComponentType} from './assert'; -import {ChainedInjector} from './chained_injector'; import {createRootComponent, createRootComponentView, createRootContext, LifecycleHooksFeature} from './component'; import {getComponentDef} from './definition'; import {NodeInjector} from './di'; @@ -78,6 +80,26 @@ export const SCHEDULER = new InjectionToken<((fn: () => void) => void)>('SCHEDUL factory: () => defaultScheduler, }); +function createChainedInjector(rootViewInjector: Injector, moduleInjector: Injector): Injector { + return { + get: (token: ProviderToken, notFoundValue?: T, flags?: InjectFlags): T => { + const value = rootViewInjector.get(token, NOT_FOUND_CHECK_ONLY_ELEMENT_INJECTOR as T, flags); + + if (value !== NOT_FOUND_CHECK_ONLY_ELEMENT_INJECTOR || + notFoundValue === NOT_FOUND_CHECK_ONLY_ELEMENT_INJECTOR) { + // Return the value from the root element injector when + // - it provides it + // (value !== NOT_FOUND_CHECK_ONLY_ELEMENT_INJECTOR) + // - the module injector should not be checked + // (notFoundValue === NOT_FOUND_CHECK_ONLY_ELEMENT_INJECTOR) + return value; + } + + return moduleInjector.get(token, notFoundValue, flags); + } + }; +} + /** * Render3 implementation of {@link viewEngine_ComponentFactory}. */ @@ -114,11 +136,11 @@ export class ComponentFactory extends viewEngine_ComponentFactory { ngModule?: viewEngine_NgModuleRef|undefined): viewEngine_ComponentRef { ngModule = ngModule || this.ngModule; - const rootViewInjector = ngModule ? new ChainedInjector(injector, ngModule.injector) : injector; + const rootViewInjector = + ngModule ? createChainedInjector(injector, ngModule.injector) : injector; const rendererFactory = - rootViewInjector.get(RendererFactory2, domRendererFactory3 as RendererFactory2) as - RendererFactory3; + rootViewInjector.get(RendererFactory2, domRendererFactory3) as RendererFactory3; const sanitizer = rootViewInjector.get(Sanitizer, null); const hostRenderer = rendererFactory.createRenderer(null, this.componentDef); diff --git a/packages/core/src/render3/interfaces/injector.ts b/packages/core/src/render3/interfaces/injector.ts index e4a7255ac76..09b7fcd10af 100644 --- a/packages/core/src/render3/interfaces/injector.ts +++ b/packages/core/src/render3/interfaces/injector.ts @@ -48,7 +48,7 @@ import {LView, TData} from './view'; * index + 7: cumulative bloom filter * index + 8: cumulative bloom filter * index + TNODE: TNode associated with this `NodeInjector` - * `const tNode = tView.data[index + NodeInjectorOffset.TNODE]` + * `canst tNode = tView.data[index + NodeInjectorOffset.TNODE]` * ``` */ export const enum NodeInjectorOffset { diff --git a/packages/core/test/acceptance/di_spec.ts b/packages/core/test/acceptance/di_spec.ts index 4450a874536..a348745229c 100644 --- a/packages/core/test/acceptance/di_spec.ts +++ b/packages/core/test/acceptance/di_spec.ts @@ -3546,603 +3546,4 @@ describe('di', () => { TestBed.configureTestingModule({declarations: [App]}); expect(() => TestBed.createComponent(App)).toThrowError(/NullInjectorError/); }); - - describe('injector when creating embedded view', () => { - const token = new InjectionToken('greeting'); - - @Directive({selector: 'menu-trigger'}) - class MenuTrigger { - @Input('triggerFor') menu!: TemplateRef; - - constructor(private viewContainerRef: ViewContainerRef) {} - - open(injector: Injector|undefined) { - this.viewContainerRef.createEmbeddedView(this.menu, undefined, {injector}); - } - } - - it('should be able to provide an injection token through a custom injector', () => { - @Directive({selector: 'menu'}) - class Menu { - constructor(@Inject(token) public tokenValue: string) {} - } - - @Component({ - template: ` - - - - - ` - }) - class App { - @ViewChild(MenuTrigger) trigger!: MenuTrigger; - @ViewChild(Menu) menu!: Menu; - } - - TestBed.configureTestingModule({declarations: [App, MenuTrigger, Menu]}); - const injector = Injector.create({providers: [{provide: token, useValue: 'hello'}]}); - const fixture = TestBed.createComponent(App); - fixture.detectChanges(); - - fixture.componentInstance.trigger.open(injector); - fixture.detectChanges(); - - expect(fixture.componentInstance.menu.tokenValue).toBe('hello'); - }); - - it('should be able to provide an injection token to a nested template through a custom injector', - () => { - @Directive({selector: 'menu'}) - class Menu { - constructor(@Inject(token) public tokenValue: string) {} - } - - @Component({ - template: ` - - - - - - - - - - ` - }) - class App { - @ViewChild('outerTrigger', {read: MenuTrigger}) outerTrigger!: MenuTrigger; - @ViewChild('innerTrigger', {read: MenuTrigger}) innerTrigger!: MenuTrigger; - @ViewChild('innerMenu', {read: Menu}) innerMenu!: Menu; - } - - TestBed.configureTestingModule({declarations: [App, MenuTrigger, Menu]}); - const fixture = TestBed.createComponent(App); - fixture.detectChanges(); - - fixture.componentInstance.outerTrigger.open( - Injector.create({providers: [{provide: token, useValue: 'hello'}]})); - fixture.detectChanges(); - - fixture.componentInstance.innerTrigger.open(undefined); - fixture.detectChanges(); - - expect(fixture.componentInstance.innerMenu.tokenValue).toBe('hello'); - }); - - it('should be able to resolve a token from a custom grandparent injector if the token is not provided in the parent', - () => { - @Directive({selector: 'menu'}) - class Menu { - constructor(@Inject(token) public tokenValue: string) {} - } - - @Component({ - template: ` - - - - - - - - - - - - - - - ` - }) - class App { - @ViewChild('grandparentTrigger', {read: MenuTrigger}) grandparentTrigger!: MenuTrigger; - @ViewChild('parentTrigger', {read: MenuTrigger}) parentTrigger!: MenuTrigger; - @ViewChild('childTrigger', {read: MenuTrigger}) childTrigger!: MenuTrigger; - @ViewChild('childMenu', {read: Menu}) childMenu!: Menu; - } - - TestBed.configureTestingModule({declarations: [App, MenuTrigger, Menu]}); - const fixture = TestBed.createComponent(App); - fixture.detectChanges(); - - fixture.componentInstance.grandparentTrigger.open( - Injector.create({providers: [{provide: token, useValue: 'hello'}]})); - fixture.detectChanges(); - - fixture.componentInstance.parentTrigger.open(Injector.create({providers: []})); - fixture.detectChanges(); - - fixture.componentInstance.childTrigger.open(undefined); - fixture.detectChanges(); - - expect(fixture.componentInstance.childMenu.tokenValue).toBe('hello'); - }); - - it('should resolve value from node injector if it is lower than embedded view injector', () => { - @Directive({selector: 'menu'}) - class Menu { - constructor(@Inject(token) public tokenValue: string) {} - } - - @Component({ - selector: 'wrapper', - providers: [{provide: token, useValue: 'hello from wrapper'}], - template: ` - - - - - ` - }) - class Wrapper { - @ViewChild(MenuTrigger) trigger!: MenuTrigger; - @ViewChild(Menu) menu!: Menu; - } - - @Component({ - template: ` - - - - - ` - }) - class App { - @ViewChild(MenuTrigger) trigger!: MenuTrigger; - @ViewChild(Wrapper) wrapper!: Wrapper; - } - - TestBed.configureTestingModule({declarations: [App, MenuTrigger, Menu, Wrapper]}); - const fixture = TestBed.createComponent(App); - fixture.detectChanges(); - - fixture.componentInstance.trigger.open( - Injector.create({providers: [{provide: token, useValue: 'hello from injector'}]})); - fixture.detectChanges(); - - fixture.componentInstance.wrapper.trigger.open(undefined); - fixture.detectChanges(); - - expect(fixture.componentInstance.wrapper.menu.tokenValue).toBe('hello from wrapper'); - }); - - it('should be able to inject a value provided at the module level', () => { - @Directive({selector: 'menu'}) - class Menu { - constructor(@Inject(token) public tokenValue: string) {} - } - - @Component({ - template: ` - - - - - ` - }) - class App { - @ViewChild(MenuTrigger) trigger!: MenuTrigger; - @ViewChild(Menu) menu!: Menu; - } - - @NgModule({ - declarations: [App, MenuTrigger, Menu], - exports: [App, MenuTrigger, Menu], - providers: [{provide: token, useValue: 'hello'}] - }) - class Module { - } - - TestBed.configureTestingModule({imports: [Module]}); - const injector = Injector.create({providers: []}); - const fixture = TestBed.createComponent(App); - fixture.detectChanges(); - - fixture.componentInstance.trigger.open(injector); - fixture.detectChanges(); - - expect(fixture.componentInstance.menu.tokenValue).toBe('hello'); - }); - - it('should have value from custom injector take precedence over module injector', () => { - @Directive({selector: 'menu'}) - class Menu { - constructor(@Inject(token) public tokenValue: string) {} - } - - @Component({ - template: ` - - - - - ` - }) - class App { - @ViewChild(MenuTrigger) trigger!: MenuTrigger; - @ViewChild(Menu) menu!: Menu; - } - - @NgModule({ - declarations: [App, MenuTrigger, Menu], - exports: [App, MenuTrigger, Menu], - providers: [{provide: token, useValue: 'hello from module'}] - }) - class Module { - } - - TestBed.configureTestingModule({imports: [Module]}); - const injector = - Injector.create({providers: [{provide: token, useValue: 'hello from injector'}]}); - const fixture = TestBed.createComponent(App); - fixture.detectChanges(); - - fixture.componentInstance.trigger.open(injector); - fixture.detectChanges(); - - expect(fixture.componentInstance.menu.tokenValue).toBe('hello from injector'); - }); - - it('should be able to inject built-in tokens when a custom injector is provided', () => { - @Directive({selector: 'menu'}) - class Menu { - constructor(public elementRef: ElementRef, public changeDetectorRef: ChangeDetectorRef) {} - } - - @Component({ - template: ` - - - - - ` - }) - class App { - @ViewChild(MenuTrigger) trigger!: MenuTrigger; - @ViewChild(Menu) menu!: Menu; - } - - TestBed.configureTestingModule({declarations: [App, MenuTrigger, Menu]}); - const injector = Injector.create({providers: []}); - const fixture = TestBed.createComponent(App); - fixture.detectChanges(); - - fixture.componentInstance.trigger.open(injector); - fixture.detectChanges(); - - expect(fixture.componentInstance.menu.elementRef.nativeElement) - .toBe(fixture.nativeElement.querySelector('menu')); - expect(fixture.componentInstance.menu.changeDetectorRef).toBeTruthy(); - }); - - it('should have value from parent component injector take precedence over module injector', - () => { - @Directive({selector: 'menu'}) - class Menu { - constructor(@Inject(token) public tokenValue: string) {} - } - - @Component({ - template: ` - - - - - `, - providers: [{provide: token, useValue: 'hello from parent'}] - }) - class App { - @ViewChild(MenuTrigger) trigger!: MenuTrigger; - @ViewChild(Menu) menu!: Menu; - } - - @NgModule({ - declarations: [App, MenuTrigger, Menu], - exports: [App, MenuTrigger, Menu], - providers: [{provide: token, useValue: 'hello from module'}] - }) - class Module { - } - - TestBed.configureTestingModule({imports: [Module]}); - const injector = Injector.create({providers: []}); - const fixture = TestBed.createComponent(App); - fixture.detectChanges(); - - fixture.componentInstance.trigger.open(injector); - fixture.detectChanges(); - - expect(fixture.componentInstance.menu.tokenValue).toBe('hello from parent'); - }); - - it('should be able to inject an injectable with dependencies', () => { - @Injectable() - class Greeter { - constructor(@Inject(token) private tokenValue: string) {} - - greet() { - return `hello from ${this.tokenValue}`; - } - } - - @Directive({selector: 'menu'}) - class Menu { - constructor(public greeter: Greeter) {} - } - - @Component({ - template: ` - - - - - ` - }) - class App { - @ViewChild(MenuTrigger) trigger!: MenuTrigger; - @ViewChild(Menu) menu!: Menu; - } - - @NgModule({ - declarations: [App, MenuTrigger, Menu], - exports: [App, MenuTrigger, Menu], - providers: [{provide: token, useValue: 'module'}] - }) - class Module { - } - - TestBed.configureTestingModule({imports: [Module]}); - const injector = Injector.create({ - providers: [ - {provide: Greeter, useClass: Greeter}, - {provide: token, useValue: 'injector'}, - ] - }); - const fixture = TestBed.createComponent(App); - fixture.detectChanges(); - - fixture.componentInstance.trigger.open(injector); - fixture.detectChanges(); - - expect(fixture.componentInstance.menu.greeter.greet()).toBe('hello from injector'); - }); - - it('should be able to inject a value from a grandparent component when a custom injector is provided', - () => { - @Directive({selector: 'menu'}) - class Menu { - constructor(@Inject(token) public tokenValue: string) {} - } - - @Component({ - selector: 'parent', - template: ` - - - - - ` - }) - class Parent { - @ViewChild(MenuTrigger) trigger!: MenuTrigger; - @ViewChild(Menu) menu!: Menu; - } - - @Component({ - template: '', - providers: [{provide: token, useValue: 'hello from grandparent'}] - }) - class GrandParent { - @ViewChild(Parent) parent!: Parent; - } - - TestBed.configureTestingModule({declarations: [GrandParent, Parent, MenuTrigger, Menu]}); - const injector = Injector.create({providers: []}); - const fixture = TestBed.createComponent(GrandParent); - fixture.detectChanges(); - - fixture.componentInstance.parent.trigger.open(injector); - fixture.detectChanges(); - - expect(fixture.componentInstance.parent.menu.tokenValue).toBe('hello from grandparent'); - }); - - it('should be able to use a custom injector when created through TemplateRef', () => { - let injectedValue: string|undefined; - - @Directive({selector: 'menu'}) - class Menu { - constructor(@Inject(token) tokenValue: string) { - injectedValue = tokenValue; - } - } - - @Component({ - template: ` - - - - ` - }) - class App { - @ViewChild(TemplateRef) template!: TemplateRef; - } - - @NgModule({ - declarations: [App, Menu], - exports: [App, Menu], - providers: [{provide: token, useValue: 'hello from module'}] - }) - class Module { - } - - TestBed.configureTestingModule({imports: [Module]}); - const injector = - Injector.create({providers: [{provide: token, useValue: 'hello from injector'}]}); - const fixture = TestBed.createComponent(App); - fixture.detectChanges(); - - fixture.componentInstance.template.createEmbeddedView({}, injector); - fixture.detectChanges(); - - expect(injectedValue).toBe('hello from injector'); - }); - - it('should use a custom injector when the view is created outside of the declaration view', - () => { - const declarerToken = new InjectionToken('declarerToken'); - const creatorToken = new InjectionToken('creatorToken'); - - @Directive({selector: 'menu'}) - class Menu { - constructor( - @Inject(token) public tokenValue: string, - @Optional() @Inject(declarerToken) public declarerTokenValue: string, - @Optional() @Inject(creatorToken) public creatorTokenValue: string) {} - } - - @Component({ - selector: 'declarer', - template: '', - providers: [{provide: declarerToken, useValue: 'hello from declarer'}] - }) - class Declarer { - @ViewChild(Menu) menu!: Menu; - @ViewChild(TemplateRef) template!: TemplateRef; - } - - @Component({ - selector: 'creator', - template: '', - providers: [{provide: creatorToken, useValue: 'hello from creator'}] - }) - class Creator { - @ViewChild(MenuTrigger) trigger!: MenuTrigger; - } - - @Component({ - template: ` - - - ` - }) - class App { - @ViewChild(Declarer) declarer!: Declarer; - @ViewChild(Creator) creator!: Creator; - } - - TestBed.configureTestingModule( - {declarations: [App, MenuTrigger, Menu, Declarer, Creator]}); - const injector = Injector.create({providers: [{provide: token, useValue: 'hello'}]}); - const fixture = TestBed.createComponent(App); - fixture.detectChanges(); - const {declarer, creator} = fixture.componentInstance; - - creator.trigger.menu = declarer.template; - creator.trigger.open(injector); - fixture.detectChanges(); - - expect(declarer.menu.tokenValue).toBe('hello'); - expect(declarer.menu.declarerTokenValue).toBe('hello from declarer'); - expect(declarer.menu.creatorTokenValue).toBeNull(); - }); - - it('should behave consistently with `createComponent` when token is shadowed in node injector', - () => { - @Directive({selector: 'trigger'}) - class Trigger { - constructor(public viewContainerRef: ViewContainerRef) {} - } - - @Directive({selector: 'overlay'}) - class Overlay { - constructor(@Inject(token) public tokenValue: string) {} - } - - @Component({ - selector: 'overlay-host', - template: '', - providers: [{provide: token, useValue: 'hello from parent'}], - }) - class OverlayHost { - @ViewChild(Overlay) overlay!: Overlay; - } - - @Component({ - selector: 'wrapper', - template: '', - providers: [{provide: token, useValue: 'hello from parent'}], - }) - class Wrapper { - } - - @Component({ - template: ` - - - - - - - - ` - }) - class App { - @ViewChild(Trigger) trigger!: Trigger; - @ViewChild('template', {read: TemplateRef}) template!: TemplateRef; - @ViewChild(Overlay) overlayInTemplate!: Overlay; - - openFromTemplate(injector: Injector) { - this.trigger.viewContainerRef.createEmbeddedView(this.template, null, {injector}); - } - - openFromComponent(injector: Injector) { - return this.trigger.viewContainerRef.createComponent(OverlayHost, {injector}); - } - } - - TestBed.configureTestingModule( - {declarations: [App, Trigger, Overlay, OverlayHost, Wrapper]}); - const fixture = TestBed.createComponent(App); - fixture.detectChanges(); - const providers = [{provide: token, useValue: 'hello from custom injector'}]; - - fixture.componentInstance.openFromTemplate(Injector.create({providers})); - fixture.detectChanges(); - - const componentRef = - fixture.componentInstance.openFromComponent(Injector.create({providers})); - fixture.detectChanges(); - - // The node injector is expected to take precedence over the provided injector, despite - // technically being higher in the tree, because the custom one is provided as a module - // injector. This is consistent with how `createComponent` has always worked and avoids - // ambiguity as to whether the provided injector should be in the declaration or insertion - // node injector tree. - expect(fixture.componentInstance.overlayInTemplate.tokenValue).toBe('hello from parent'); - expect(componentRef.instance.overlay.tokenValue).toBe('hello from parent'); - }); - }); }); diff --git a/packages/core/test/bundling/forms_reactive/bundle.golden_symbols.json b/packages/core/test/bundling/forms_reactive/bundle.golden_symbols.json index 81664ac20b5..afe8691c865 100644 --- a/packages/core/test/bundling/forms_reactive/bundle.golden_symbols.json +++ b/packages/core/test/bundling/forms_reactive/bundle.golden_symbols.json @@ -77,9 +77,6 @@ { "name": "COMPOSITION_BUFFER_MODE" }, - { - "name": "ChainedInjector" - }, { "name": "ChangeDetectionStrategy" }, diff --git a/packages/core/test/bundling/forms_template_driven/bundle.golden_symbols.json b/packages/core/test/bundling/forms_template_driven/bundle.golden_symbols.json index d0ae40574d8..add4b427c14 100644 --- a/packages/core/test/bundling/forms_template_driven/bundle.golden_symbols.json +++ b/packages/core/test/bundling/forms_template_driven/bundle.golden_symbols.json @@ -77,9 +77,6 @@ { "name": "COMPOSITION_BUFFER_MODE" }, - { - "name": "ChainedInjector" - }, { "name": "ChangeDetectionStrategy" }, diff --git a/packages/core/test/bundling/router/bundle.golden_symbols.json b/packages/core/test/bundling/router/bundle.golden_symbols.json index 788d072262c..3331cc8aaf1 100644 --- a/packages/core/test/bundling/router/bundle.golden_symbols.json +++ b/packages/core/test/bundling/router/bundle.golden_symbols.json @@ -92,9 +92,6 @@ { "name": "CatchSubscriber" }, - { - "name": "ChainedInjector" - }, { "name": "ChangeDetectionStrategy" }, diff --git a/packages/core/test/bundling/todo/bundle.golden_symbols.json b/packages/core/test/bundling/todo/bundle.golden_symbols.json index a530f251ded..f2904bb9b5e 100644 --- a/packages/core/test/bundling/todo/bundle.golden_symbols.json +++ b/packages/core/test/bundling/todo/bundle.golden_symbols.json @@ -2,9 +2,6 @@ { "name": "CLEAN_PROMISE" }, - { - "name": "ChainedInjector" - }, { "name": "ChangeDetectionStrategy" },