fix(ivy): inject attributes for directives on ng-template / ng-container (#25697)

PR Close #25697
This commit is contained in:
Pawel Kozlowski 2018-08-28 14:13:32 +02:00 committed by Matias Niemelä
parent 0fe708ff82
commit 0386c44acc
2 changed files with 68 additions and 9 deletions

View File

@ -268,12 +268,12 @@ const componentFactoryResolver: ComponentFactoryResolver = new ComponentFactoryR
* @experimental * @experimental
*/ */
export function injectAttribute(attrNameToInject: string): string|undefined { export function injectAttribute(attrNameToInject: string): string|undefined {
ngDevMode && assertPreviousIsParent(); const lNode = getPreviousOrParentNode();
const lElement = getPreviousOrParentNode() as LElementNode; ngDevMode && assertNodeOfPossibleTypes(
ngDevMode && assertNodeType(lElement, TNodeType.Element); lNode, TNodeType.Container, TNodeType.Element, TNodeType.ElementContainer);
const tElement = lElement.tNode; const tNode = lNode.tNode;
ngDevMode && assertDefined(tElement, 'expecting tNode'); ngDevMode && assertDefined(tNode, 'expecting tNode');
const attrs = tElement.attrs; const attrs = tNode.attrs;
if (attrs) { if (attrs) {
for (let i = 0; i < attrs.length; i = i + 2) { for (let i = 0; i < attrs.length; i = i + 2) {
const attrName = attrs[i]; const attrName = attrs[i];

View File

@ -6,19 +6,19 @@
* found in the LICENSE file at https://angular.io/license * 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 {RenderFlags} from '@angular/core/src/render3/interfaces/definition';
import {defineComponent} from '../../src/render3/definition'; import {defineComponent} from '../../src/render3/definition';
import {bloomAdd, bloomFindPossibleInjector, getOrCreateNodeInjector, injectAttribute} from '../../src/render3/di'; import {bloomAdd, bloomFindPossibleInjector, getOrCreateNodeInjector, injectAttribute} from '../../src/render3/di';
import {NgOnChangesFeature, PublicFeature, defineDirective, directiveInject, injectChangeDetectorRef, injectElementRef, injectTemplateRef, injectViewContainerRef} from '../../src/render3/index'; 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 {LInjector} from '../../src/render3/interfaces/injector';
import {AttributeMarker, TNodeType} from '../../src/render3/interfaces/node'; import {AttributeMarker, TNodeType} from '../../src/render3/interfaces/node';
import {LViewFlags} from '../../src/render3/interfaces/view'; import {LViewFlags} from '../../src/render3/interfaces/view';
import {ViewRef} from '../../src/render3/view_ref'; 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('di', () => {
describe('no dependencies', () => { describe('no dependencies', () => {
@ -1207,6 +1207,23 @@ describe('di', () => {
describe('@Attribute', () => { 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', () => { it('should inject attribute', () => {
let exist = 'wrong' as string | undefined; let exist = 'wrong' as string | undefined;
let nonExist = 'wrong' as string | undefined; let nonExist = 'wrong' as string | undefined;
@ -1224,6 +1241,48 @@ describe('di', () => {
expect(nonExist).toEqual(undefined); expect(nonExist).toEqual(undefined);
}); });
// https://stackblitz.com/edit/angular-scawyi?file=src%2Fapp%2Fapp.component.ts
it('should inject attributes on <ng-template>', () => {
let myDirectiveInstance: MyDirective;
/* <ng-template myDirective="initial" exist="existValue" other="ignore"></ng-template>*/
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 <ng-container>', () => {
let myDirectiveInstance: MyDirective;
/* <ng-container myDirective="initial" exist="existValue" other="ignore"></ng-container>*/
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 // https://stackblitz.com/edit/angular-8ytqkp?file=src%2Fapp%2Fapp.component.ts
it('should not inject attributes representing bindings and outputs', () => { it('should not inject attributes representing bindings and outputs', () => {
let exist = 'wrong' as string | undefined; let exist = 'wrong' as string | undefined;