Skip to content

Commit 9bfe428

Browse files
AndrewKushnirmhevery
authored andcommitted
fix(ivy): call hostBindings function with proper element index (angular#27694)
We invoked `hostBindings` function in Create and Update modes with different element index due to the fact that we did not subtract HEADER_OFFSET from the index before passing it to `hostBindings` function in Create mode. Now we subtract HEADER_OFFSET value before invoking `hostBindings`, which makes Ceate and Update calls consistent. PR Close angular#27694
1 parent 4c1cd1b commit 9bfe428

File tree

3 files changed

+57
-22
lines changed

3 files changed

+57
-22
lines changed

packages/core/src/render3/component.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -198,7 +198,7 @@ export function createRootComponent<T>(
198198
if (tView.firstTemplatePass && componentDef.hostBindings) {
199199
const rootTNode = getPreviousOrParentTNode();
200200
setCurrentDirectiveDef(componentDef);
201-
componentDef.hostBindings(RenderFlags.Create, component, rootTNode.index);
201+
componentDef.hostBindings(RenderFlags.Create, component, rootTNode.index - HEADER_OFFSET);
202202
setCurrentDirectiveDef(null);
203203
}
204204

packages/core/src/render3/instructions.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1541,7 +1541,7 @@ function invokeDirectivesHostBindings(tView: TView, viewData: LView, tNode: TNod
15411541
if (def.hostBindings) {
15421542
const previousExpandoLength = expando.length;
15431543
setCurrentDirectiveDef(def);
1544-
def.hostBindings !(RenderFlags.Create, directive, tNode.index);
1544+
def.hostBindings !(RenderFlags.Create, directive, tNode.index - HEADER_OFFSET);
15451545
setCurrentDirectiveDef(null);
15461546
// `hostBindings` function may or may not contain `allocHostVars` call
15471547
// (e.g. it may not if it only contains host listeners), so we need to check whether

packages/core/test/render3/host_binding_spec.ts

Lines changed: 55 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ describe('host bindings', () => {
5454
allocHostVars(1);
5555
}
5656
if (rf & RenderFlags.Update) {
57-
elementProperty(elementIndex, 'id', bind(ctx.id));
57+
elementProperty(elementIndex, 'id', bind(ctx.id), null, true);
5858
}
5959
}
6060
});
@@ -75,7 +75,7 @@ describe('host bindings', () => {
7575
allocHostVars(1);
7676
}
7777
if (rf & RenderFlags.Update) {
78-
elementProperty(elIndex, 'id', bind(ctx.id));
78+
elementProperty(elIndex, 'id', bind(ctx.id), null, true);
7979
}
8080
},
8181
template: (rf: RenderFlags, ctx: HostBindingComp) => {}
@@ -84,7 +84,7 @@ describe('host bindings', () => {
8484

8585
it('should support host bindings in directives', () => {
8686
let directiveInstance: Directive|undefined;
87-
87+
const elementIndices: number[] = [];
8888
class Directive {
8989
// @HostBinding('className')
9090
klass = 'foo';
@@ -94,11 +94,12 @@ describe('host bindings', () => {
9494
selectors: [['', 'dir', '']],
9595
factory: () => directiveInstance = new Directive,
9696
hostBindings: (rf: RenderFlags, ctx: any, elementIndex: number) => {
97+
elementIndices.push(elementIndex);
9798
if (rf & RenderFlags.Create) {
9899
allocHostVars(1);
99100
}
100101
if (rf & RenderFlags.Update) {
101-
elementProperty(elementIndex, 'className', bind(ctx.klass));
102+
elementProperty(elementIndex, 'className', bind(ctx.klass), null, true);
102103
}
103104
}
104105
});
@@ -112,15 +113,46 @@ describe('host bindings', () => {
112113
directiveInstance !.klass = 'bar';
113114
fixture.update();
114115
expect(fixture.html).toEqual('<span class="bar"></span>');
116+
117+
// verify that we always call `hostBindings` function with the same element index
118+
expect(elementIndices.every(id => id === elementIndices[0])).toBeTruthy();
115119
});
116120

117121
it('should support host bindings on root component', () => {
122+
const elementIndices: number[] = [];
123+
124+
class HostBindingComp {
125+
// @HostBinding()
126+
id = 'my-id';
127+
128+
static ngComponentDef = defineComponent({
129+
type: HostBindingComp,
130+
selectors: [['host-binding-comp']],
131+
factory: () => new HostBindingComp(),
132+
consts: 0,
133+
vars: 0,
134+
hostBindings: (rf: RenderFlags, ctx: HostBindingComp, elIndex: number) => {
135+
elementIndices.push(elIndex);
136+
if (rf & RenderFlags.Create) {
137+
allocHostVars(1);
138+
}
139+
if (rf & RenderFlags.Update) {
140+
elementProperty(elIndex, 'id', bind(ctx.id), null, true);
141+
}
142+
},
143+
template: (rf: RenderFlags, ctx: HostBindingComp) => {}
144+
});
145+
}
146+
118147
const fixture = new ComponentFixture(HostBindingComp);
119148
expect(fixture.hostElement.id).toBe('my-id');
120149

121150
fixture.component.id = 'other-id';
122151
fixture.update();
123152
expect(fixture.hostElement.id).toBe('other-id');
153+
154+
// verify that we always call `hostBindings` function with the same element index
155+
expect(elementIndices.every(id => id === elementIndices[0])).toBeTruthy();
124156
});
125157

126158
it('should support host bindings on nodes with providers', () => {
@@ -150,7 +182,7 @@ describe('host bindings', () => {
150182
allocHostVars(1);
151183
}
152184
if (rf & RenderFlags.Update) {
153-
elementProperty(elIndex, 'id', bind(ctx.id));
185+
elementProperty(elIndex, 'id', bind(ctx.id), null, true);
154186
}
155187
},
156188
template: (rf: RenderFlags, ctx: CompWithProviders) => {},
@@ -186,7 +218,7 @@ describe('host bindings', () => {
186218
allocHostVars(1);
187219
}
188220
if (rf & RenderFlags.Update) {
189-
elementProperty(elIndex, 'title', bind(ctx.title));
221+
elementProperty(elIndex, 'title', bind(ctx.title), null, true);
190222
}
191223
},
192224
template: (rf: RenderFlags, ctx: HostTitleComp) => {}
@@ -239,7 +271,7 @@ describe('host bindings', () => {
239271
allocHostVars(1);
240272
}
241273
if (rf & RenderFlags.Update) {
242-
elementProperty(elIndex, 'id', bind(ctx.id));
274+
elementProperty(elIndex, 'id', bind(ctx.id), null, true);
243275
}
244276
},
245277
template: (rf: RenderFlags, ctx: HostBindingComp) => {}
@@ -329,7 +361,7 @@ describe('host bindings', () => {
329361
allocHostVars(1);
330362
}
331363
if (rf & RenderFlags.Update) {
332-
elementProperty(elIndex, 'title', bind(ctx.value));
364+
elementProperty(elIndex, 'title', bind(ctx.value), null, true);
333365
}
334366
},
335367
inputs: {inputValue: 'inputValue'}
@@ -562,10 +594,11 @@ describe('host bindings', () => {
562594
allocHostVars(8);
563595
}
564596
if (rf & RenderFlags.Update) {
565-
elementProperty(elIndex, 'id', bind(pureFunction1(3, ff, ctx.id)));
566-
elementProperty(elIndex, 'dir', bind(ctx.dir));
597+
elementProperty(elIndex, 'id', bind(pureFunction1(3, ff, ctx.id)), null, true);
598+
elementProperty(elIndex, 'dir', bind(ctx.dir), null, true);
567599
elementProperty(
568-
elIndex, 'title', bind(pureFunction2(5, ff2, ctx.title, ctx.otherTitle)));
600+
elIndex, 'title', bind(pureFunction2(5, ff2, ctx.title, ctx.otherTitle)), null,
601+
true);
569602
}
570603
},
571604
template: (rf: RenderFlags, ctx: HostBindingComp) => {}
@@ -640,7 +673,7 @@ describe('host bindings', () => {
640673
allocHostVars(3);
641674
}
642675
if (rf & RenderFlags.Update) {
643-
elementProperty(elIndex, 'id', bind(pureFunction1(1, ff, ctx.id)));
676+
elementProperty(elIndex, 'id', bind(pureFunction1(1, ff, ctx.id)), null, true);
644677
}
645678
},
646679
template: (rf: RenderFlags, ctx: HostBindingComp) => {}
@@ -671,7 +704,7 @@ describe('host bindings', () => {
671704
allocHostVars(3);
672705
}
673706
if (rf & RenderFlags.Update) {
674-
elementProperty(elIndex, 'title', bind(pureFunction1(1, ff1, ctx.title)));
707+
elementProperty(elIndex, 'title', bind(pureFunction1(1, ff1, ctx.title)), null, true);
675708
}
676709
}
677710
});
@@ -728,7 +761,7 @@ describe('host bindings', () => {
728761
allocHostVars(3);
729762
}
730763
if (rf & RenderFlags.Update) {
731-
elementProperty(elIndex, 'title', bind(pureFunction1(1, ff1, ctx.title)));
764+
elementProperty(elIndex, 'title', bind(pureFunction1(1, ff1, ctx.title)), null, true);
732765
}
733766
}
734767
});
@@ -799,10 +832,12 @@ describe('host bindings', () => {
799832
}
800833
if (rf & RenderFlags.Update) {
801834
elementProperty(
802-
elIndex, 'id', bind(ctx.condition ? pureFunction1(2, ff, ctx.id) : 'green'));
835+
elIndex, 'id', bind(ctx.condition ? pureFunction1(2, ff, ctx.id) : 'green'), null,
836+
true);
803837
elementProperty(
804838
elIndex, 'title',
805-
bind(ctx.otherCondition ? pureFunction1(4, ff1, ctx.title) : 'other title'));
839+
bind(ctx.otherCondition ? pureFunction1(4, ff1, ctx.title) : 'other title'), null,
840+
true);
806841
}
807842
},
808843
template: (rf: RenderFlags, ctx: HostBindingComp) => {}
@@ -859,7 +894,7 @@ describe('host bindings', () => {
859894
allocHostVars(1);
860895
}
861896
if (rf & RenderFlags.Update) {
862-
elementProperty(elementIndex, 'id', bind(ctx.id));
897+
elementProperty(elementIndex, 'id', bind(ctx.id), null, true);
863898
}
864899
},
865900
factory: () => superDir = new SuperDirective(),
@@ -877,7 +912,7 @@ describe('host bindings', () => {
877912
allocHostVars(1);
878913
}
879914
if (rf & RenderFlags.Update) {
880-
elementProperty(elementIndex, 'title', bind(ctx.title));
915+
elementProperty(elementIndex, 'title', bind(ctx.title), null, true);
881916
}
882917
},
883918
factory: () => subDir = new SubDirective(),
@@ -965,7 +1000,7 @@ describe('host bindings', () => {
9651000
allocHostVars(1);
9661001
}
9671002
if (rf & RenderFlags.Update) {
968-
elementProperty(elIndex, 'id', bind(ctx.foos.length));
1003+
elementProperty(elIndex, 'id', bind(ctx.foos.length), null, true);
9691004
}
9701005
},
9711006
contentQueries: (dirIndex) => { registerContentQuery(query(null, ['foo']), dirIndex); },
@@ -1024,7 +1059,7 @@ describe('host bindings', () => {
10241059
allocHostVars(1);
10251060
}
10261061
if (rf & RenderFlags.Update) {
1027-
elementProperty(elIndex, 'id', bind(ctx.myValue));
1062+
elementProperty(elIndex, 'id', bind(ctx.myValue), null, true);
10281063
}
10291064
},
10301065
template: (rf: RenderFlags, cmp: HostBindingWithContentHooks) => {}

0 commit comments

Comments
 (0)