Skip to content

Commit 911bfef

Browse files
committed
fix(ivy): bindings should be checked in components created dynamically by root component' (angular#26864)
PR Close angular#26864
1 parent aadbc8a commit 911bfef

File tree

2 files changed

+64
-22
lines changed

2 files changed

+64
-22
lines changed

packages/core/src/render3/instructions.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -421,6 +421,7 @@ function renderComponentOrTemplate<T>(
421421
// Element was stored at 0 in data and directive was stored at 0 in directives
422422
// in renderComponent()
423423
setHostBindings(getTView(), hostView);
424+
refreshDynamicEmbeddedViews(hostView);
424425
componentRefresh(HEADER_OFFSET, false);
425426
}
426427
} finally {
@@ -1828,7 +1829,7 @@ export function containerRefreshEnd(): void {
18281829
* Goes over dynamic embedded views (ones created through ViewContainerRef APIs) and refreshes them
18291830
* by executing an associated template function.
18301831
*/
1831-
function refreshDynamicEmbeddedViews(lViewData: LViewData) {
1832+
export function refreshDynamicEmbeddedViews(lViewData: LViewData) {
18321833
for (let current = getLViewChild(lViewData); current !== null; current = current[NEXT]) {
18331834
// Note: current can be an LViewData or an LContainer instance, but here we are only interested
18341835
// in LContainer. We can tell it's an LContainer because its length is less than the LViewData

packages/core/test/render3/view_container_ref_spec.ts

Lines changed: 62 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1771,6 +1771,28 @@ describe('ViewContainerRef', () => {
17711771

17721772
describe('view engine compatibility', () => {
17731773

1774+
@Component({selector: 'app', template: ''})
1775+
class AppCmpt {
1776+
static ngComponentDef = defineComponent({
1777+
type: AppCmpt,
1778+
selectors: [['app']],
1779+
factory: () => new AppCmpt(
1780+
directiveInject(ViewContainerRef as any), injectComponentFactoryResolver()),
1781+
consts: 0,
1782+
vars: 0,
1783+
template: (rf: RenderFlags, cmp: AppCmpt) => {}
1784+
});
1785+
1786+
constructor(private _vcRef: ViewContainerRef, private _cfResolver: ComponentFactoryResolver) {
1787+
}
1788+
1789+
insert(comp: any) {
1790+
this._vcRef.createComponent(this._cfResolver.resolveComponentFactory(comp));
1791+
}
1792+
1793+
clear() { this._vcRef.clear(); }
1794+
}
1795+
17741796
// https://stackblitz.com/edit/angular-xxpffd?file=src%2Findex.html
17751797
it('should allow injecting VCRef into the root (bootstrapped) component', () => {
17761798

@@ -1781,39 +1803,58 @@ describe('ViewContainerRef', () => {
17811803
}
17821804
}, 1, 0);
17831805

1784-
@Component({selector: 'app', template: ''})
1785-
class AppCmpt {
1786-
static ngComponentDef = defineComponent({
1787-
type: AppCmpt,
1788-
selectors: [['app']],
1789-
factory: () => new AppCmpt(
1790-
directiveInject(ViewContainerRef as any), injectComponentFactoryResolver()),
1791-
consts: 0,
1792-
vars: 0,
1793-
template: (rf: RenderFlags, cmp: AppCmpt) => {}
1794-
});
17951806

1796-
constructor(
1797-
private _vcRef: ViewContainerRef, private _cfResolver: ComponentFactoryResolver) {}
1807+
const fixture = new ComponentFixture(AppCmpt);
1808+
expect(fixture.outerHtml).toBe('<div host="mark"></div>');
17981809

1799-
insert() {
1800-
this._vcRef.createComponent(this._cfResolver.resolveComponentFactory(DynamicComponent));
1801-
}
1810+
fixture.component.insert(DynamicComponent);
1811+
fixture.update();
1812+
expect(fixture.outerHtml)
1813+
.toBe('<div host="mark"></div><dynamic-cmpt>inserted dynamically</dynamic-cmpt>');
1814+
1815+
fixture.component.clear();
1816+
fixture.update();
1817+
expect(fixture.outerHtml).toBe('<div host="mark"></div>');
1818+
});
18021819

1803-
clear() { this._vcRef.clear(); }
1820+
it('should check bindings for components dynamically created by root component', () => {
1821+
class DynamicCompWithBindings {
1822+
checkCount = 0;
1823+
1824+
ngDoCheck() { this.checkCount++; }
1825+
1826+
/** check count: {{ checkCount }} */
1827+
static ngComponentDef = defineComponent({
1828+
type: DynamicCompWithBindings,
1829+
selectors: [['dynamic-cmpt-with-bindings']],
1830+
factory: () => new DynamicCompWithBindings(),
1831+
consts: 1,
1832+
vars: 1,
1833+
template: (rf: RenderFlags, ctx: DynamicCompWithBindings) => {
1834+
if (rf & RenderFlags.Create) {
1835+
text(0);
1836+
}
1837+
if (rf & RenderFlags.Update) {
1838+
textBinding(0, interpolation1('check count: ', ctx.checkCount, ''));
1839+
}
1840+
}
1841+
});
18041842
}
18051843

18061844
const fixture = new ComponentFixture(AppCmpt);
18071845
expect(fixture.outerHtml).toBe('<div host="mark"></div>');
18081846

1809-
fixture.component.insert();
1847+
fixture.component.insert(DynamicCompWithBindings);
18101848
fixture.update();
18111849
expect(fixture.outerHtml)
1812-
.toBe('<div host="mark"></div><dynamic-cmpt>inserted dynamically</dynamic-cmpt>');
1850+
.toBe(
1851+
'<div host="mark"></div><dynamic-cmpt-with-bindings>check count: 1</dynamic-cmpt-with-bindings>');
18131852

1814-
fixture.component.clear();
18151853
fixture.update();
1816-
expect(fixture.outerHtml).toBe('<div host="mark"></div>');
1854+
expect(fixture.outerHtml)
1855+
.toBe(
1856+
'<div host="mark"></div><dynamic-cmpt-with-bindings>check count: 2</dynamic-cmpt-with-bindings>');
18171857
});
1858+
18181859
});
18191860
});

0 commit comments

Comments
 (0)