Getting selection from p:selectOneMenu in PrimeFaces - jsf

I want to select a value from a p:selectOneMenu component (a dropdownlist) in Primefaces. I get my data from a Java Bean. I have the following code:
XHTML:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:p="http://primefaces.org/ui">
<h:body>
<h:form>
<p:messages id="errorMessages" style="color:red;margin:8px;" />
<br></br>
<p:panelGrid columns="2" style="margin-bottom:10px" cellpadding="5">
<h:outputText value="Tasks: "/>
<p:selectOneMenu value="#{devTestController.selectedTask}">
<f:selectItems value="#{devTestController.tasks}" var="task" itemLabel="#{task.label}" itemValue="#{task.value}"/>
<f:converter converterId="infoRowBeanConverter" />
</p:selectOneMenu>
</p:panelGrid>
<br/>
<p:commandButton value="Execute Task" update = "errorMessages" action="#{devTestController.executeTask()}"/>
</h:form>
</h:body>
</html>
Java Bean DevTestController.java:
package mypackage;
import java.util.LinkedList;
import java.util.List;
import javax.annotation.PostConstruct;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.RequestScoped;
import javax.faces.context.FacesContext;
import javax.servlet.http.HttpServletRequest;
#ManagedBean
#RequestScoped
public class DevTestController
{
private InfoRowBean selectedTask;
private static List<InfoRowBean> tasks;
#PostConstruct
public void initList()
{
if (tasks == null)
{
tasks = new LinkedList<>();
tasks.add(new InfoRowBean("Task 1", "Task 1"));
tasks.add(new InfoRowBean("Task 2", "Task 2"));
}
}
public InfoRowBean getSelectedTask()
{
return selectedTask;
}
public void setSelectedTask(InfoRowBean selectedTask)
{
this.selectedTask = selectedTask;
}
public List<InfoRowBean> getTasks()
{
return tasks;
}
public void executeTask()
{
System.out.println("Executing task " + selectedTask.label);
}
}
InfoRowBean.java:
package mypackage;
import java.util.List;
public class InfoRowBean
{
String label = null;
String value = null;
public InfoRowBean(String label, String value)
{
setLabel(label);
setValue(value);
}
public String getLabel()
{
return label;
}
public void setLabel(String label)
{
this.label = label;
}
public String getValue()
{
return value;
}
public void setValue(String value)
{
this.value = value;
}
// This must return true for another InfoRowBean object with same label/id.
public boolean equals(Object other)
{
return other instanceof InfoRowBean && (label != null) ? label.equals(((InfoRowBean) other).label) : (other == this);
}
// This must return the same hashcode for every InfoRowBean object with the same label.
public int hashCode()
{
return label != null ? this.getClass().hashCode() + label.hashCode() : super.hashCode();
}
// Override Object#toString() so that it returns a human readable String representation.
// It is not required by the Converter or so, it just pleases the reading in the logs.
public String toString()
{
return "InfoRowBean[" + label + "," + value + "]";
}
}
Converter InfoRowBeanConverter.java:
package mypackage;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.convert.Converter;
import javax.faces.convert.FacesConverter;
#FacesConverter("infoRowBeanConverter")
public class InfoRowBeanConverter implements Converter
{
public Object getAsObject(FacesContext context, UIComponent component, String value)
{
return value;
}
public String getAsString(FacesContext context, UIComponent component, Object value)
{
return value.toString();
}
}
If I press the button nothing happens (no error also). If I remove the parameter "value" from tag (namely leave ), the button works fine, but of course I don't get the selected item. What is the problem here?

The problem is that your converter isn't converting the submitted string value to a concrete InfoRowBean instance in getAsObject() method, but instead returning the raw submitted String value as you have generated in getAsString() method. This doesn't match the type of selectedTask, which is InfoRowBean.
You need to fix your converter accordingly in such way that getAsString() returns the unique string representation of the complex object, usually in flavor of the database identifier (so that it can be used further in text based formats such as HTML output and HTTP request parameters), and that getAsObject() converts exactly that unique string representation back to the concrete complex object instance, usually via a DB call using the unique identifier as key.
An alternative is to use omnifaces.SelectItemsConverter of the JSF utility library OmniFaces, so that you never need to create custom converters for components using <f:selectItem(s)> with complex objects as values.
Another alternative is to change selectedTask to be String instead of InfoRowBean (and get rid of the whole converter as it is completely useless in this construct).
See also:
How to populate options of h:selectOneMenu from database?

Related

JSF SelectOneMenu showing wrong value after validation error on another field was fixed and form re-submitted

I am running into a very odd issue where the selectOneMenu jsf control is showing the wrong value as selected one (even though the model gets updated with the correct value) after there was a validation error on a completely different field on a previous (not current) submit.
How to replicate (minimum code below):
load the form and leave the "Required text field" text box blank.
Set some values to Active and Inactive in a couple of dropdowns
below.
Submit the form. There will be an error message about the
"Required text field" being blank and the drop downs you set will
still have the same value you gave them (expected). The printout of
what the model has will still show the old value and not the value
you selected (expected).
Put some text into the "Required text
field" and submit the form.
The error message goes away and the
form gets "saved". The printouts of what the model has next to the
dropdowns have the value you set (expected), however, the dropdowns
themselves no longer have the value you selected as the selected
option (NOT EXPECTED).
Sample code:
<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://xmlns.jcp.org/jsf/html"
xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
xmlns:f="http://xmlns.jcp.org/jsf/core">
<h:head>
<title>Facelet Title</title>
</h:head>
<h:body>
<h:form id="myForm">
<div>
<h:outputLabel value="Required text field" for="reqField" />
<h:inputText value="#{myBeanController.requiredField}" id="reqField" label="Required Text Field" required="true" />
</div>
<div>
<h:outputLabel value="some objects" />
<ui:repeat value="#{myBeanController.bunchOfObjects}" var="obj">
<div>
value on obj model: #{obj}
<h:selectOneMenu value="#{obj.type}">
<f:selectItems value="#{myBeanController.availableRequiredTypeOptions}" />
</h:selectOneMenu>
</div>
</ui:repeat>
</div>
<div>
<h:commandButton value="Submit" action="#{myBeanController.save}" />
</div>
</h:form>
</h:body>
</html>
Controller bean:
package com.mycompany.reproducefieldnotsavingaftervalidation;
import java.util.Collection;
import java.util.LinkedList;
import java.util.Map;
import java.util.TreeMap;
import javax.annotation.PostConstruct;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.SessionScoped;
import org.apache.commons.lang3.text.WordUtils;
#ManagedBean
#SessionScoped
public class MyBeanController {
private String requiredField;
private Collection<MyDomainClass> bunchOfObjects;
#PostConstruct
public void init(){
// create some sample objects
bunchOfObjects = new LinkedList<>();
bunchOfObjects.add(new MyDomainClass(MyDomainClass.PossibleTypes.ACTIVE));
bunchOfObjects.add(new MyDomainClass(MyDomainClass.PossibleTypes.INACTIVE));
bunchOfObjects.add(new MyDomainClass(MyDomainClass.PossibleTypes.UNKNOWN));
bunchOfObjects.add(new MyDomainClass(MyDomainClass.PossibleTypes.UNKNOWN));
bunchOfObjects.add(new MyDomainClass(MyDomainClass.PossibleTypes.UNKNOWN));
bunchOfObjects.add(new MyDomainClass(MyDomainClass.PossibleTypes.UNKNOWN));
}
public void save() {
System.out.println("SAVING FORM");
System.out.println("requiredField: " + getRequiredField());
System.out.println("Bunch of objects: ");
for(MyDomainClass obj : getBunchOfObjects()) {
System.out.println("/tObj: " + obj);
}
}
public Map<String, String> getAvailableRequiredTypeOptions() {
Map<String, String> options = new TreeMap<>();
for(MyDomainClass.PossibleTypes type : MyDomainClass.PossibleTypes.values()) {
// make the text of the option pretty by removing all caps and replacing underscores with space
options.put(WordUtils.capitalizeFully(type.name(), new char[]{'_'}).replaceAll("_", " "), type.name());
}
return options;
}
public String getRequiredField() {
return requiredField;
}
public void setRequiredField(String requiredField) {
this.requiredField = requiredField;
}
public Collection<MyDomainClass> getBunchOfObjects() {
return bunchOfObjects;
}
public void setBunchOfObjects(Collection<MyDomainClass> bunchOfObjects) {
this.bunchOfObjects = bunchOfObjects;
}
}
Model:
package com.mycompany.reproducefieldnotsavingaftervalidation;
import java.util.Objects;
public class MyDomainClass {
private String type;
public MyDomainClass() { }
public MyDomainClass(PossibleTypes type) {
this.type = type.name();
}
public MyDomainClass(String type) {
this.type = type;
}
public static enum PossibleTypes {
UNKNOWN, ACTIVE, INACTIVE
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
#Override
public String toString() {
return "MyDomainClass{" + "type=" + type + '}';
}
#Override
public int hashCode() {
int hash = 7;
hash = 79 * hash + Objects.hashCode(this.getType());
return hash;
}
#Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (!(obj instanceof MyDomainClass)) {
return false;
}
final MyDomainClass other = (MyDomainClass) obj;
if (!Objects.equals(this.getType(), other.getType())) {
return false;
}
return true;
}
}
Switching the
<ui:repeat>
to
<c:forEach>
Seems to have fixed the issue. I don't know why the ui:repeat was causing issues only on a save after previously failed validation and not on a first valid save (if anyone know the answer to this, please post it, I would be interested to know). Based on some research, ui:repeat happens at a different phase than f:selectItem or f:selectItems, but then I would expect the issue to happen on every save and it's not.
Regardless, if you're seeing something similar and your selects are inside of a ui:repeat, try switching that out with a c:forEach to see if that fixes it.

How to store value of selectOneMenu in controller class variable using JSF and PrimeFaces [duplicate]

This question already has answers here:
Conversion Error setting value for 'null Converter' - Why do I need a Converter in JSF?
(2 answers)
Closed 7 years ago.
Inside my controller StudentController I have a variable Course course and I want to set this variable with the value, selected in a selectOneMenu, inside my JSF-page. Clicking the save-button would temporarily just call the JSF-page index (I will fill that part with my own code that will persist the student course, which will not be a problem).
The corresponding part in my JSF-page:
<h:form>
<h:panelGrid columns="2" style="margin-bottom:10px" cellpadding="5">
<p:outputLabel for="course" value="Select Course:" />
<p:selectOneMenu id="course" value="#{studentController.course}">
<f:selectItem itemLabel="--- Please Select ---" itemValue="" />
<f:selectItems value="#{studentController.courses}" var="course"
itemLabel="#{course.name}" itemValue="#{course}" />
</p:selectOneMenu>
</h:panelGrid>
<p:commandButton action="index?faces-redirect=true"
value="Save" icon="ui-icon-circle-check" />
</h:form>
The corresponding part in the controller:
#ManagedBean
#SessionScoped
public class StudentController {
// list contains available courses, is not empty
private List<Course> courses;
private Course course = new Course();
// getter, setter and other functionality
}
When I click the save-button, nothing happens. No error messages in my console, no redirecting.
How can I store the value, selected on the selectOneMenu inside my course variable?
In order to map value from <p:selectOneMenu to your java object you'll need to use jsf converter.
You can use generic converter without adding any dependencies
import java.util.Map;
import java.util.Map.Entry;
import java.util.UUID;
import java.util.WeakHashMap;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.convert.Converter;
import javax.faces.convert.FacesConverter;
#FacesConverter(value = "entityConverter")
public class EntityConverter implements Converter {
private static Map<Object, String> entities = new WeakHashMap<Object, String>();
#Override
public String getAsString(FacesContext context, UIComponent component, Object entity) {
synchronized (entities) {
if (!entities.containsKey(entity)) {
String uuid = UUID.randomUUID().toString();
entities.put(entity, uuid);
return uuid;
} else {
return entities.get(entity);
}
}
}
#Override
public Object getAsObject(FacesContext context, UIComponent component, String uuid) {
for (Entry<Object, String> entry : entities.entrySet()) {
if (entry.getValue().equals(uuid)) {
return entry.getKey();
}
}
return null;
}
}
Then change your xhtml as
<p:selectOneMenu converter="entityConverter">
Now you'll have selected value in the managed bean.

Using SelectManyCheckbox with list of objects

Im trying to create a JSF page that lets the user select X amount of ingredients, and then saves those selected ingredients to a list.
Ingredient is an object with two values, String IngredientName and int ingredientPrice.
What I want to do is to create 1 selectItem per Ingredient in an IngredientList (dynamically sized), and then save the selected items to another list of ingredients.
I've tried doing this multiple different ways but either I get classcast exceptions or the checkboxes don't appear at all.
My Bean:
#ManagedBean
#SessionScoped
public class ManagedIngredientsBean {
#EJB
IngredientBean iBean;
private List<Ingredient> ingredientList;
private List<Ingredient> checkedOptions;
private List<SelectItem> selectList;
public ManagedIngredientsBean() {
}
public String createNew(){
ingredientList = iBean.getAllIngredients();
selectList = new ArrayList<SelectItem>(ingredientList.size());
for(Ingredient i : ingredientList){
selectList.add(new SelectItem(i.getIngredientName()));
}
return "createnew.xhtml";
}
public List<SelectItem> getSelectList() {
return selectList;
}
public void setSelectList(List<SelectItem> selectList) {
this.selectList = selectList;
}
public List<Ingredient> getCheckedOptions() {
return checkedOptions;
}
public void setCheckedOptions(List<Ingredient> checkedOptions) {
this.checkedOptions = checkedOptions;
}
public List<Ingredient> getIngredientList() {
return ingredientList;
}
public void setIngredientList(List<Ingredient> ingredientList) {
this.ingredientList = ingredientList;
}
#FacesConverter(value="userConverter")
public static class UserConverter implements Converter {
public Object getAsObject(FacesContext facesContext,
UIComponent component, String value) {
return value;
}
public String getAsString(FacesContext facesContext,
UIComponent component, Object o) {
Ingredient i = (Ingredient) o;
return i.getIngredientName();
}
}
}
IngredientBean used to get the Ingredient items from the persistence database and returning them as a list:
#Stateless(name = "IngredientEJB")
public class IngredientBean {
EntityManagerFactory entFactory;
EntityManager em;
public IngredientBean() {
entFactory = Persistence.createEntityManagerFactory("NewPersistenceUnit");
em = entFactory.createEntityManager();
}
public List<Ingredient> getAllIngredients(){
TypedQuery<Ingredient> ingQuery = em.createQuery("SELECT i FROM Ingredient i", Ingredient.class);
List<Ingredient> iList = ingQuery.getResultList();
return iList;
}
}
My JSF Page:
<!DOCTYPE html
PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://xmlns.jcp.org/jsf/core">
<h:head>
<title>Create New Order</title>
</h:head>
<h:body>
<h:form>
<h:selectManyCheckbox value = "#{managedIngredientsBean.checkedOptions}">
<f:converter converterId="userConverter"/>
<f:selectItem value = "#{managedIngredientsBean.selectList}" var = "item" itemLabel = "#{item.getIngredientName()}" itemValue = "#{item}"/>
</h:selectManyCheckbox>
</h:form>
</h:body>
</html>
I'm probably missing something obvious or simply misunderstanding how to use the selectManyCheckbox element but I'm completely stuck on how to fix this. Appreciate any answers on how I should be implementing this. :)
Edit: Forgot to mention, the createNew() method in the managed bean is called in the previous JSF page and redirects to this one.
your converter is broken.
first, it have to be a bean so must not be a static class.
second, it "should" be symmetric:
x.equals(c.getAsObject(ctx, comp, c.getAsString(ctx, component, x))); "should" be true.
#FacesConverter(value="userConverter")
public class UserConverter implements Converter
{
public Object getAsObject(FacesContext facesContext, UIComponent component, String value)
{
return database.loadIngredientByUniqueValue(value);
}
public String getAsString(FacesContext facesContext,UIComponent component, Object o)
{
Ingredient i = (Ingredient) o;
return i.getSomeUniqueValue();
}
}

Custom component inside <ui:repeat> doesn't find iterated item during encode

I'm trying to create a custom component for displaying an Entity with a certain form. So I've created my #FacesComponent and he's working but only when he is not inside a loop like <ui:repeat>. When I'm using the following code, my component is displaying null values for price and photo but not for name. Do you have an explaination ?
XHTML code :
<ui:define name="content">
<f:view>
<h:form>
<ui:repeat value="#{dataManagedBean.listNewestCocktails}" var="item" varStatus="status">
<h:outputText value="#{item.price}"/> <!--working very well-->
<t:cocktailVignette idPrefix="newCocktails" name="foo" price="#{item.price}" urlPhoto="#{item.photoURI}"/> <!-- not working the getPrice here -->
</ui:repeat>
<!--<t:cocktailVignette idPrefix="allCocktails" name="OSEF" price="20" urlPhoto="osefdelurl" ></t:cocktailVignette> -->
</h:form>
</f:view>
My component code :
package component;
import java.io.IOException;
import javax.faces.context.FacesContext;
import javax.faces.component.FacesComponent;
import javax.faces.component.UIComponentBase;
import javax.faces.context.ResponseWriter;
#FacesComponent(value = "CocktailVignette")
public class CocktailVignette extends UIComponentBase {
private String idPrefix;
private String name;
private String price;
private String urlPhoto;
public String getIdPrefix() {
return idPrefix;
}
public void setIdPrefix(String idPrefix) {
this.idPrefix = idPrefix;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPrice() {
return price;
}
public void setPrice(String price) {
this.price = price;
}
public String getUrlPhoto() {
return urlPhoto;
}
public void setUrlPhoto(String urlPhoto) {
this.urlPhoto = urlPhoto;
}
#Override
public String getFamily() {
return "CocktailVignette";
}
#Override
public void encodeBegin(FacesContext context) throws IOException {
ResponseWriter writer = context.getResponseWriter();
writer.write("<div id=\""+idPrefix+name+"\" class=\"cocktail-vignette\">");
writer.write("<h2>"+name+"</h2>");
writer.write("<h3>"+price+"</h3>");
writer.write("</div>");
}
}
Thanks a lot :) I'm trying but nothing is working ...
All of component's attributes which are sensitive to changes in state (e.g. the value being dependent on <ui:repeat var>, at least those which is not known during view build time but during view render time only), must delegate the storage of attribute value to the state helper as available by inherited getStateHelper() method.
Kickoff example:
public String getPrice() {
return (String) getStateHelper().eval("price");
}
public void setPrice(String price) {
getStateHelper().put("price", price);
}
Apply the same for all other attributes and get rid of the instance variable declarations. Important note is that the state helper key ("price" in above example) must be exactly the same as attribute name.
See also:
How to save state when extending UIComponentBase

i can't pass values from source to target in primefaces picklist

That's my first post.I am trying to make a picklist for my primefaces application.I can see the values listed in source part and also i can pass source values to target panel in the screen.But in the server side,tariffListForDestination (binded to target panel in picklist) still remains empty.But in the screen everything is ok.And also when i debug my application and made a breakpoint in the ontransfer method and pass a source object to target,i see no value in the event.getItems(event TransferEvent).
I am sending related part of my code below.
Thanks for reply.
DÇ
my xhtml code snippet:
<p:pickList id="pojoPickList" value="#{serviceCopyInsertBean.tariffList}" var="tariff" effect="bounce"
itemValue="#{tariff}" itemLabel="#{tariff.cpcmOfferName}" converter="tmp2TariffDescMappingConverter"
showSourceControls="true" showTargetControls="true" showCheckbox="true"
showSourceFilter="true" showTargetFilter="true" filterMatchMode="contains" >
<f:facet name="sourceCaption">Available</f:facet>
<f:facet name="targetCaption">Starting</f:facet>
<p:ajax event="transfer" listener="#{serviceCopyInsertBean.onTransfer}"/>
<p:column style="width:75%;">
#{tariff.cpcmOfferName}
</p:column>
</p:pickList>
My converter Tmp2TariffDescMappingConverter
import java.util.ArrayList;
import java.util.List;
import javax.faces.application.FacesMessage;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.convert.Converter;
import javax.faces.convert.ConverterException;
import javax.faces.convert.FacesConverter;
import com.i2i.st.entity.Tmp2TariffDescMapping;
#FacesConverter("tmp2TariffDescMappingConverter")
public class Tmp2TariffDescMappingConverter implements Converter {
private static List<Tmp2TariffDescMapping> tmp2TariffDescMappingDB;
static {
tmp2TariffDescMappingDB = new ArrayList<Tmp2TariffDescMapping>();
}
public Object getAsObject(FacesContext facesContext, UIComponent component, String submittedValue) {
if (submittedValue.trim().equals("")) {
return null;
} else {
try {
int number = Integer.parseInt(submittedValue);
for (Tmp2TariffDescMapping tmp :tmp2TariffDescMappingDB) {
if (tmp.getCpcmOfferId() == number) {
return tmp;
}
}
} catch(NumberFormatException exception) {
throw new ConverterException(new FacesMessage(FacesMessage.SEVERITY_ERROR, "Conversion Error", "Tmp2TariffDescMapping"));
}
}
return null;
}
public String getAsString(FacesContext facesContext, UIComponent component, Object value) {
if (value == null) {
return null;
} else {
return String.valueOf(((Tmp2TariffDescMapping) value).getCpcmOfferId());
}
}
}
My JSF bean
private DualListModel<Tmp2TariffDescMapping> tariffList=new DualListModel<Tmp2TariffDescMapping>();
private List<Tmp2TariffDescMapping> tariffListForSource=new ArrayList<Tmp2TariffDescMapping>();
private List<Tmp2TariffDescMapping> tariffListForDestination=new ArrayList<Tmp2TariffDescMapping>();
tariffList=new DualListModel<Tmp2TariffDescMapping>(tariffListForSource, tariffListForDestination);
I don't understand why are you iterating over tmp2TariffDescMappingDB in the getAsObject(). How did you filled that list?
You should search into tariffList.getTarget() and tariffList.getSource() to look the both sides of the pickList. Something similiar to this generic one:
public T getAsObject(FacesContext context, UIComponent component, String value) {
...
if (component instanceof PickList) {
DualListModel<T> dl = (DualListModel) ((PickList) component).getValue();
for (T t : dl.getTarget()) {
//search here
}
for (T t : dl.getSource()) {
//maybe here too
}
...

Resources