Field ID conflict inside a JSF form with dataGrid

2019-07-21 08:05发布

I'm implementing a typical shopping cart, based on PrimeFaces' dataGrid component. Below is some sample code.

Everything is ok, but the inputText field causes the bean property to be set as many times as there are elements in the page. So, if I put 1 in the first item in the datagrid, it is set, but it's set to 0 N times after this, thus overwriting it.

I guess this is because each inputText has the same id. I have tried to enclose the input + commandLink within their own form, but that doesn't work. I am sure this is a common case, and there must be an elegant solution.

<h:form id="itemsForm" prependId="false">
    <p:dataGrid id="itemList" value="#{itemsBean.items}" var="item" type="unordered" paginator="true" rows="12" columns="1">                    
        <p:column>
            <p:inputText value="#{cartBean.quantity}"/>
            <p:commandLink actionListener="#{cartBean.add}" update=":cartPanel" value="Add to cart">
                <f:setPropertyActionListener target="#{cartBean.itemToCart}" value="#{item}"/>
            </p:commandLink>
        </p:column>             
    </p:dataGrid>
</h:form>

1条回答
老娘就宠你
2楼-- · 2019-07-21 08:32

You can use AJAX here to do a partial submit* of your form data. I'll give an example here using JSF 2.0's build-in AJAX support and with the standard dataTable, but the idea is the same for JSF 1.x (using e.g. a4j) and dataGrid:

Facelet:

<html xmlns="http://www.w3.org/1999/xhtml"
    xmlns:h="http://java.sun.com/jsf/html"
    xmlns:f="http://java.sun.com/jsf/core"   
>   
    <h:head/>

    <h:body>

        <h:form id="form">

            <h:dataTable id="table" value="#{singleListSelectBean.items}" var="item">
                <h:column>                    
                    #{item}            
                    <h:inputText id="input" value="#{singleListSelectBean.value}" />
                    <f:ajax execute="input">
                        <h:commandButton value="Select" action="#{singleListSelectBean.select(item)}"  />
                    </f:ajax>
                </h:column>
            </h:dataTable>

        </h:form>

    </h:body>
</html>

Bean:

@ManagedBean
@ViewScoped
public class SingleListSelectBean {

    private List<String> items = Arrays.asList("a", "b", "c");
    private String value;

    public void select(String item) {
        System.out.format("Selecting item %s with value %s.", item, value);
    }

    public String getValue() {
        return value;
    }

    public void setValue(String value) {
        this.value = value;
    }

    public List<String> getItems() {
        return items;
    }

}

What happens here is that the <f:ajax> tag applies Ajax capabilities to the command button. Via the execute attribute you tell it that only the input of the component with ID input should be processed.

In the context of rendering each command button, ID input resolves to a different actual client ID for each row. E.g. form:table:0:input for the first row, form:table:1:input for the second row and so on. Via this, the partial submit is being done and your backing bean's single value binding will receive the value for the row on which the user clicked.

Note that in the example given, the table is not re-rendered and the value will 'stick' in each input text component. This might give the illusion your backing bean is bound to multiple values, but this is not the case.

(*) Although conceptually we're talking about a partial submit, a specific implementation of JSF (e.g. Mojarra) can still opt to post the whole form and filter it server-side.

查看更多
登录 后发表回答