Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support for client hydration for Web Components hydrated in the server. #52275

Open
dchicurel opened this issue Oct 18, 2023 · 5 comments
Open
Assignees
Labels
area: core Issues related to the framework runtime core: hydration
Milestone

Comments

@dchicurel
Copy link

Which @angular/* package(s) are the source of the bug?

platform-browser, platform-server

Is this a regression?

No

Description

First of all, a huge thanks for your continuous efforts in improving SSR and with this new non-destructive hydration feature.

We've been exploring this new feature and have seen promising results. However, we have encounter a big challenge when trying to integrate it with the server hydration of Web Components, specifically with Stencil.

Our Angular components are predominantly composed of elements from our Stencil components library. To improve rendering and mitigate flickering of the Web Components, we've been pre-rendering them using Stencil’s renderToString method for server-side hydration. This approach has been effective until now but seems incompatible with Angular's non-destructive hydration. The method takes the entire generated HTML (result from Angular SSR rendering) and renders the Web Components, producing a new full HTML page, which certainly modifies the DOM and triggers the NG0500 Hydration error.

We understand that the ideal would be to avoid the DOM manipulation and for that, it would be needed to have some kind of per-component hydration method in Stencil which I think is not available at the moment, and wouldn't be too easy to implement.

I was thinking that a potential resolution might involve implementing a specific hydration method in the Stencil Angular wrapper, allowing hydration via Angular's DOM renderer functions instead of direct HTML generation. However, I think this wouldn't be easy and might essentially be like implementing a full Stencil to Angular compiler rather than just a wrapper.

I these solutions are not possible, we would have to skip the hydration of all the web components with ngSkipHydration but I think that doesn't solve the issue and by doing this, we would be losing most of the advantages of having the client hydration.

This is one of the main issues we are encountering now and preventing us of enabling the non-destructive hydration with provideClientHydration.

While I understand that this might not be a direct issue with Angular and the new hydration feature, any guidance on how this might be resolved or mitigated would be very valuable. Also, I was wondering if there might be a way to facilitate collaboration with Stencil or other Web Component libraries to propose a solution. Actually, are you aware if this issue has been addressed in some way in Lit, or is there a possibility for collaboration with that team?

I've created this Sandbox to reproduce the problem and it would be useful for exploring potential solutions as it hosts the Stencil components in the same environment.
(It should run well on first load, or it can be downloaded as well and run with ./start.sh script. Basically, it install and build the Stencil components and the Angular wrapper, and then the Angular SSR server)

Thank you for considering this issue, and we look forward to any insights or suggestions you might have.

Please provide a link to a minimal reproduction of the bug

https://codesandbox.io/p/sandbox/angular-ssr-ap-r3s9vc

Please provide the exception or error you saw

Error: NG0500: During hydration Angular expected a text node but found a comment node.

Angular expected this DOM:

<my-component _ngcontent-ng-c2448950787="" class="hydrated">
  #text(Click here)  <-- AT THIS LOCATION
  …
</my-component>

Actual DOM is:

<my-component _ngcontent-ng-c2448950787="" class="hydrated">
  <!--  -->  <-- AT THIS LOCATION
  …
</my-component>

Note: attributes are only displayed to better represent the DOM but have no effect on hydration mismatches.

To fix this problem:
  * check the "AppComponent" 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/NG0500

Please provide the environment you discovered this bug in (run ng version)

Angular CLI: 16.2.1
Node: 18.13.0
Package Manager: npm 8.19.3
OS: darwin x64

Angular: 16.2.4
... animations, common, compiler, compiler-cli, core, forms
... platform-browser, platform-browser-dynamic, platform-server
... router

Package                         Version
---------------------------------------------------------
@angular-devkit/architect       0.1602.1
@angular-devkit/build-angular   16.2.1
@angular-devkit/core            16.2.1
@angular-devkit/schematics      16.2.1
@angular/cli                    16.2.1
@nguniversal/builders           16.2.0
@nguniversal/express-engine     16.2.0
@schematics/angular             16.2.1
rxjs                            7.8.1
typescript                      5.1.6
zone.js                         0.13.3

Anything else?

No response

@AndrewKushnir AndrewKushnir self-assigned this Oct 19, 2023
@AndrewKushnir AndrewKushnir added area: core Issues related to the framework runtime core: defer Issues related to @defer blocks. labels Oct 19, 2023
@ngbot ngbot bot added this to the needsTriage milestone Oct 19, 2023
@AndrewKushnir AndrewKushnir added core: hydration and removed core: defer Issues related to @defer blocks. labels Oct 19, 2023
@adrianiskandar
Copy link

+1 on this issue. We heavily rely on web components, and the new Angular hydration method doesn't adequately support the rendering of web components. As @dchicurel mentioned, the user experience is as described. This issue is preventing us from embracing the new non-destructive hydration feature and will block us to upgrade to Angular 17 where the new hydration method is used by default I believe.

While the old destructive hydration method sort of works, it results in a poor user experience. Users briefly see the full page, then it goes blank for a moment, or they experience page flickering before it finally renders. This behavior also negatively impacts our web performance metrics. I sincerely hope we can receive some guidance on this matter and find the best way to resolve it. Thank you.

@Jefftopia
Copy link
Contributor

Jefftopia commented Oct 20, 2023

+1

I am investing more in web-component design systems for low-level cross-framework compatibility.
I am not server-rendering web-components yet, but I'd like to. I'm using a mixture of Lit and Stencil web-components.
As a temporary aid to CLS issues, I am setting pre-hydrated dimensions with css for web-components.

The design system components focus on style, not behavior. E.g., a button component may just have:

html`<slot @onclick="this.handleClick"></slot>`

with some constructed slot styles added.

The following usage is intended:

my-button.component.ts

@Component({
  selector: 'some-button',
  template: `
    <design-system-button> // provided by the design system
      <button onClick="handleClick()"></button>
    </<design-system-button>
  `,
    standalone: true,
    schemas: [CUSTOM_ELEMENTS_SCHEMA]
})
export class SomeButtonComponent {
  public handleClick() {}
}

Another example is a web-component to add a global header and footer to all pages. The Angular app is slotted into the header/footer component:

index.html

<page-wrapper> <!-- global header and footer applied -->
  <my-angular-app-root></my-angular-app-root> <!-- angular app is projected via a <slot> -->
</page-wrapper>

Two consumption patterns:

Web components can be build-time dependencies or runtime dependencies. Runtime is nice for largely static or independently managed components that call their own services; e.g. a "microfrontend". Apps consuming these MFE's that have little to no interaction can get updates for "free" by not needing to upgrade npm dependencies for these components. The page-wrapper above is a good example.

Alternatively, web-components can be build-time integrations from npm like with material design today.

Final thoughts

Today, with web-components being CSR'd in my apps, Angular SSR appears to just ignore custom elements, hence, the main drawback is found in flash of content as the web-components are hydrated on the client after SSR takes place.

@ahmedmohamedrashad01
Copy link

Hello,
Could you please help me i have VPS with Cyberpanel and uploaded dist folder but SEO not working and there is no HTML tags on source code ,
Thanks

@dgp1130
Copy link
Contributor

dgp1130 commented Dec 12, 2023

This issue is preventing us from embracing the new non-destructive hydration feature and will block us to upgrade to Angular 17 where the new hydration method is used by default I believe.

I'm pretty sure you can remove provideClientHydration() to stick with the old mechanism, so this shouldn't block upgrade to v17. Enabling non-destructive hydration is not required to use v17.

For additional context, I don't believe web components currently have a defined model for SSR and I think every framework mostly does its own thing here. There is a proposed community protocol for SSR which could define an interoperable protocol, but it is still quite early there.

webcomponents-cg/community-protocols#7

@JSMike
Copy link

JSMike commented Apr 2, 2024

One potential solution could be adding config for a list of component selectors (and/or regex for a set of selectors) and a related function to call to do the SSR render for that component, and a way to append/prepend any needed functions/snippets that are required for hydration. Then maybe add a flag to enable memoization of the component and the attributes passed in for performance.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area: core Issues related to the framework runtime core: hydration
Projects
None yet
Development

No branches or pull requests

7 participants