ionic-framework: bug: angular, moving elements inside of ion-router-outlet causes dom tree mismatch during hydration

Prerequisites

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

  1. ionic start -> Angular Standalone -> list prefill
  2. ng add @angular/ssr
  3. Create app.config.ts based on main.ts
  4. 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)

Commits related to this issue

Most upvoted comments

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 of ion-router-outlet. However, the component needs to be a child of ion-router-outlet so 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#L278

This 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 setting ngSkipHydration on the component that renders ion-router-outlet.

However, this would mean setting ngSkipHydration on 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/ssr does 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/ssr to break. A good example of this is the ion-header component.

Unfortunately, this is not something we can address as the Angular team needs to add support for Web Components to the @angular/ssr package. If this is something import for your use case, I recommend providing feedback to the Angular team on https://github.com/angular/angular/issues.

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)