You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Dynamic components aren't hydrated when root component is used as an anchor #51157
The component correctly appears in the SSR output. However, Angular does not appear to hydrate the component. Instead, it creates a new instance, and leaves the existing copy in the DOM dehydrated (and thus, broken), even after hydration completes.
Please provide a link to a minimal reproduction of the bug
ViewContainerRef.createComponent adds the component as sibling of the curremnt component. This means when you use ViewContainerRef.createComponent in the root component, the component is created outside the Angular app (It's created outside the <app-root></app-root>.
There is no issue when you do the samething in any other compoent !
Reacted by Andrew Kushnir, Gerald Monaco and hiepxanh
I've performed an initial investigation and can confirm what @JeanMeche mentioned above: the problem only happens in a root components when inject(ViewContainerRef) is used, thus transforming a value the first slot of a root LView to LContainer (from a component's LView). Related code: https://github.com/angular/angular/blob/main/packages/core/src/hydration/annotate.ts#L101-L122. The current logic basically skips serialization of all views within a root view container. The correct logic should handle such a case and go over all views within a view container recursively (since containers might potentially have nested view containers as well).
There is also 1 limitation that we would likely need to add: such view containers can not have embedded views in them (i.e. views create via ViewContainerRef.createEmbeddedView), since there is no host element there where we can place ngh id to refer to serialized info. There is a comment node (acting as a host node) available for those views, but the current hydration logic can't use those nodes as a container for ngh id info yet (we'd to support those cases in the future though for partial per-view hydration).
changed the title [-]Dynamic components aren't hydrated[/-][+]Dynamic components aren't hydrated when root component is used as an anchor[/+]on Jul 24, 2023
Note (to myself): here is a new TestBed test (can be added to the packages/platform-server/test/hydration_spec.ts) that reproduces the issue:
it('should hydrate dynamically created components using root component as an anchor', async () => {
@Component({
standalone: true,
imports: [CommonModule],
selector: 'dynamic',
template: `
<span>This is a content of a dynamic component.</span>
<b *ngIf="isServer">This is a SERVER-ONLY content</b>
<i *ngIf="!isServer">This is a CLIENT-ONLY content</i>
`,
})
class DynamicComponent {
isServer = isPlatformServer(inject(PLATFORM_ID));
}
@Component({
standalone: true,
selector: 'app',
template: `
<main>Hi! This is the main content.</main>
`,
})
class SimpleComponent {
vcr = inject(ViewContainerRef);
ngAfterViewInit() {
const compRef = this.vcr.createComponent(DynamicComponent);
compRef.changeDetectorRef.detectChanges();
}
}
const html = await ssr(SimpleComponent);
let ssrContents = getAppContents(html);
expect(ssrContents).toContain('<app ngh');
resetTViewsFor(SimpleComponent, DynamicComponent);
const appRef = await hydrate(html, SimpleComponent);
const compRef = getComponentRef<SimpleComponent>(appRef);
appRef.tick();
ssrContents = stripExcessiveSpaces(stripUtilAttributes(ssrContents, false));
// We expect to see SERVER content, but not CLIENT.
expect(ssrContents).not.toContain('<i>This is a CLIENT-ONLY content</i>');
expect(ssrContents).toContain('<b>This is a SERVER-ONLY content</b>');
const clientRootNode = compRef.location.nativeElement;
await whenStable(appRef);
const clientContents =
stripExcessiveSpaces(stripUtilAttributes(clientRootNode.outerHTML, false));
// After the cleanup, we expect to see CLIENT content, but not SERVER.
expect(clientContents).toContain('<i>This is a CLIENT-ONLY content</i>');
expect(clientContents).not.toContain('<b>This is a SERVER-ONLY content</b>');
});
Hi, I'm using ngx-dynamic-hooks which is have the same logic. Which have the same issue because of the recreation of angular component which cause by hydration logic. Can I suggest some kind of attribute mark which notice that the Hydration should do nothing with that component?
@hiepxanh I've created a fix for the problem reported in this ticket, see PR #51247. You can test (locally) if this fix also resolves the problem that you've mentioned by using build artifacts from the PR. You can try updating your package.json file and change the reference to the @angular/core package and running npm install --force:
@AndrewKushnir Oh my god! I'm really lucky to meet you, sir. Every time I get stuck in a hard issue, you just appear like a god. It has been 1 year since the last issue #46473 (comment) and we meet each other again. Thank you so much for this support. It really works and the performance has improved a lot.
This will be really useful for drag and drop or some Angular builder.
Since Angular 2 beta and Angular JS, I never lost faith among a lot of JS frameworks. And after 10 years of working with Angular, my knowledge is still usable in this version.
Thank you so much, it works! I can confirm it worked!
I hope this fix can land in the next version.
Before change, there is a flick which lost and generate the HTML again.
You did a great job tracking down the root cause of the problem to this ticket 👍
Great to hear that the fix resolves the issue in your application and you see performance improvements. The fix would go though the regular process (code review, testing and merging) and will be available in one of the upcoming Angular weekly releases. I'll keep this thread updated once the fix is released.
Activity
JeanMeche commentedon Jul 24, 2023
ViewContainerRef.createComponentadds the component as sibling of the curremnt component. This means when you useViewContainerRef.createComponentin the root component, the component is created outside the Angular app (It's created outside the<app-root></app-root>.There is no issue when you do the samething in any other compoent !
AndrewKushnir commentedon Jul 24, 2023
I've performed an initial investigation and can confirm what @JeanMeche mentioned above: the problem only happens in a root components when
inject(ViewContainerRef)is used, thus transforming a value the first slot of a root LView to LContainer (from a component's LView). Related code: https://github.com/angular/angular/blob/main/packages/core/src/hydration/annotate.ts#L101-L122. The current logic basically skips serialization of all views within a root view container. The correct logic should handle such a case and go over all views within a view container recursively (since containers might potentially have nested view containers as well).There is also 1 limitation that we would likely need to add: such view containers can not have embedded views in them (i.e. views create via
ViewContainerRef.createEmbeddedView), since there is no host element there where we can placenghid to refer to serialized info. There is a comment node (acting as a host node) available for those views, but the current hydration logic can't use those nodes as a container fornghid info yet (we'd to support those cases in the future though for partial per-view hydration).[-]Dynamic components aren't hydrated[/-][+]Dynamic components aren't hydrated when root component is used as an anchor[/+]AndrewKushnir commentedon Jul 24, 2023
Note (to myself): here is a new TestBed test (can be added to the packages/platform-server/test/hydration_spec.ts) that reproduces the issue:
hiepxanh commentedon Aug 1, 2023
Hi, I'm using ngx-dynamic-hooks which is have the same logic. Which have the same issue because of the recreation of angular component which cause by hydration logic. Can I suggest some kind of attribute mark which notice that the Hydration should do nothing with that component?
p/s: I'm using old day workaround to solve this problem Angular-Dynamic-Hooks/ngx-dynamic-hooks#34 (comment)
1 remaining item
AndrewKushnir commentedon Aug 2, 2023
@hiepxanh I've created a fix for the problem reported in this ticket, see PR #51247. You can test (locally) if this fix also resolves the problem that you've mentioned by using build artifacts from the PR. You can try updating your
package.jsonfile and change the reference to the@angular/corepackage and runningnpm install --force:Note: this archive is available only temporarily, please do not use it in production builds.
hiepxanh commentedon Aug 2, 2023
@AndrewKushnir Oh my god! I'm really lucky to meet you, sir. Every time I get stuck in a hard issue, you just appear like a god. It has been 1 year since the last issue #46473 (comment) and we meet each other again. Thank you so much for this support. It really works and the performance has improved a lot.
This will be really useful for drag and drop or some Angular builder.
Since Angular 2 beta and Angular JS, I never lost faith among a lot of JS frameworks. And after 10 years of working with Angular, my knowledge is still usable in this version.
Thank you so much, it works! I can confirm it worked!
I hope this fix can land in the next version.
Before change, there is a flick which lost and generate the HTML again.
After change, it stable no flick:

AndrewKushnir commentedon Aug 2, 2023
@hiepxanh thanks for your kind words!
You did a great job tracking down the root cause of the problem to this ticket 👍
Great to hear that the fix resolves the issue in your application and you see performance improvements. The fix would go though the regular process (code review, testing and merging) and will be available in one of the upcoming Angular weekly releases. I'll keep this thread updated once the fix is released.
Thank you.
fix(core): handle hydration of view containers for root components (#…
AndrewKushnir commentedon Aug 9, 2023
@hiepxanh just wanted to let you know that Angular v16.2.0 is out (available on NPM) and this fix is included into the release. Thank you.
hiepxanh commentedon Aug 10, 2023
I can confirm it work in the codesandbox.io https://codesandbox.io/p/sandbox/relaxed-feather-27fv8y with angular 16.2.0 version. Thank you for your support @AndrewKushnir
hiepxanh commentedon Aug 10, 2023
@AndrewKushnir thank you so much sir. But I think it lead to other issue. No bug no life, I think so.
Anyway can you take a look on this issue?
#51318
angular-automatic-lock-bot commentedon Sep 29, 2023
This issue has been automatically locked due to inactivity.
Please file a new issue if you are encountering a similar or related problem.
Read more about our automatic conversation locking policy.
This action has been performed automatically by a bot.
fix(core): handle hydration of view containers for root components (a…
fix(core): handle hydration of view containers for root components (a…