diff --git a/packages/core/src/render3/di.ts b/packages/core/src/render3/di.ts index 5b036f4a22..b4b5ac6e26 100644 --- a/packages/core/src/render3/di.ts +++ b/packages/core/src/render3/di.ts @@ -268,12 +268,12 @@ const componentFactoryResolver: ComponentFactoryResolver = new ComponentFactoryR * @experimental */ export function injectAttribute(attrNameToInject: string): string|undefined { - ngDevMode && assertPreviousIsParent(); - const lElement = getPreviousOrParentNode() as LElementNode; - ngDevMode && assertNodeType(lElement, TNodeType.Element); - const tElement = lElement.tNode; - ngDevMode && assertDefined(tElement, 'expecting tNode'); - const attrs = tElement.attrs; + const lNode = getPreviousOrParentNode(); + ngDevMode && assertNodeOfPossibleTypes( + lNode, TNodeType.Container, TNodeType.Element, TNodeType.ElementContainer); + const tNode = lNode.tNode; + ngDevMode && assertDefined(tNode, 'expecting tNode'); + const attrs = tNode.attrs; if (attrs) { for (let i = 0; i < attrs.length; i = i + 2) { const attrName = attrs[i]; diff --git a/packages/core/test/render3/di_spec.ts b/packages/core/test/render3/di_spec.ts index d64a369751..b8d91d5859 100644 --- a/packages/core/test/render3/di_spec.ts +++ b/packages/core/test/render3/di_spec.ts @@ -6,19 +6,19 @@ * found in the LICENSE file at https://angular.io/license */ -import {ChangeDetectorRef, ElementRef, Host, InjectFlags, Optional, Self, SkipSelf, TemplateRef, ViewContainerRef, defineInjectable} from '@angular/core'; +import {Attribute, ChangeDetectorRef, ElementRef, Host, InjectFlags, Optional, Self, SkipSelf, TemplateRef, ViewContainerRef, defineInjectable} from '@angular/core'; import {RenderFlags} from '@angular/core/src/render3/interfaces/definition'; import {defineComponent} from '../../src/render3/definition'; import {bloomAdd, bloomFindPossibleInjector, getOrCreateNodeInjector, injectAttribute} from '../../src/render3/di'; import {NgOnChangesFeature, PublicFeature, defineDirective, directiveInject, injectChangeDetectorRef, injectElementRef, injectTemplateRef, injectViewContainerRef} from '../../src/render3/index'; -import {bind, container, containerRefreshEnd, containerRefreshStart, createLNode, createLViewData, createTView, element, elementEnd, elementStart, embeddedViewEnd, embeddedViewStart, enterView, interpolation2, leaveView, projection, projectionDef, reference, template, text, textBinding} from '../../src/render3/instructions'; +import {bind, container, containerRefreshEnd, containerRefreshStart, createLNode, createLViewData, createTView, element, elementEnd, elementStart, embeddedViewEnd, embeddedViewStart, enterView, interpolation2, leaveView, projection, projectionDef, reference, template, text, textBinding, loadDirective, elementContainerStart, elementContainerEnd} from '../../src/render3/instructions'; import {LInjector} from '../../src/render3/interfaces/injector'; import {AttributeMarker, TNodeType} from '../../src/render3/interfaces/node'; import {LViewFlags} from '../../src/render3/interfaces/view'; import {ViewRef} from '../../src/render3/view_ref'; -import {ComponentFixture, createComponent, createDirective, renderComponent, renderToHtml, toHtml} from './render_util'; +import {ComponentFixture, createComponent, createDirective, renderComponent, toHtml} from './render_util'; describe('di', () => { describe('no dependencies', () => { @@ -1207,6 +1207,23 @@ describe('di', () => { describe('@Attribute', () => { + class MyDirective { + exists = 'wrong' as string | undefined; + myDirective = 'wrong' as string | undefined; + constructor( + @Attribute('exist') existAttrValue: string|undefined, + @Attribute('myDirective') myDirectiveAttrValue: string|undefined) { + this.exists = existAttrValue; + this.myDirective = myDirectiveAttrValue; + } + + static ngDirectiveDef = defineDirective({ + type: MyDirective, + selectors: [['', 'myDirective', '']], + factory: () => new MyDirective(injectAttribute('exist'), injectAttribute('myDirective')) + }); + } + it('should inject attribute', () => { let exist = 'wrong' as string | undefined; let nonExist = 'wrong' as string | undefined; @@ -1224,6 +1241,48 @@ describe('di', () => { expect(nonExist).toEqual(undefined); }); + // https://stackblitz.com/edit/angular-scawyi?file=src%2Fapp%2Fapp.component.ts + it('should inject attributes on ', () => { + let myDirectiveInstance: MyDirective; + + /* */ + const MyApp = createComponent('my-app', function(rf: RenderFlags, ctx: any) { + if (rf & RenderFlags.Create) { + template( + 0, null, 0, 0, null, + ['myDirective', 'initial', 'exist', 'existValue', 'other', 'ignore']); + } + if (rf & RenderFlags.Update) { + myDirectiveInstance = loadDirective(0); + } + }, 1, 0, [MyDirective]); + + new ComponentFixture(MyApp); + expect(myDirectiveInstance !.exists).toEqual('existValue'); + expect(myDirectiveInstance !.myDirective).toEqual('initial'); + }); + + // https://stackblitz.com/edit/angular-scawyi?file=src%2Fapp%2Fapp.component.ts + it('should inject attributes on ', () => { + let myDirectiveInstance: MyDirective; + + /* */ + const MyApp = createComponent('my-app', function(rf: RenderFlags, ctx: any) { + if (rf & RenderFlags.Create) { + elementContainerStart( + 0, ['myDirective', 'initial', 'exist', 'existValue', 'other', 'ignore']); + elementContainerEnd(); + } + if (rf & RenderFlags.Update) { + myDirectiveInstance = loadDirective(0); + } + }, 1, 0, [MyDirective]); + + new ComponentFixture(MyApp); + expect(myDirectiveInstance !.exists).toEqual('existValue'); + expect(myDirectiveInstance !.myDirective).toEqual('initial'); + }); + // https://stackblitz.com/edit/angular-8ytqkp?file=src%2Fapp%2Fapp.component.ts it('should not inject attributes representing bindings and outputs', () => { let exist = 'wrong' as string | undefined;