-1

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.

Kicsi
  • 1,173
  • 10
  • 22

2 Answers2

0

During apply request values phase, JSF needs to iterate over the model in order to find the clicked command link. If the model changes incompatibly during the HTTP request wherein the form submit is processed (the postback) as compared to the initial HTTP request wherein the table with the command links is shown, then JSF may not be able to find the clicked command link and thus never queue the desired action, or the object representing the "current row" is not the same as the enduser intented.

If your bean is request scoped, then it should be written in such way that it initializes selectedTest in the constructor or @PostConstruct method based on some request parameter. At least, you should absolutely not perform business logic in getters.

You can pass the parameters necessary for reconstructing the selectedTest as <f:param> in the command link.

<h:commandLink ...>
    <f:param name="some" value="#{bean.some}" />
</h:commandLink>

And prepare the model as follows:

@ManagedProperty
private String some;

@PostConstruct
public void init(){ 
    selectedTest = populateItBasedOn(some);
}

// Don't change standard getters/setters!

See also:

Community
  • 1
  • 1
BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555
  • Right now, the post-postback model is compatible with the pre-postback model only after the update model phase. "During apply request values phase, JSF needs to..." - what i dont understand is why so early, when its only used during invoke application phase. The above methods are in a POJO, which itself is a property of my bean, and i this POJO as data holder for my cc. I would like to keep it that way, so i can reuse it more freely (easier to have more than one). The getSelectedTest method is just for testing, i usually use getters and cache the property as a way of lazy loading. ... – Kicsi Jan 20 '13 at 02:00
  • The apply request values phase basically parses the HTTP request parameter map and sets the submitted values for the input (`EditableValueHolder`) components and queues the action events for the command (`ActionSource`) components. That's just how JSF is specified to work. – BalusC Jan 20 '13 at 02:04
  • Is there a way to use something like the f:param or a list of checkboxes independently of the commandLinks (i dont want to put n params to each of the n commandLinks, where n is size of myList), and executed early enough? (i mean during apply request phase). So what i want is basically either fill up my list faster, or look up the commandLink later. – Kicsi Jan 20 '13 at 02:06
  • Oh! I didn't understood the functional requirement clearly until I saw your answer. Yes, that's a way :) – BalusC Jan 20 '13 at 11:58
0

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>
Kicsi
  • 1,173
  • 10
  • 22