Skip to content

Fix #5885: index tag ids through implicit facet panels on refresh - #5886

Merged
BalusC merged 1 commit into
4.0from
issue_5885
Jul 28, 2026
Merged

Fix #5885: index tag ids through implicit facet panels on refresh#5886
BalusC merged 1 commit into
4.0from
issue_5885

Conversation

@BalusC

@BalusC BalusC commented Jul 28, 2026

Copy link
Copy Markdown
Contributor

What

Closes #5885: a UIInput inside an <f:facet> with more than one child lost its submitted value and its invalid state during Render Response, so a failed ajax validation re-rendered the model value next to the error message.

ComponentSupport.buildTagIdIndex descended into an IMPLICIT_PANEL only for panels sitting among the parent's children, not among its facets — while a facet is the only place Facelets ever creates one (addComponent wraps a facet that receives a second child). The scan it replaced, findChildByTagIdFullStateSaving, lumps facets and children into one list and therefore covers both. So on a refresh-time re-apply every component inside a multi-child facet missed the index, was treated as new, and was recreated; markForDeletion/finalizeForDeletion then dropped the original along with its per-request state. The FacesMessage survived because it lives in FacesContext keyed by client id, hence an error message next to a field that looks valid.

The two conditions in the report follow from this: a single-child facet is the facet, so the facetName fast path finds it; and the render-time re-apply only runs at all when the view carries a dynamic action (canSkipTransientBuildRefresh), which is why a programmatically added component resource was needed to expose it.

Both loops now index through implicit panels, and the index generation additionally counts the children of the panels it indexed through, so a body that adds a component under an otherwise unchanged parent still invalidates the entry.

Regression from #5835 (4.0.20 / 4.1.11 / 5.0). The report bisected to 4.1.10, which contains both #5835 and the render-refresh skip (#5815) that gates when the defect is observable.

Not limited to UIInput: any per-request component state inside a multi-child facet was lost on that re-apply.

Testing

New TCK IT in jakartaee/faces#2218 (faces50/facelets, Issue5885IT), run against this branch's master equivalent:

testSingleChildFacet testMultiChildFacet
before pass failexpected: <> but was: <MODEL VALUE>
after pass pass

The implicit panel is specified behaviour: the f:facet VDLDoc has said since 2.3 that "When the facet contains more than one child the children will be automatically put in a container UIPanel". The older prose in Standard Tag Libraries, "Using Facets" — "one and only one child UIComponent tag", with footnote 10 pointing at <h:panelGroup> — predates that and was never reconciled with it. Wrapping the children in an <h:panelGroup> is nevertheless a working workaround on an affected release, since the facet then has a single child.

Full Jakarta Faces TCK passes. 4.1 and master upmerge to follow.

🤖 Generated with Claude Code (Opus 5)

buildTagIdIndex descended into an IMPLICIT_PANEL only for panels among
the children, not among the facets, while a facet is the only place
Facelets ever creates one: addComponent wraps a facet that receives a
second child. So on a refresh-time re-apply every component inside a
multi-child facet missed the index and was recreated, dropping its
per-request state — an input lost its submitted value and its invalid
state and re-rendered the model value next to its validation message.

The index generation now also counts the children of the implicit panels
it indexed through, so a body that adds one under an otherwise unchanged
parent still invalidates the entry.

TCK coverage in jakartaee/faces#2218.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant