ionic-framework: bug: angular, moving elements inside of ion-router-outlet causes dom tree mismatch during hydration
Prerequisites
- I have read the Contributing Guidelines.
- I agree to follow the Code of Conduct.
- I have searched for existing issues that already report this problem, without success.
Ionic Framework Version
v7.x
Current Behavior
When starting a new project with ionic start with the option Angular Standalone, then @angular/ssr is not working correctly. When adding @angular/ssr, the Angular CLI is adding files, such as app.config.server.ts. This file is referring to app.config.ts. This file is however not present, so you have to create is. Not a big deal.
However, after npm run dev:serve, the application is running, but gets an error:
main.ts:12 ERROR Error: NG0501: During hydration Angular expected more sibling nodes to be present.
Actual DOM is:
<ion-app>
…
<ion-router-outlet>…</ion-router-outlet>
<!-- container --> <-- AT THIS LOCATION
</ion-app>
To fix this problem:
* check corresponding component for hydration-related issues
* check to see if your template has valid HTML structure
* or skip hydration by adding the 'ngSkipHydration' attribute to its host node in a template
Find more at https://angular.io/errors/NG0501
at validateSiblingNodeExists (core.mjs:18669:15)
at siblingAfter (core.mjs:19187:22)
at locateDehydratedViewsInContainer (core.mjs:19395:32)
at populateDehydratedViewsInLContainerImpl (core.mjs:20174:44)
at locateOrCreateAnchorNode (core.mjs:20189:10)
at createContainerRef (core.mjs:20086:5)
at injectViewContainerRef (core.mjs:19846:12)
at core.mjs:4188:29
at runInInjectorProfilerContext (core.mjs:867:9)
at lookupTokenUsingNodeInjector (core.mjs:4187:17)
This is just a starter project with ‘list’ prefill.
Expected Behavior
The application should run correctly, and must be able to hydrate the components.
Steps to Reproduce
- ionic start -> Angular Standalone -> list prefill
- ng add @angular/ssr
- Create app.config.ts based on main.ts
- npm run dev:ssr
Code Reproduction URL
No response
Ionic Info
Ionic:
Ionic CLI : 7.1.5 (/usr/local/lib/node_modules/@ionic/cli) Ionic Framework : @ionic/angular 7.5.4 @angular-devkit/build-angular : 17.0.0 @angular-devkit/schematics : 17.0.0 @angular/cli : 17.0.0 @ionic/angular-toolkit : 9.0.0
Capacitor:
Capacitor CLI : 5.5.1 @capacitor/android : not installed @capacitor/core : 5.5.1 @capacitor/ios : not installed
Utility:
cordova-res : not installed globally native-run (update available: 2.0.0) : 1.7.4
System:
NodeJS : v18.18.2 (/usr/local/Cellar/node@18/18.18.2/bin/node) npm : 9.8.1 OS : macOS Unknown
Additional Information
No response
About this issue
- Original URL
- State: closed
- Created 8 months ago
- Reactions: 1
- Comments: 15 (7 by maintainers)
Thanks! So the problem appears to be that we are moving children inside of
ion-router-outlet. By default, the current view’s component is mounted as a sibling ofion-router-outlet. However, the component needs to be a child ofion-router-outletso things like page transitions and swipe to go back can work properly.To work around this, we move the element inside of
ion-router-outlet: https://github.com/ionic-team/ionic-framework/blob/9d57758e3ec1095c6eb951d2640b95084b711bbc/packages/angular/common/src/directives/navigation/stack-controller.ts#L278This causes a DOM tree mismatch during hydration because Angular was expecting the view’s component to be a sibling of
ion-router-outlet. According to https://angular.io/guide/hydration#third-party-libraries-with-dom-manipulation, you can avoid this by settingngSkipHydrationon the component that rendersion-router-outlet.However, this would mean setting
ngSkipHydrationon the root element which defeats the purpose of having hydration in the first place. To fix this, we likely need to update our implementation to ensure that view components are mounted in the correct place the first time so we don’t need to move anything around in the DOM.Also wanted to set appropriate expectations:
@angular/ssrdoes not support Web Components at the moment, so even if we fix this issue (which we should as there are positive performance implications for apps if we address this) there will still be other issues. For example, Stencil renders comments inside of scoped components for style encapsulation. These comments cause@angular/ssrto break. A good example of this is theion-headercomponent.Unfortunately, this is not something we can address as the Angular team needs to add support for Web Components to the
@angular/ssrpackage. If this is something import for your use case, I recommend providing feedback to the Angular team on https://github.com/angular/angular/issues.Relevant link: https://github.com/angular/angular/issues/52275
I am not able to use that, that one is based on ngModules and I am using Angular Standalone (as the preferred solution nowadays). And Angular SSR should work out of the box in v17 (maybe this is related: https://forum.ionicframework.com/t/angular-universal-update/213522/2)