diff --git a/packages/core/src/render3/component.ts b/packages/core/src/render3/component.ts index 595d1ac598..1a50c67d7e 100644 --- a/packages/core/src/render3/component.ts +++ b/packages/core/src/render3/component.ts @@ -198,7 +198,7 @@ export function createRootComponent( if (tView.firstTemplatePass && componentDef.hostBindings) { const rootTNode = getPreviousOrParentTNode(); setCurrentDirectiveDef(componentDef); - componentDef.hostBindings(RenderFlags.Create, component, rootTNode.index); + componentDef.hostBindings(RenderFlags.Create, component, rootTNode.index - HEADER_OFFSET); setCurrentDirectiveDef(null); } diff --git a/packages/core/src/render3/instructions.ts b/packages/core/src/render3/instructions.ts index 30522ebc1f..80d11dcf31 100644 --- a/packages/core/src/render3/instructions.ts +++ b/packages/core/src/render3/instructions.ts @@ -1541,7 +1541,7 @@ function invokeDirectivesHostBindings(tView: TView, viewData: LView, tNode: TNod if (def.hostBindings) { const previousExpandoLength = expando.length; setCurrentDirectiveDef(def); - def.hostBindings !(RenderFlags.Create, directive, tNode.index); + def.hostBindings !(RenderFlags.Create, directive, tNode.index - HEADER_OFFSET); setCurrentDirectiveDef(null); // `hostBindings` function may or may not contain `allocHostVars` call // (e.g. it may not if it only contains host listeners), so we need to check whether diff --git a/packages/core/test/render3/host_binding_spec.ts b/packages/core/test/render3/host_binding_spec.ts index c87ae25259..ec03af03e9 100644 --- a/packages/core/test/render3/host_binding_spec.ts +++ b/packages/core/test/render3/host_binding_spec.ts @@ -54,7 +54,7 @@ describe('host bindings', () => { allocHostVars(1); } if (rf & RenderFlags.Update) { - elementProperty(elementIndex, 'id', bind(ctx.id)); + elementProperty(elementIndex, 'id', bind(ctx.id), null, true); } } }); @@ -75,7 +75,7 @@ describe('host bindings', () => { allocHostVars(1); } if (rf & RenderFlags.Update) { - elementProperty(elIndex, 'id', bind(ctx.id)); + elementProperty(elIndex, 'id', bind(ctx.id), null, true); } }, template: (rf: RenderFlags, ctx: HostBindingComp) => {} @@ -84,7 +84,7 @@ describe('host bindings', () => { it('should support host bindings in directives', () => { let directiveInstance: Directive|undefined; - + const elementIndices: number[] = []; class Directive { // @HostBinding('className') klass = 'foo'; @@ -94,11 +94,12 @@ describe('host bindings', () => { selectors: [['', 'dir', '']], factory: () => directiveInstance = new Directive, hostBindings: (rf: RenderFlags, ctx: any, elementIndex: number) => { + elementIndices.push(elementIndex); if (rf & RenderFlags.Create) { allocHostVars(1); } if (rf & RenderFlags.Update) { - elementProperty(elementIndex, 'className', bind(ctx.klass)); + elementProperty(elementIndex, 'className', bind(ctx.klass), null, true); } } }); @@ -112,15 +113,46 @@ describe('host bindings', () => { directiveInstance !.klass = 'bar'; fixture.update(); expect(fixture.html).toEqual(''); + + // verify that we always call `hostBindings` function with the same element index + expect(elementIndices.every(id => id === elementIndices[0])).toBeTruthy(); }); it('should support host bindings on root component', () => { + const elementIndices: number[] = []; + + class HostBindingComp { + // @HostBinding() + id = 'my-id'; + + static ngComponentDef = defineComponent({ + type: HostBindingComp, + selectors: [['host-binding-comp']], + factory: () => new HostBindingComp(), + consts: 0, + vars: 0, + hostBindings: (rf: RenderFlags, ctx: HostBindingComp, elIndex: number) => { + elementIndices.push(elIndex); + if (rf & RenderFlags.Create) { + allocHostVars(1); + } + if (rf & RenderFlags.Update) { + elementProperty(elIndex, 'id', bind(ctx.id), null, true); + } + }, + template: (rf: RenderFlags, ctx: HostBindingComp) => {} + }); + } + const fixture = new ComponentFixture(HostBindingComp); expect(fixture.hostElement.id).toBe('my-id'); fixture.component.id = 'other-id'; fixture.update(); expect(fixture.hostElement.id).toBe('other-id'); + + // verify that we always call `hostBindings` function with the same element index + expect(elementIndices.every(id => id === elementIndices[0])).toBeTruthy(); }); it('should support host bindings on nodes with providers', () => { @@ -150,7 +182,7 @@ describe('host bindings', () => { allocHostVars(1); } if (rf & RenderFlags.Update) { - elementProperty(elIndex, 'id', bind(ctx.id)); + elementProperty(elIndex, 'id', bind(ctx.id), null, true); } }, template: (rf: RenderFlags, ctx: CompWithProviders) => {}, @@ -186,7 +218,7 @@ describe('host bindings', () => { allocHostVars(1); } if (rf & RenderFlags.Update) { - elementProperty(elIndex, 'title', bind(ctx.title)); + elementProperty(elIndex, 'title', bind(ctx.title), null, true); } }, template: (rf: RenderFlags, ctx: HostTitleComp) => {} @@ -239,7 +271,7 @@ describe('host bindings', () => { allocHostVars(1); } if (rf & RenderFlags.Update) { - elementProperty(elIndex, 'id', bind(ctx.id)); + elementProperty(elIndex, 'id', bind(ctx.id), null, true); } }, template: (rf: RenderFlags, ctx: HostBindingComp) => {} @@ -329,7 +361,7 @@ describe('host bindings', () => { allocHostVars(1); } if (rf & RenderFlags.Update) { - elementProperty(elIndex, 'title', bind(ctx.value)); + elementProperty(elIndex, 'title', bind(ctx.value), null, true); } }, inputs: {inputValue: 'inputValue'} @@ -562,10 +594,11 @@ describe('host bindings', () => { allocHostVars(8); } if (rf & RenderFlags.Update) { - elementProperty(elIndex, 'id', bind(pureFunction1(3, ff, ctx.id))); - elementProperty(elIndex, 'dir', bind(ctx.dir)); + elementProperty(elIndex, 'id', bind(pureFunction1(3, ff, ctx.id)), null, true); + elementProperty(elIndex, 'dir', bind(ctx.dir), null, true); elementProperty( - elIndex, 'title', bind(pureFunction2(5, ff2, ctx.title, ctx.otherTitle))); + elIndex, 'title', bind(pureFunction2(5, ff2, ctx.title, ctx.otherTitle)), null, + true); } }, template: (rf: RenderFlags, ctx: HostBindingComp) => {} @@ -640,7 +673,7 @@ describe('host bindings', () => { allocHostVars(3); } if (rf & RenderFlags.Update) { - elementProperty(elIndex, 'id', bind(pureFunction1(1, ff, ctx.id))); + elementProperty(elIndex, 'id', bind(pureFunction1(1, ff, ctx.id)), null, true); } }, template: (rf: RenderFlags, ctx: HostBindingComp) => {} @@ -671,7 +704,7 @@ describe('host bindings', () => { allocHostVars(3); } if (rf & RenderFlags.Update) { - elementProperty(elIndex, 'title', bind(pureFunction1(1, ff1, ctx.title))); + elementProperty(elIndex, 'title', bind(pureFunction1(1, ff1, ctx.title)), null, true); } } }); @@ -728,7 +761,7 @@ describe('host bindings', () => { allocHostVars(3); } if (rf & RenderFlags.Update) { - elementProperty(elIndex, 'title', bind(pureFunction1(1, ff1, ctx.title))); + elementProperty(elIndex, 'title', bind(pureFunction1(1, ff1, ctx.title)), null, true); } } }); @@ -799,10 +832,12 @@ describe('host bindings', () => { } if (rf & RenderFlags.Update) { elementProperty( - elIndex, 'id', bind(ctx.condition ? pureFunction1(2, ff, ctx.id) : 'green')); + elIndex, 'id', bind(ctx.condition ? pureFunction1(2, ff, ctx.id) : 'green'), null, + true); elementProperty( elIndex, 'title', - bind(ctx.otherCondition ? pureFunction1(4, ff1, ctx.title) : 'other title')); + bind(ctx.otherCondition ? pureFunction1(4, ff1, ctx.title) : 'other title'), null, + true); } }, template: (rf: RenderFlags, ctx: HostBindingComp) => {} @@ -859,7 +894,7 @@ describe('host bindings', () => { allocHostVars(1); } if (rf & RenderFlags.Update) { - elementProperty(elementIndex, 'id', bind(ctx.id)); + elementProperty(elementIndex, 'id', bind(ctx.id), null, true); } }, factory: () => superDir = new SuperDirective(), @@ -877,7 +912,7 @@ describe('host bindings', () => { allocHostVars(1); } if (rf & RenderFlags.Update) { - elementProperty(elementIndex, 'title', bind(ctx.title)); + elementProperty(elementIndex, 'title', bind(ctx.title), null, true); } }, factory: () => subDir = new SubDirective(), @@ -965,7 +1000,7 @@ describe('host bindings', () => { allocHostVars(1); } if (rf & RenderFlags.Update) { - elementProperty(elIndex, 'id', bind(ctx.foos.length)); + elementProperty(elIndex, 'id', bind(ctx.foos.length), null, true); } }, contentQueries: (dirIndex) => { registerContentQuery(query(null, ['foo']), dirIndex); }, @@ -1024,7 +1059,7 @@ describe('host bindings', () => { allocHostVars(1); } if (rf & RenderFlags.Update) { - elementProperty(elIndex, 'id', bind(ctx.myValue)); + elementProperty(elIndex, 'id', bind(ctx.myValue), null, true); } }, template: (rf: RenderFlags, cmp: HostBindingWithContentHooks) => {}