1
votes

In a column of a table I've tried to bind a component value to a local scoped EL variable and later on to use that variable as a paramter:

        <h:column>
            <h:outputLabel value="Enter a quantity to put into the cart" for="qty"/>
            <h:inputText id="qty" binding="#{qty}" converter="javax.faces.Number"/>
        </h:column>
        <h:column>
            <h:commandButton value="Put into cart" type="submit"
                             action="#{shoppingCart.addToCart(product, qty)}"/>
        </h:column>

Where product is the current element of the datatable (a list of filtered or unfiltered products that are not in the cart).

Now when trying to add a product to the cart (e.g. with the quantity 12) it throws the following exception:

javax.faces.el.EvaluationException: java.lang.IllegalArgumentException: Cannot convert javax.faces.component.html.HtmlInputText@377c8b02 of type class javax.faces.component.html.HtmlInputText to class java.lang.Integer
at javax.faces.component.MethodBindingMethodExpressionAdapter.invoke(MethodBindingMethodExpressionAdapter.java:101)
at com.sun.faces.application.ActionListenerImpl.processAction(ActionListenerImpl.java:102)
at javax.faces.component.UICommand.broadcast(UICommand.java:315)
at javax.faces.component.UIData.broadcast(UIData.java:1108)
at javax.faces.component.UIViewRoot.broadcastEvents(UIViewRoot.java:790)
at javax.faces.component.UIViewRoot.processApplication(UIViewRoot.java:1282)
at com.sun.faces.lifecycle.InvokeApplicationPhase.execute(InvokeApplicationPhase.java:81)
at com.sun.faces.lifecycle.Phase.doPhase(Phase.java:101)
at com.sun.faces.lifecycle.LifecycleImpl.execute(LifecycleImpl.java:198)
at javax.faces.webapp.FacesServlet.service(FacesServlet.java:658)
at org.apache.catalina.core.StandardWrapper.service(StandardWrapper.java:1682)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:318)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:160)
at org.apache.catalina.core.StandardPipeline.doInvoke(StandardPipeline.java:734)
at org.apache.catalina.core.StandardPipeline.invoke(StandardPipeline.java:673)
at com.sun.enterprise.web.WebPipeline.invoke(WebPipeline.java:99)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:174)
at org.apache.catalina.connector.CoyoteAdapter.doService(CoyoteAdapter.java:416)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:283)
at com.sun.enterprise.v3.services.impl.ContainerMapper$HttpHandlerCallable.call(ContainerMapper.java:459)
at com.sun.enterprise.v3.services.impl.ContainerMapper.service(ContainerMapper.java:167)
at org.glassfish.grizzly.http.server.HttpHandler.runService(HttpHandler.java:206)
at org.glassfish.grizzly.http.server.HttpHandler.doHandle(HttpHandler.java:180)
at org.glassfish.grizzly.http.server.HttpServerFilter.handleRead(HttpServerFilter.java:235)
at org.glassfish.grizzly.filterchain.ExecutorResolver$9.execute(ExecutorResolver.java:119)
at org.glassfish.grizzly.filterchain.DefaultFilterChain.executeFilter(DefaultFilterChain.java:283)
at org.glassfish.grizzly.filterchain.DefaultFilterChain.executeChainPart(DefaultFilterChain.java:200)
at org.glassfish.grizzly.filterchain.DefaultFilterChain.execute(DefaultFilterChain.java:132)
at org.glassfish.grizzly.filterchain.DefaultFilterChain.process(DefaultFilterChain.java:111)
at org.glassfish.grizzly.ProcessorExecutor.execute(ProcessorExecutor.java:77)
at org.glassfish.grizzly.nio.transport.TCPNIOTransport.fireIOEvent(TCPNIOTransport.java:536)
at org.glassfish.grizzly.strategies.AbstractIOStrategy.fireIOEvent(AbstractIOStrategy.java:112)
at org.glassfish.grizzly.strategies.WorkerThreadIOStrategy.run0(WorkerThreadIOStrategy.java:117)
at org.glassfish.grizzly.strategies.WorkerThreadIOStrategy.access$100(WorkerThreadIOStrategy.java:56)
at org.glassfish.grizzly.strategies.WorkerThreadIOStrategy$WorkerThreadRunnable.run(WorkerThreadIOStrategy.java:137)
at org.glassfish.grizzly.threadpool.AbstractThreadPool$Worker.doWork(AbstractThreadPool.java:591)
at org.glassfish.grizzly.threadpool.AbstractThreadPool$Worker.run(AbstractThreadPool.java:571)
at java.lang.Thread.run(Thread.java:745)

I'm using Glassfish 4.1.1 with JSF version 2.2.12.

I'd be grateful for any ideas

CLARIFACATION

As pointed out by BalusC in this question JSF component binding without bean property and others, binding="#{var}" is actually valid XHTML. This way no backing bean is needed and the declared variable is request scoped. I find this option more elegant and hence would like to stick with it.

3
Why do you see it as an advantage that you do not need a backing bean for this? As a matter of fact, I cannot imagine you do not have a 'product' class in the shopping cart with a member field 'qty' or similar, so the 'backing bean' is already thereKukeltje
I have made a correction. It's actually about creating a new ShoppingCartItem. Sorry for ambigious questioningWecherowski

3 Answers

1
votes

Instead of binding="#{qty}" (which should be used to bind your input component to a Java instance of your component in a managed bean), use: value="#{shoppingCart.quantityAsInteger}", or if you are working with a list of shopping cart items: value="#{shoppingCartItem.quantityAsInteger}".

Apart from proper value binding this will also give you the benefit of having the quantity as part of your model. Additionally, when the value is bound to an Integer, there is no need to add a converter. JSF will take care of Integer (and some other types) conversion by default.

So, make sure you have a managed bean containing the quantity or a list of cart items which each have a quantity. In case of a list of cart items, you will end up with something like:

ShoppingCart.java

// You will probably already have something like this for your table
private List<ShoppingCartItem> shoppingCartItems;

ShoppingCartItem.java

// Quantity used for binding (add getter and setter).
private Integer quantity;

private Product product;

XHTML

<h:column>
    <h:outputLabel value="Enter a quantity to put into the cart" for="qty"/>
    <h:inputText id="qty" binding="#{shoppingCartItem.quantity}" />
</h:column>
0
votes

Try using value attribute instead of binding attribute in h:inputText and use binding attribute on a table component. That way you can access to the selected (clicked) row in a action method: if binding attribute of a table has value #{bean.dataTable}, you can get row data in a method like getDataTable.getRowData().

0
votes

Though declaring an EL variable is valid XHTML, the component itself is bound to the variable, if using the binding attribute. No conversion errors are thrown when using the EL variable with the value attribute, like that:

        <h:inputText id="qty" value="#{qty}" converter="javax.faces.Number"/>
        ....
        <h:commandButton value="Put into cart" type="submit"
                         action="#{shoppingCart.addToCart(product, qty)}"/>