Skip to content

[4.1.x] Save valid and localValueSet of UIInput with its local value again - #1130

Merged
tandraschko merged 1 commit into
apache:4.1.xfrom
BalusC:uiinput-save-valid-localvalueset
Nov 4, 2026
Merged

tandraschko merged 1 commit into
apache:4.1.xfrom
BalusC:uiinput-save-valid-localvalueset

Conversation

@BalusC

@BalusC BalusC commented Nov 4, 2026 •

Copy link
Copy Markdown
Contributor

While comparing the view state of Mojarra and MyFaces, the saved state of an input turned out to hold its local value, but no longer the valid and localValueSet flags which belong to it. This makes an input render a stale value forever.

This PR targets 4.1.x. The same change is needed on main, which has the same commit (3ca891b). Please upmerge or cherry-pick it into main yourselves.

Problem

Since 4.1.4, an input can keep rendering a stale local value forever, while a readonly input bound to the same property follows the model. Reproducer:

<h:form id="form">
    <h:selectOneMenu id="rendered" value="#{bean.rendered}">
        <f:selectItem itemValue="#{false}" itemLabel="Hide" />
        <f:selectItem itemValue="#{true}" itemLabel="Show" />
        <f:ajax execute="@form" render="@form" />
    </h:selectOneMenu>

    <h:panelGroup id="group" rendered="#{bean.rendered}">
        <h:commandButton id="increment" value="Increment" action="#{bean.increment}">
            <f:ajax execute="@this" render="@form" />
        </h:commandButton>
        <h:inputText id="editable" value="#{bean.value}" />
        <h:inputText id="readonly" value="#{bean.value}" readonly="true" />
    </h:panelGroup>
</h:form>
@Named @ViewScoped
public class Bean implements Serializable {
    private boolean rendered;
    private int value;

    public void increment() {
        value++;
    }

    // Getters and setters.
}

Select "Show", click "Increment" twice, select "Hide" and then "Show" again, and click "Increment" twice. The value of each input after each step, editable first:

Step MyFaces 4.1.4 MyFaces 4.1.3
Show 0, 0 0, 0
Increment 1, 1 1, 1
Increment 2, 2 2, 2
Hide, then Show 2, 2 2, 2
Increment 2, 3 3, 3
Increment 2, 4 4, 4

MyFaces 4.1.4 gives the same result with server and with client side state saving.

Cause

The "Hide" request executes @form while the group is still rendered, so the editable input gets a local value. During Update Model Values the menu sets rendered to false before the walk reaches the group, so the input never reaches updateModel(), which would clear the local value. The local value is saved in the StateHelper, but since 3ca891b on main and 4e2bf73 on 4.1.x ("[perf] use and optimize TransientStateHelper, …") valid and localValueSet are held in the TransientStateHelper, and that is not saved during Render Response. On every later request, updateModel() stops at !isLocalValueSet(), never clears the local value, and getValue() keeps returning it before it evaluates the value expression.

Change

isValid(), setValid(), isLocalValueSet() and setLocalValueSet() use the StateHelper again, with the same bodies as in 4.1.3, and localValueSet and valid are back in INITIAL_STATE_PROPERTIES. A default value is still not written when the component has no entry for it yet, so an input at its defaults adds nothing to the saved state.

submittedValue stays in the TransientStateHelper. It is per-request state. 4.1.3 did save it, so this is the one difference to 4.1.3 which remains.

Two comments in EditableValueHolderState described valid and localValueSet as transient state, and are updated accordingly.

This gives back part of the speed gain of the perf commit: setting valid or localValueSet is a StateHelper put again, also per row of a UIData or UIRepeat. The saved state does not grow from it, because a value which is back at its default is removed from the delta before saving.

The new UIInputPSSTest saves and restores the state during Render Response, as for the view state, because outside that phase UIComponentBase.saveState() also saves the transient state. Without the change in UIInput, two of its tests fail. Without the re-added INITIAL_STATE_PROPERTIES entries, two of its tests fail as well. The full api and impl builds pass.

Mojarra made the same change in 4.0.19 and 4.1.10, got the same report in eclipse-ee4j/mojarra#6019, and fixed it the same way in eclipse-ee4j/mojarra#6021.

🤖 Generated with Claude Opus 5.5

Since 4e2bf73, valid and localValueSet were held in the
TransientStateHelper, while the local value they qualify stayed in the
saved StateHelper. An input which got a local value but never reached
updateModel, because its parent turned rendered=false during the same
Update Model Values walk, then kept that local value with the flag
dropped. Every later updateModel bailed on !isLocalValueSet(), and the
input rendered the stale local value forever.

Save both flags in the StateHelper again, as in 4.1.3, and restore
their INITIAL_STATE_PROPERTIES entries so that a flag back at its
default leaves the delta. submittedValue stays transient.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@tandraschko
tandraschko merged commit 761b06a into apache:4.1.x Nov 4, 2026
1 check passed
asf-gitbox-commits pushed a commit that referenced this pull request Nov 4, 2026
…1130)

Since 4e2bf73, valid and localValueSet were held in the
TransientStateHelper, while the local value they qualify stayed in the
saved StateHelper. An input which got a local value but never reached
updateModel, because its parent turned rendered=false during the same
Update Model Values walk, then kept that local value with the flag
dropped. Every later updateModel bailed on !isLocalValueSet(), and the
input rendered the stale local value forever.

Save both flags in the StateHelper again, as in 4.1.3, and restore
their INITIAL_STATE_PROPERTIES entries so that a flag back at its
default leaves the delta. submittedValue stays transient.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
@BalusC
BalusC deleted the uiinput-save-valid-localvalueset branch November 5, 2026 10:38
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.

2 participants