fix(framework): align template cursor in SSR hydration resolvers for nested blocks#196
Merged
toddreifsteck merged 1 commit intomainfrom Apr 8, 2026
Merged
fix(framework): align template cursor in SSR hydration resolvers for nested blocks#196toddreifsteck merged 1 commit intomainfrom
toddreifsteck merged 1 commit intomainfrom
Conversation
…nested blocks When pathStart > 0 (used during in-place hydration of repeat and conditional block items), $resolveSSR and $resolve skipped the leading path segments but never advanced the template DOM cursor through them. This left the template cursor pointing at the wrapper div from getTemplateDom() while the SSR cursor had already descended to the block root element. The misalignment caused path resolution to fail for any binding nested deeper than one level inside a block: - Issue #175 (variant selector): The inner for-loop over group.values inside the outer for-loop over optionGroups could not resolve its parent dd element. The inner repeat was never hydrated, event handlers were never wired on the pill buttons, and clicking a variant after a direct page load (SSR) did nothing. - Issue #176 (cart duplicates): The for-loop over cartItems inside the if-condition block could not resolve its parent ul element. An empty repeat was created with a new anchor in the wrong container. When updateInstance ran syncRepeat, it created new DOM items alongside the SSR-rendered originals, doubling every cart line. The fix adds a pre-advance loop in both resolveSSR (template-only) and resolve (template path resolution) to walk the cursor through path[0..pathStart-1] before the main resolution loop. This is a no-op for the common pathStart=0 case and adds exactly one extra childNodes lookup for the pathStart=1 case used by block hydration. Zero new allocations. Regression tests added at two levels: - Framework (webui-framework): 10 new SSR hydration tests for the nested-repeat fixture (light + shadow DOM), covering: no duplication after hydration, attribute preservation, reactive updates, list growth, and outer-scope text bindings. - Commerce app (E2E): 3 new Playwright tests -- variant selector click activation after page refresh (#175), and cart line count after SSR reload (#176). Closes #175, closes #176 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
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 pathStart > 0 (used during in-place hydration of repeat and conditional block items), $resolveSSR and $resolve skipped the leading path segments but never advanced the template DOM cursor through them. This left the template cursor pointing at the wrapper div from getTemplateDom() while the SSR cursor had already descended to the block root element.
The misalignment caused path resolution to fail for any binding nested deeper than one level inside a block:
Issue commerce example bug: refreshing product page, variant selectors don't work #175 (variant selector): The inner for-loop over group.values inside the outer for-loop over optionGroups could not resolve its parent dd element. The inner repeat was never hydrated, event handlers were never wired on the pill buttons, and clicking a variant after a direct page load (SSR) did nothing.
Issue commerce example bug: add to card is adding duplicates with
<for>#176 (cart duplicates): The for-loop over cartItems inside the if-condition block could not resolve its parent ul element. An empty repeat was created with a new anchor in the wrong container. When updateInstance ran syncRepeat, it created new DOM items alongside the SSR-rendered originals, doubling every cart line.The fix adds a pre-advance loop in both resolveSSR (template-only) and resolve (template path resolution) to walk the cursor through path[0..pathStart-1] before the main resolution loop. This is a no-op for the common pathStart=0 case and adds exactly one extra childNodes lookup for the pathStart=1 case used by block hydration. Zero new allocations.
Regression tests added at two levels:
Framework (webui-framework): 10 new SSR hydration tests for the nested-repeat fixture (light + shadow DOM), covering: no duplication after hydration, attribute preservation, reactive updates, list growth, and outer-scope text bindings.
Commerce app (E2E): 3 new Playwright tests -- variant selector click activation after page refresh (commerce example bug: refreshing product page, variant selectors don't work #175), and cart line count after SSR reload (commerce example bug: add to card is adding duplicates with
<for>#176).Closes #175, closes #176