fix(core): recover embedded views whose first creation pass was incomplete#68172
Draft
arturovt wants to merge 1 commit intoangular:mainfrom
Draft
fix(core): recover embedded views whose first creation pass was incomplete#68172arturovt wants to merge 1 commit intoangular:mainfrom
arturovt wants to merge 1 commit intoangular:mainfrom
Conversation
…plete When a directive throws during an embedded view's first creation pass, Angular marks the embedded TView as `incompleteFirstPass` and sets `firstCreatePass` to `false`. Any TNodes that hadn't been reached yet (because the template function exited early) remain null in the TView's data array. On re-render (e.g. toggling an `@if` off then back on), the declaration functions — `declareDirectiveHostTemplate` and `declareNoDirectiveHostTemplate` — previously guarded TNode creation behind `firstCreatePass` only. With `firstCreatePass=false` and `data[index]=null`, they fell through to the else branch and passed `null` to `templateCreate`, which then crashed at `tNode.flags |= flags`: ``` TypeError: Cannot read properties of null (reading 'flags') at templateCreate (chunk-S65TXBMT.js:3:72993) ... ``` This was observed in production via Rollbar and confirmed reproducible only in hydration mode (hydration enables the `locateOrCreateContainerAnchor` path that processes all template containers on creation, making the null slot reachable). Fix: extend the guards in all three functions to also trigger when the relevant slot is uninitialized: - `declareDirectiveHostTemplate` / `declareNoDirectiveHostTemplate`: add `|| data[adjustedIndex] === null` so the TNode is (re-)created when missing. - `templateCreate`: add `|| tNode.tView === null` so the embedded TView is created for a freshly constructed TNode even when firstCreatePass is already false. This mirrors the existing recovery logic in `getOrCreateComponentTView`, which already checks `incompleteFirstPass` before reusing a component TView.
Contributor
|
Woah, looks like you've opened a lot of issues/PRs recently. While we appreciate contributions from the community, triaging and reviewing a large influx of content in a short time period takes time away from other ongoing projects. As a result, we're closing these issues/PRs to maintain the team's focus. Note that this is not necessarily a rejection of the goals or direction of any of these contributions in particular, so much as a reflection of the team's current capacity and priorities. You are welcome to open a smaller subset of issues/PRs in accordance with our policy focused on the most important and impactful contributions and we will do our best to prioritize a response as soon as possible. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
When a directive throws during an embedded view's first creation pass, Angular marks the embedded TView as
incompleteFirstPassand setsfirstCreatePasstofalse. Any TNodes that hadn't been reached yet (because the template function exited early) remain null in the TView's data array.On re-render (e.g. toggling an
@ifoff then back on), the declaration functions —declareDirectiveHostTemplateanddeclareNoDirectiveHostTemplate— previously guarded TNode creation behindfirstCreatePassonly. WithfirstCreatePass=falseanddata[index]=null, they fell through to the else branch and passednulltotemplateCreate, which then crashed attNode.flags |= flags:This was observed in production via Rollbar and confirmed reproducible only in hydration mode (hydration enables the
locateOrCreateContainerAnchorpath that processes all template containers on creation, making the null slot reachable).Fix: extend the guards in all three functions to also trigger when the relevant slot is uninitialized:
declareDirectiveHostTemplate/declareNoDirectiveHostTemplate: add|| data[adjustedIndex] === nullso the TNode is (re-)created when missing.templateCreate: add|| tNode.tView === nullso the embedded TView is created for a freshly constructed TNode even when firstCreatePass is already false.This mirrors the existing recovery logic in
getOrCreateComponentTView, which already checksincompleteFirstPassbefore reusing a component TView.