Ajax call to toggle a composite component boolean

2019-08-12 12:10发布

问题:

I have a pretty simple composite component which toggles back and forth between two images when it is clicked. When I click it the first time, it works fine. Every time after that, nothing happens. The "toggleSingleSelect" is called, but the panel itself is not re-rendered.

singleMultiSelectText.xhtml:

<cc:interface componentType="singleMultiSelect">

</cc:interface>

<cc:implementation>

    <h:panelGroup id="singleMultiSelectPanel" styleClass="field" layout="block">

        <h:commandButton styleClass="toggle" 
                         image="#{cc.singleSelect ? 
                                  '/resources/img/expand-single.gif' : 
                                  '/resources/img/collapse-single.gif'}">
            <f:ajax listener="#{cc.toggleSingleSelect}"
                    execute="@this" render="singleMultiSelectPanel"/>
        </h:commandButton>

    </h:panelGroup>

</cc:implementation>

singleMultiSelect.java:

@FacesComponent(value = "singleMultiSelect")
public class SingleMultiSelect extends UINamingContainer {

private boolean singleSelect = true;

public void toggleSingleSelect(AjaxBehaviorEvent event) {
    singleSelect = ! singleSelect;
}    

}

The console has the following output when I click it the first time:

update["assetSearchForm:j_idt112:_t113"]: <input class="toggle" id="assetSearchFor....
update["javax.faces.ViewState"]: -4115183671224810146:2892643767875659913....

Every time after that, though, I just get:

update["javax.faces.ViewState"]: -4115183671224810146:2892643767875659913....

回答1:

JSF components are created during view build time of every request. In other words, JSF component instances are basically request scoped.

Your singleSelect property needs to be retained across postbacks on the same view. In other words, you want it to be view scoped.

However, you assigned it as an instance variable of the component instead of explicitly saving it in the JSF view state. So, during every request, it would reinitialize back to its default value of true, and effectively become false every time you press the button.

You need to explicitly save view scoped state in the JSF view state. You can use the inherited getStateHelper() method for this.

public void toggleSingleSelect(AjaxBehaviorEvent event) {
    setSingleSelect(!isSingleSelect());
}

public void setSingleSelect(boolean singleSelect) {
    getStateHelper().put("singleSelect", singleSelect);
}

public boolean isSingleSelect() {
    return (boolean) getStateHelper().eval("singleSelect", true);
}

See also:

  • How to save state when extending UIComponentBase