If you are making a simple attempt to declare an instance variable in a converter, you will notice that you can't store the variable state over time. This may look like a strange behavior, but the truth is that the getAsObject
and getAsString
are called on different instances. This is the simple explanation of why the instance variable doesn't have persistence over these methods calls.
We can fix this using UIComponent set/getAttribute
or using a session variable instead. In this recipe, we will use a session variable to simulate an instance variable of a converter. For this, let's suppose that we have two numbers, one inserted by the user and one is selected by the user from a selectOneMenu
component. The inserted value is multiplied with the selected value, inside of a custom converter, in the getAsObject
method. In the backing bean we keep the multiplied result. Before the result is rendered, its value is divided
by the same value in the getAsString
method. If everything works fine, then we will not notice these operations over the inserted value.
We developed this recipe with NetBeans 6.8, JSF 2.0, and GlassFish v3. The JSF 2.0 classes were obtained from the NetBeans JSF 2.0 bundled library.
Storing the selected value in the session is a simple task. First, the backing bean associated to this value is marked with the annotation @SessionScoped
, indicating that the instance of this bean should be stored in session. Second, we pass the selected value in the traditional way (this code is from the multiply.xhtml
page of the application), as shown next:
<h:form id="MultiplyForm"> <h:outputText value="Select the multiply factor:" /> <h:selectOneMenu id="factorID" value="#{factorBean.selectedFactor}"> <f:selectItems value="#{factorBean.factors}"/> </h:selectOneMenu> <h:commandButton id="submit" action= "number?faces-redirect=true" value="Submit"/> </h:form>
The selectedFactor
property belongs to the next backing bean:
package multiply; import java.util.LinkedList; import java.util.List; import javax.faces.bean.ManagedBean; import javax.faces.bean.SessionScoped; import javax.faces.model.SelectItem; @ManagedBean @SessionScoped public class FactorBean { private List<SelectItem> factors = new LinkedList<SelectItem>(); private double selectedFactor; public FactorBean(){ factors.add(new SelectItem("1.0", "1.0")); factors.add(new SelectItem("2.0", "2.0")); factors.add(new SelectItem("3.0", "3.0")); factors.add(new SelectItem("4.0", "4.0")); factors.add(new SelectItem("5.0", "5.0")); } public List<SelectItem> getFactors() { return factors; } public void setFactors(List<SelectItem> factors) { this.factors = factors; } public double getSelectedFactor() { return this.selectedFactor; } public void setSelectedFactor(double selectedFactor) { this.selectedFactor = selectedFactor; } }
Now, the multiplication factor is on session and we can request the user to insert a value to be multiplied by this factor (number.xhtml
), as shown next:
<h:form id="NumberForm"> <h:outputText value="Insert the value to be multiplied:"/> <h:inputText id="valueID" required="true" value="#{multiplyBean.value}" converter="multiplyConverter" /> <h:message showSummary="true" showDetail="false" for="valueID" style="color: red; text-decoration:overline"/> <br /> <h:commandButton id="submit" action="number?faces- redirect=true" value="Submit"/> </h:form>
The value
is stored in the MultiplyBean
, as shown next:
package multiply; import javax.faces.bean.ManagedBean; import javax.faces.bean.SessionScoped; @ManagedBean @SessionScoped public class MultiplyBean { private double value = 0.0d; public double getValue() { return this.value; } public void setValue(double value) { this.value = value; } }
As you can see the operations are taking place in a converter. Now, the converter has access to the multiplication factor in a very easy approach, as shown next:
public String getAsString(FacesContext arg0, UIComponent arg1, Object arg2) { if (arg0 == null) { throw new NullPointerException("context"); } if (arg1 == null) { throw new NullPointerException("component"); } FacesContext ctx = FacesContext.getCurrentInstance(); ValueExpression vex = ctx.getApplication().getExpressionFactory(). createValueExpression(ctx.getELContext(), "#{factorBean}", FactorBean.class); FactorBean c = (FactorBean) vex.getValue(ctx.getELContext()); try { Double dividedVal = (Double) arg2 / c.getSelectedFactor(); return dividedVal.toString(); } catch (Exception e) { FacesMessage message = new FacesMessage(FacesMessage.SEVERITY_ERROR, "Error!", "Cannot accomplish this operation (DIVIDE) !"); throw new ConverterException(message); } } public Object getAsObject(FacesContext arg0, UIComponent arg1, String arg2) { if (arg0 == null) { throw new NullPointerException("context"); } if (arg1 == null) { throw new NullPointerException("component"); } FacesContext ctx = FacesContext.getCurrentInstance(); ValueExpression vex = ctx.getApplication().getExpressionFactory(). createValueExpression(ctx.getELContext(), "#{factorBean}", FactorBean.class); FactorBean c = (FactorBean) vex.getValue(ctx.getELContext()); try { Double val = new Double(arg2); Double multiplyVal = val * c.getSelectedFactor(); return multiplyVal; } catch (NumberFormatException e) { FacesMessage message = new FacesMessage(FacesMessage.SEVERITY_ERROR, "Error!","Connot accomplish this operation (MULTIPLY)!"); throw new ConverterException(message); } }