Lalin Pethiyagoda
Lalin Pethiyagoda

Reputation: 45

Input field in <h:dataTable> always receives value of last item in list

I have a <h:dataTable> displaying a product catalog in Proudcts.xhtml:

<h:form name="ViewProductsManagedBean">
    <h:dataTable var="product" value="#{ViewProductsManagedBean.productsList}">
        <h:column>
            <h:outputText value="#{product.productid}" />
        </h:column>
        <h:column>
            <h:outputText value="#{product.itemcode}" />
        </h:column>
        <h:column>
            <h:outputText value="#{product.itemdescription}" />
        </h:column>
        <h:column>
            <h:outputText value="#{product.unitprice}" />
        </h:column>
        <h:column>
            <h:selectOneMenu value="#{ViewProductsManagedBean.quantityPurchased}" required="true">
                <f:selectItem itemValue="1" itemLabel="1" />
                <f:selectItem itemValue="2" itemLabel="2" />
                <f:selectItem itemValue="3" itemLabel="3" />
                <f:selectItem itemValue="4" itemLabel="4" />
                <f:selectItem itemValue="5" itemLabel="5"/>
            </h:selectOneMenu>
        </h:column>
        <h:column>
            <h:commandButton action="#{ViewProductsManagedBean.addItemToCart(product)}" value="Add to basket" />
        </h:column>
    </h:dataTable>
</h:form>

With this managed bean:

@ManagedBean(name="ViewProductsManagedBean")
@SessionScoped
public class ViewProductsManagedBean {

    private double unitprice;
    private String itemdescription;
    private String itemcode;
    private int quantityPurchased;
    private String result;

    @EJB
    ProductLocal productFacadeBean;

    @EJB
    CartFacade cartFunctions;

    private List<ProductEntity> productsList = new ArrayList<>();
    private List<StockEntity> stocksList = new ArrayList<>();
    private ProductEntity product;

    @PostConstruct
    private void init(){
        setProductsList();
        product = new ProductEntity();
    }

    public void addItemToCart(ProductEntity product) {
        int quantity=this.quantityPurchased;
        cartFunctions.addItemToCart(product, quantity);
        System.out.println(product.toString());         
    }

    // getters+setters
}

The problem is with the <h:selectOneMenu> to select the quantity. No matter what value is selected, the managed bean always receives a value of 1 for quantity, EXCEPT when the quantity is changed in the last item of the product catalog, in which case the quantity for ALL the items change to the value selected for the last item in the catalog, and the correct quantity is sent to the managed bean.

How is this caused and how can I solve it?

Upvotes: 1

Views: 1370

Answers (3)

BalusC
BalusC

Reputation: 1108632

Here,

<h:selectOneMenu value="#{ViewProductsManagedBean.quantityPurchased}">

You're basically binding the value of all input fields to one and same bean property. So, when the form gets submitted, every iteration of the table will override the bean property everytime with the submitted value of the current iteration round until you end up getting the value of the last row. If you have placed a debug breakpoint on the setter method, you should have noticed that.

This is definitely not right. You need to associate the input value with the currently iterated object. The simplest but naive way would be to directly associate it with the object:

<h:selectOneMenu value="#{product.quantityPurchased}">

This only tight-couples in your particular case the "quantity" model with the "product" model. It's functionally reasonable to keep them separated. A more proper solution is then to map the input value with currently iterated object as key (provided that the object has a proper equals() and hashCode() implementation, obviously):

<h:selectOneMenu value="#{ViewProductsManagedBean.quantitiesPurchased[product]}" converter="javax.faces.Integer">

With:

private Map<ProductEntity, Integer> quantitiesPurchased = new HashMap<>();

Regardless of the approach, in the action method, just iterate over it to collect them all.

Upvotes: 3

yohanfernando
yohanfernando

Reputation: 61

Agree with Matt. Change your behaviour to be Ajax bt adding and set to execute the @this and quantity and render the cart. That way it will only process that row, not the whole table.

Also I would change the action yo actionlistener.

Upvotes: -1

Matt Handy
Matt Handy

Reputation: 30025

Each line of your h:datatable references the same variable ViewProductsManagedBean.quantityPurchased. If the form is submitted, the value of ViewProductsManagedBean.quantityPurchased will be set for each line again and again. That's why the value of the last row defines the final state of quantityPurchased and this is the value you get in your method.

Upvotes: 1

Related Questions