JSF & ui:repeat - issue with adding an object to cart

限于喜欢 提交于 2019-12-06 21:01:28
Dawid Pytel

I assume that you use Mojarra - reference implementation of JSF.

Original code does not work because of bug in Mojarra described in this answer. In short ui:repeat does not maintain state of its rows.

In order to get it working you have to either:

  1. switch to another implementation like Apache MyFaces where this code works (see my implementation)
  2. or move form outside the ui:repeat
  3. as suggested in that answer:

    use another iterating component (e.g. <c:forEach>, <h:dataTable>, <t:dataList>, <p:dataList>, etc)

However, simply moving form outside the ui:repeat like @user2314868 suggested does not work. It is because all fields are posted from the form. As a result each h:selectOneRadio updates #{pizzaResult.chosenSize} during Update Model Values phase. Therefore only last update will be visible in Invoke Application phase. Similarly for #{pizzaResult.chosenQuantity}.

In order to get it working I propose to replace single value like chosenSize with array of values. Than we can take advantage index property of status variable of ui:repeat.

<h:form id="pizzasForm">
    <ui:repeat var="result" value="#{pizzaResult.results}" varStatus="loop">

        <ul>
            <li><p>Name: #{result.pizza.name}</p></li>
            <li><p>ID: #{result.pizza.pizzaID}</p></li>
            <li>
                <p>Sizes:</p> <h:selectOneRadio id="chosenSize"
                    value="#{pizzaResult.chosenSize[loop.index]}">
                    <f:selectItems value="#{result.sizeList}" var="size"
                        itemLabel="#{size.diameter}" itemValue="#{size.sizeID}" />
                </h:selectOneRadio>
            </li>
            <li>
                <p>Quantity:</p> <h:selectOneListbox id="chosenQuantity"
                    value="#{pizzaResult.chosenQuantity[loop.index]}" size="1">
                    <f:selectItem id="quantity1" itemLabel="1x" itemValue="1" />
                    <f:selectItem id="quantity2" itemLabel="2x" itemValue="2" />
                </h:selectOneListbox>
            </li>
            <li><h:commandButton value="add to cart"
                    action="#{pizzaResult.addToCart(loop.index)}"/></li>
        </ul>

    </ui:repeat>
</h:form>

Changes in PizzaResult:

@ManagedBean
@SessionScoped
public class PizzaResult {

    // injection of PizzaSearch
    @ManagedProperty(value = "#{pizzaSearch}")
    private PizzaSearch pizzaSearch;

    // variables
    private List<PizzaObject> results;
    private int[] _chosenSize;
    private int[] _chosenQuantity;

    @PostConstruct
    public void initResults() {
        this.setResults(getPizzaSearch().getResults());
        int size = this.getResults().size();
        this._chosenSize = new int[size];
        this._chosenQuantity = new int[size];
    }

    // method to add the pizza object to the cart
    // a simple text output for testings
    public void addToCart(int index) {
        System.out.println("chosen pizza ID: " + results.get(index).getPizza().getPizzaID());
        System.out.println("chosen size:     " + getChosenSize()[index]);
        System.out.println("chosen quantity: " + getChosenQuantity()[index]);
    }
...

Full working example can be found here

Step to do:

Step 1:

Move the h:form outside the ui:repeat (In JSF)

Step 2:

Give some id to the form (In JSF)

Step 3:

update the form once add method exucuted
<h:commandButton value="add to cart" action="#{pizzaResult.addToCart(result.pizza.pizzaID)}" update="give form id name here"/>

Step 4:

In addToCart() method, reset chosenPizzaID, _chosenSize and _chosenQuantity to 0
i.e 
chosenPizzaID=0
_chosenSize=0
_chosenQuantity=0
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!