【问题标题】:JSF & ui:repeat - issue with adding an object to cartJSF & ui:repeat - 将对象添加到购物车的问题
【发布时间】:2014-09-30 13:59:35
【问题描述】:

我再次需要一些关于我用 Java Server Faces 编写的披萨搜索程序的帮助。

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

问题: 几乎所有内容都正确显示。但是在将披萨添加到购物车时,就会出现错误。如果用户搜索特定的比萨饼,通过将比萨饼 ID、选择的尺寸和选择的数量提交到添加到购物车方法中不会有任何问题。但是当列表包含多个披萨对象时,只有最后一个披萨可以通过提交正确的披萨 ID、选择的大小和选择的数量值正确地添加到购物车中。如果用户尝试将上面的一个披萨放入他的购物车,则将采用之前提交的尺寸和数量,前提是之前已经成功执行了“添加到购物车操作”。如果不是 0,则无论用户选择什么尺寸和数量,都会提交。

示例: 用户搜索“pizza salami”。他在购物车中添加了 2 个 40 英寸的。 (chosenPizzaID: 1; selectedSize: 40; selectedQuantity 2)。一切都正确执行。但在那之后,用户搜索所有的比萨饼。他想添加显示列表中的第一个披萨。这个披萨只有 30" 的尺寸。他选择了 3 个尺寸为 30" 的披萨,然后单击“添加到购物车按钮”。该程序采用之前的参数 selectedSize 和 selectedQuantity (chosenSize: 40; selectedQuantity: 2)。

PizzaSearch的代码sn-p:

@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的代码sn-p:

@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输出页面的代码sn-p

<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 和 selectedQuantity 调用。但我不知道如何解决这个问题。我希望你能以某种方式帮助我。谢谢!

【问题讨论】:

  • 您的 PizzaResult bean 是会话范围还是视图范围?
  • 我尝试了 ViewScoped 和 SessionScoped,但都没有解决问题。 @yavuzkavus
  • 在您的页面上将h:form 移到ui:repeat 之外后,您可以再试一次吗?
  • 如果我将h:form 放在ui:repeat 之外,则不会显示任何搜索结果。这甚至不是我的本意。我想要每个显示的比萨饼对象都有一个“添加到购物车按钮”。我真的认为这个问题的原因是由变量 selectedSize 和 selectedQuantity 引起的。 @tt_emrah

标签: jsf javabeans facelets commandbutton uirepeat


【解决方案1】:

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

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

为了使其正常工作,您必须:

  1. 切换到其他实现,例如 Apache MyFaces,此代码可以在其中工作(请参阅 my implementation
  2. 或将表单移出ui:repeat
  3. that answer 中所建议:

    使用另一个迭代组件(例如&lt;c:forEach&gt;&lt;h:dataTable&gt;&lt;t:dataList&gt;&lt;p:dataList&gt; 等)

但是,像@user2314868 建议的那样,简单地将表单移到ui:repeat 之外是行不通的。这是因为所有字段都是从表单中发布的。因此,每个h:selectOneRadio 在更新模型值阶段都会更新#{pizzaResult.chosenSize}。因此,在调用应用程序阶段只能看到最后一次更新。 #{pizzaResult.chosenQuantity} 也是如此。

为了让它工作,我建议用值数组替换像 selectedSize 这样的单个值。比我们可以利用ui:repeat 的状态变量的index 属性。

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

@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]);
    }
...

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

【讨论】:

  • 正确。必须使用 ajax&execute,或者变量必须存储在数组中。
  • 感谢您的详细回答!对我来说这似乎是合乎逻辑的,但是当我想执行我的程序时仍然遇到一个问题:单击“添加到购物车”按钮后,我的浏览器告诉我有一个错误:“/pizzasearch.xhtml @112,134 value="#{pizzaResult.chosenQuantity[loop.index]}": null"。你知道怎么处理吗?
  • 您是否也对PizzaResult 进行了更改?我将文件添加到答案中。
  • 我发现在 PostConstruct 提供的 initResults() 方法中,results.size 的值为 null。因此 selectedSize 和 selectedQuantity 也是 0。我怎样才能克服这个错误?
  • null的大小怎么可以是int类型的?
【解决方案2】:

步骤:

第 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

【讨论】:

  • 不幸的是,在您的步骤之后发生了同样的错误。我已经将 h:form 放在了 ui:repeat 之外,我给了它一些 ID,因为我使用的是 NetBeans,所以我使用了 ajax &lt;h:commandButton ... &gt; &lt;f:ajax render="@form"/&gt; &lt;/:h:commandButton&gt; 的等效更新版本。我还在 addToCard() 中重置了 selectedSize 和 selectedQuantity 的值,但没有解决这个问题。
猜你喜欢
  • 2013-06-04
  • 2011-08-21
  • 1970-01-01
  • 1970-01-01
  • 2015-10-10
  • 1970-01-01
  • 1970-01-01
  • 2020-01-14
  • 1970-01-01
相关资源
最近更新 更多