JSF& ui:repeat - 将对象添加到购物车的问题

时间:2014-08-07 10:52:38

标签: jsf javabeans facelets commandbutton uirepeat

我再次使用Java Server Faces编写的pizza-search-programm需要一些帮助。

该计划: 用户可以通过输入表单来搜索比萨饼。过滤搜索是可能的,因为用户可以决定他是否搜索比萨饼名称,比萨饼ID或其他指定标准。该程序将生成一个SQL查询,该查询返回pizza对象并将其存储到对象列表中。 JSF页面通过ui:repeat标记迭代它们来显示披萨对象列表。将显示比萨饼名称,比萨饼ID,可用尺寸(显示为单选按钮)和可能数量列表。对于每个显示的披萨对象,都有一个“添加到购物车按钮”,用于在所选尺寸和数量的参数化值下将披萨添加到购物车。

问题: 几乎所有东西都正确显示。但是当在购物车上添加比萨饼时,会发生错误。如果用户搜索特定比萨饼,则通过将比萨饼ID,所选尺寸和所选数量提交给添加到购物车方法将没有问题。但是,当列表包含多于一个比萨饼对象时,通过提交比萨饼ID,所选尺寸和所选数量的正确值,可以仅将最后一个比萨饼正确地添加到购物车中。如果用户试图将其中一个上层比萨饼放入购物车,那么之前提交的尺寸和数量将被采用,前提是之前已经成功执行了“添加购物车动作”。如果不是用户选择的大小和数量,则不会提交0。

示例: 用户搜索“pizza salami”。他将其中的两个40英寸加到他的购物车上。 (selectedPizzaID:1; selectedSize:40; selectedQuantity 2)。一切都正确执行。但在此之后,用户搜索所有比萨饼。他想添加显示列表中的第一个披萨。这种比萨饼的尺寸仅为30英寸。他选择了30英寸的比萨饼中的3个,并点击“添加到购物车按钮”。该程序采用selectedSize和chosenQuantity的先前参数(selectedSize:40; selectedQuantity:2)。

PizzaSearch的代码段:

@ManagedBean
@SessionScoped
public class PizzaSearch {

   // variables in order to submit the search criteria
   private List<PizzaObject> results = new ArrayList<PizzaObject>();

   // methods to generate the search
   // each search result will fill/replace the list of pizza objects 'results'

   // getter and setter methods

}

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.results = pizzaSearch.getResults();
   }

   // method to add the pizza object to the cart
   // a simple text output for testings
   public void addToCart(int chosenPizzaID) {
      System.out.println("chosen pizza ID: " + chosenPizzaID);
      System.out.println("chosen size:     " + _chosenSize);
      System.out.println("chosen quantity: " + _chosenQuantity);
   }

   // getter and setter methods
}

JSF输出页面的代码片段

<ui:repeat var="result" value="#{pizzaResult.results}">
   <h:form>
      <ul>
         <li><p>Name: #{result.pizza.name}</p></li>
         <li><p>ID: #{result.pizza.pizzaID}</p></li>
         <li>
            <p>Toppings:</p>
            <ui:repeat var="topping" value="#{result.toppingList}">
               <p>#{topping.toppingName}</p>
            </ui:repeat>
         </li>
         <li>
            <p>Sizes:</p>
            <h:selectOneRadio id="chosenSize" value="#{pizzaResult.chosenSize}">
               <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}" 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(result.pizza.pizzaID)}"/>
         </li>
      </ul>
   </h:form>
</ui:repeat>

我觉得问题会被变量selectedSize和chosenQuantity调用。但我不知道如何解决这个问题。我希望你能以某种方式帮助我。谢谢!

2 个答案:

答案 0 :(得分:0)

步骤:

第1步:

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

第2步:

Give some id to the form (In JSF)

第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"/>

第4步:

In addToCart() method, reset chosenPizzaID, _chosenSize and _chosenQuantity to 0
i.e 
chosenPizzaID=0
_chosenSize=0
_chosenQuantity=0

答案 1 :(得分:0)

我假设您使用Mojarra - JSF的参考实现。

由于this answer中描述的Mojarra中的错误,原始代码无效。简而言之,ui:repeat不会保持其行的状态。

为了使其正常运行,您必须:

  1. 切换到另一个实现,例如Apache MyFaces,此代码可以正常工作(请参阅my implementation
  2. 或在ui:repeat
  3. 之外移动表单
  4. 正如that answer中所述:
      

    使用其他迭代组件(例如<c:forEach><h:dataTable><t:dataList><p:dataList>等)

  5. 但是,只需在ui:repeat之外移动表单,如@ user2314868建议不起作用。这是因为所有字段都是从表单中发布的。因此,每个h:selectOneRadio在更新模型值阶段更新#{pizzaResult.chosenSize}。因此,只有最后一次更新才会在Invoke Application阶段中显示。同样适用于#{pizzaResult.chosenQuantity}

    为了使其正常工作,我建议将chooseSize这样的单个值替换为值数组。我们可以利用状态变量index的{​​{1}}属性。

    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> 的变化:

    PizzaResult

    可以找到完整的工作示例here