angular: Rehydration doesn't work with throw error `el.setAttribute is not a function` in Angular 16 rc.1
Which @angular/* package(s) are the source of the bug?
core
Is this a regression?
No
Description
When we enable provideClientHydration(withNoHttpTransferCache())
option in AppModule and check ssr hydration result, I got some error which is unhandled by NG0XXX error code.
I don’t know how to reproduce this error in stackblitz yet.
But hope it could be helpful if we provide callstack for error message first. Here is the error callstack.
Please provide a link to a minimal reproduction of the bug
https://github.com/leo6104/ng-rehydrate-error-repro
Please provide the exception or error you saw
re.mjs:10057 ERROR TypeError: el.setAttribute is not a function
at EmulatedEncapsulationDomRenderer2.setAttribute (platform-browser.mjs:592:16)
at BaseAnimationRenderer.setAttribute (animations.mjs:270:23)
at setUpAttributes (core.mjs:1020:26)
at setupStaticAttributes (core.mjs:7535:9)
at ɵɵelementStart (core.mjs:15088:5)
at _a5_ng_container_0_Template (_a5.js:1:1)
at ReactiveLViewConsumer.runInContext (core.mjs:10311:13)
at executeTemplate (core.mjs:11069:18)
at renderView (core.mjs:10889:13)
at TemplateRef2.createEmbeddedViewImpl (core.mjs:22766:9)
h
another error case (same but little more detail callstack)
handled Promise rejection: el.setAttribute is not a function ; Zone: <root> ; Task: Promise.then ; Value: TypeError: el.setAttribute is not a function
at NoneEncapsulationDomRenderer.setAttribute (platform-browser.mjs:592:16)
at BaseAnimationRenderer.setAttribute (animations.mjs:270:23)
at setUpAttributes (core.mjs:1020:26)
at setupStaticAttributes (core.mjs:7535:9)
at ɵɵelementStart (core.mjs:15088:5)
at _a5_ng_container_0_Template (template.html:6:5)
at ReactiveLViewConsumer.runInContext (core.mjs:10311:13)
at executeTemplate (core.mjs:11069:18)
at renderView (core.mjs:10889:13)
at TemplateRef2.createEmbeddedViewImpl (core.mjs:22766:9)
TypeError: el.setAttribute is not a function
at NoneEncapsulationDomRenderer.setAttribute (https://.../chunk-SET2APWZ.js:16109:14)
at BaseAnimationRenderer.setAttribute (https://.../chunk-XDNB56EC.js:4246:23)
at setUpAttributes (https://.../chunk-YTYK4I53.js:4664:18)
at setupStaticAttributes (https://.../chunk-YTYK4I53.js:7713:5)
at ɵɵelementStart (https://.../chunk-YTYK4I53.js:11031:3)
at _a5_ng_container_0_Template (ng:///_a5.js:10:5)
at ReactiveLViewConsumer.runInContext (https://.../chunk-YTYK4I53.js:17559:11)
at executeTemplate (https://.../chunk-YTYK4I53.js:9029:14)
at renderView (https://.../chunk-YTYK4I53.js:8884:7)
at TemplateRef2.createEmbeddedViewImpl (https://.../chunk-YTYK4I53.js:18567:9)
a
Please provide the environment you discovered this bug in (run ng version
)
Angular CLI: 16.0.0-rc.0
Node: 16.19.0
Package Manager: npm 8.19.3
OS: darwin x64
Angular: 16.0.0-rc.1
... animations, common, compiler, compiler-cli, core, elements
... forms, localize, platform-browser, platform-browser-dynamic
... platform-server, router, service-worker
Package Version
---------------------------------------------------------
@angular-devkit/architect 0.1600.0-rc.0
@angular-devkit/build-angular 16.0.0-rc.0
@angular-devkit/core 16.0.0-rc.0
@angular-devkit/schematics 16.0.0-rc.0
@angular/bazel 15.2.6
@angular/cdk 16.0.0-rc.0
@angular/cli 16.0.0-rc.0
@angular/material 16.0.0-rc.0
@angular/youtube-player 16.0.0-next.2
@nguniversal/builders 16.0.0-next.0
@nguniversal/common 16.0.0-next.0
@nguniversal/express-engine 16.0.0-next.0
@schematics/angular 16.0.0-rc.0
ng-packagr 16.0.0-rc.0
rxjs 7.8.0
typescript 5.0.3
Anything else?
the component html markup is here and got error in *ngIf="d.header"
condition.
<ng-container *ngLet="{
fullWidth: fullWidth$ | async,
header: header$ | async
} as d">
<div class="board-section" [class.full-width]="d.fullWidth" cdkScrollable>
<global-navigation-bar *ngIf="d.header"></global-navigation-bar>
<div class="body-container" [class.show-header]="d.header">
<router-outlet></router-outlet>
</div>
<km-footer />
</div>
</ng-container>
<ng-template #fabWrapperContainer></ng-template>
root-cmp
decorator
@Component({
selector: 'root-cmp',
templateUrl: './root-cmp.component.html',
styleUrls: [
'./root-cmp.component.scss'
],
host: {
toastContainer: 'true' // ToastContainerDirective,
},
})
and header$ variable use ngrx route data
header$ = this.store.select(selectRouteData).pipe(
map(({ header }) => !header || header !== 'hide'),
distinctUntilChanged(),
);
ngLet directive
interface LetContext<T> {
ngLet: T;
}
@Directive({
selector: '[ngLet]',
standalone: true,
})
export class LetDirective<T> {
private _context: LetContext<T> = { ngLet: null };
constructor(_viewContainer: ViewContainerRef, _templateRef: TemplateRef<LetContext<T>>) {
_viewContainer.createEmbeddedView(_templateRef, this._context);
}
@Input()
set ngLet(value: T) {
this._context.ngLet = value;
}
}
About this issue
- Original URL
- State: closed
- Created a year ago
- Comments: 17 (16 by maintainers)
Commits related to this issue
- fix(core): include inner ViewContainerRef anchor nodes into ViewRef.rootNodes output Currently, the `ViewRef.rootNodes` output is missing anchor (comment) nodes for inner `ViewContainerRef`s, when an... — committed to AndrewKushnir/angular by AndrewKushnir a year ago
- fix(core): include inner ViewContainerRef anchor nodes into ViewRef.rootNodes output (#49867) Currently, the `ViewRef.rootNodes` output is missing anchor (comment) nodes for inner `ViewContainerRef`s... — committed to angular/angular by AndrewKushnir a year ago
@AndrewKushnir Thanks for suggestion!
We made first-version test util and it works well. Its error message shows all child components hydration mismatch. It makes we find the root cause component of hydration error.
[Result]
With more investigation, i will make new feature-request issue in next week and share brief concept for these
assertionsForHydration
utils.@leo6104 thanks for the reply and for all your help to investigate the problems! 👍
Yes, that is a quite tricky case. We will look into improving the detection for such cases.
Thanks for the feedback.
Testing utilities for applications that use hydration is something that we’d like to explore and collect more feedback about. Could you please open a new ticket (a feature request) and describe a solution that would work for your application and test scenarios? We’ll also use the ticket to collect the feedback from the community to capture more use-cases that we’d need to take into account.
@AndrewKushnir Actually, there is no special reason we putting
preserveWhitespaces: true
option. It was preserved by legacy. We have been use angular for 6 years (migrated from angularjs in 2016) and at some momenttsconfig.json
includedpreserveWhitespaces: true
option. (maybe it was default option.)when we setup initial SSR part few years ago, we want to reduce html size so we just turn off
preserveWhitespaces
intsconfig.server.json
to remove all whitespaces in prerendered html. (without changingpreserveWhitespaces
in tsconfig.app.json)It was the reason we have mismatch value between
tsconfig.app.json
andtsconfig.server.json
At this moment, we can turn off
preserveWhitespaces
. it is no problem.However, its difference between two tsconfig files shows
\n
text node
hydration error and Developer cannot expect the error came frompreserveWhitespaces
option. 😅 It could be the point angular can improve.BTW, it would be nice if angular team provide some test utils for hydration. We have lots of experience in Angular usage so we can build
ssr-dev-server
and can start hydration feature. But it was like end-to-end hydration testing process by human. We want to be more smarter and save times to testing hydration in the future. If the hydration testing util provided, there is no need to implementssr-dev-server
. I saw your PR and its hydration test util is awesome. we are trying to build similiar hydration test code in our project. we can open our hydration testing utils for public lib (if we succeed to make it) It can make our project sustainable and trustful. And i expect that easy to find unhandled case from angular internal logic.@leo6104 thanks for the reply.
There is a chance that this issue was caused by a special logic to handle empty text nodes during hydration. I’ve put together some tests and can reproduce a similar (but not exactly the same) problem, see https://github.com/angular/angular/pull/49877. I’ve also put together a possible fix and I wanted to ask if you could use the following
@angular/core
package to test the behavior:Note: the PR does not include the other fix discussed above, please let me know if this is a problem for testing, I can create a temporary PR with a combination of both fixes.
It’d be great if you could check the following use-cases and let us know the observed behavior:
<div>{{test}}</div>
?It’d still be very helpful to have a repro, so that we can perform further investigation.
Thank you.
@leo6104 thanks a lot for the reproduction! This helped to identify the root cause: the problem was triggered by an issue in the internal Angular logic that collects root nodes in a given view. The logic was not taking into account ViewContainerRef’s anchor (comment) nodes, thus returning incorrect output (less DOM nodes). Hydration logic relies on the number of root nodes in a view to properly split the DOM into segments (which belong to a particular view), thus the logic incorrectly identified the end of a segment.
The problem should be fixed by https://github.com/angular/angular/pull/49867. The PR would go through the usual review and test process before the merge, but you can test the fix by using the
core
package from the PR. You can update thepackage.json
as shown below and runnpm install
(you may need to use--force
to skip version conflicts):Please let me know if this resolves the problem.
Thank you.