Search code examples
jsfdatatablelifecycle

jsf datatable why commandLink clientid resolved before update model values phase


I have a datatable that iterates over a list, lets call it myList. I populate this myList based on some request parameters. Inside the datatable there are commandLinks. If i put a dummy entry into myList during apply request values phase, i can click on the first commandLink, and it works as it should (it is executed during invoke application phase, and by then the correct entries are in myList). If i dont do it, or i click on the second or later commandLink, nothing happens. So im guessing the clientId of the command button is resolved during apply request phase, even thought it is only used during the invoke application phase, which results in the broken commandLinks.

something like this:

<h:selectManyCheckbox styleClass="hidden" 
                      value="#{cc.attrs.selectionList.selected}"
                      converter="#{cc.attrs.converter}" >
    <f:selectItems value="#{cc.attrs.selectionList.all}"
                   var="item" itemValue="#{item}" itemLabel="" />
</h:selectManyCheckbox>
<h:dataTable value="#{cc.attrs.selectionList.selectedTest}" var="item">
    <h:column>
        <h:commandLink value="deselect" action="#{cc.attrs.selectionList.deSelect(item)}">
            <f:ajax execute=":#{component.parent.parent.parent.clientId}"
                    render=":#{component.parent.parent.parent.clientId}" />
        </h:commandLink>
    </h:column>
</h:dataTable>

and the model:

public List<E> getSelected()
{
    return myList;
}

public List<E> getSelectedTest()
{
    if(FacesContext.getCurrentInstance().getCurrentPhaseId().equals(PhaseId.RESTORE_VIEW) && getSelectedList().isEmpty())
    {
        return Collections.singletonList(myList.get(0));
    }
    else if(FacesContext.getCurrentInstance().getCurrentPhaseId().equals(PhaseId.APPLY_REQUEST_VALUES) && getSelectedList().isEmpty())
    {
        return Collections.nCopies(2, myList.get(0));
    }
    else if(FacesContext.getCurrentInstance().getCurrentPhaseId().equals(PhaseId.PROCESS_VALIDATIONS) && getSelectedList().isEmpty())
    {
        return Collections.nCopies(3, myList.get(0));
    }
    else if(FacesContext.getCurrentInstance().getCurrentPhaseId().equals(PhaseId.UPDATE_MODEL_VALUES) && getSelectedList().isEmpty())
    {
        return Collections.nCopies(4, myList.get(0));
    }

    return myList;
}

public void deSelect(E item)
{
    myList.remove(item);
}

with this example, the top two commandLinks of the datatable works. My question is why is this behaviour, and is there any way around without filling myList with dummy entries? I do not want to use any (viewscoped) backing bean to store the data.


Solution

  • I managed to get my way around by binding the selectManyCheckbox itself to my componentBindings HashMap, and using that for the dataTable (with immediate="true" on the selectManyCheckbox):

    <h:selectManyCheckbox immediate="true" styleClass="hidden" 
                          binding="#{componentBindings[cc.attrs.selectionList]}"
                          value="#{cc.attrs.selectionList.selected}"
                          converter="#{cc.attrs.converter}" >
        <f:selectItems value="#{cc.attrs.selectionList.all}" var="item"
                       itemValue="#{item}" itemLabel="" />
    </h:selectManyCheckbox>
    
    <h:dataTable value="#{componentBindings[cc.attrs.selectionList].value}" var="item">
        <h:column>
            <h:commandLink value="deselect" action="#{cc.attrs.selectionList.deSelect(item)}">
                <f:ajax execute=":#{component.parent.parent.parent.clientId}"
                        render=":#{component.parent.parent.parent.clientId}" />
            </h:commandLink>
        </h:column>
    </h:dataTable>
    

    in faces-config.xml:

    <managed-bean>
        <description>Holder of all component bindings.</description>
        <managed-bean-name>componentBindings</managed-bean-name>
        <managed-bean-class>java.util.HashMap</managed-bean-class>
        <managed-bean-scope>request</managed-bean-scope>
    </managed-bean>