-
Available since OmniFaces 1.0
The omnifaces.SelectItemsConverter
allows you to populate a selection component with complex Java model objects (entities) as value of <f:selectItems>
and have Faces convert those automatically back without the need to provide a custom converter which may need to do the job based on possibly expensive service/DAO operations. This converter automatically converts based on the Object.toString()
of the selected item.
Usage
This converter is available by converter ID omnifaces.SelectItemsConverter
. Just specify it in the converter
attribute of the selection component holding <f:selectItems>
.
<h:selectOneMenu value="#{bean.selectedItem}" converter="omnifaces.SelectItemsConverter">
<f:selectItems value="#{bean.availableItems}" />
</h:selectOneMenu>
Since OmniFaces 4.5 it's also available by <o:selectItemsConverter>
tag.
<h:selectOneMenu value="#{bean.selectedItem}">
<f:selectItems value="#{bean.availableItems}" />
<o:selectItemsConverter />
</h:selectOneMenu>
Make sure that your entity has a good toString()
implementation
The base converter uses by default the toString()
method of the entity to uniquely identify the instance during the conversion. This is sufficient if your (abstract base) entity has a toString()
implementation which looks something like this:
@Override
public String toString() {
return String.format("%s[id=%d]", getClass().getSimpleName(), getId());
}
By the way, you should also make sure that your entity has a good equals()
and hashCode()
implementation, otherwise Faces won't be able to set the right entity back in the model. Please note that this problem is in turn unrelated to the SelectItemsConverter
, you would have faced the same problem when using any other converter.
If your entity can't have a good toString()
implementation
However, if the entity doesn't have a toString()
implementation (and thus relies on the default fqn@hashcode
implementation), or the existing implementation doesn't necessarily uniquely identify the instance, and you can't implement/change it, then it is recommended to extend the SelectItemsConverter
class and override only the getAsString
method wherein the desired implementation is provided. For example:
@FacesConverter("exampleEntitySelectItemsConverter")
public class ExampleEntitySelectItemsConverter extends SelectItemsConverter {
@Override
public String getAsString(FacesContext context, UIComponent component, Object value) {
Long id = (value instanceof ExampleEntity) ? ((ExampleEntity) value).getId() : null;
return (id != null) ? String.valueOf(id) : null;
}
}
Again, you do not need to override the getAsObject()
method which would only need to perform possibly expensive service/DAO operations. The SelectItemsConverter
base converter will already do it automatically based on the available items and the getAsString()
implementation.
An alternative is to switch to SelectItemsIndexConverter
, which will convert based on the position (index) of the selected item in the list instead of the Object.toString()
of the selected item.
The below demo uses the base converter only, as ExampleEntity
already returns its unique
identifier in the toString()
method. If you intend to use the extended select items converter
for your entities based on the above example, then just specify the converter as
converter="exampleEntitySelectItemsConverter"
instead.
<p>
The below demo uses the base converter only, as <code>ExampleEntity</code> already returns its unique
identifier in the <code>toString()</code> method. If you intend to use the extended select items converter
for your entities based on the above example, then just specify the converter as
<code>converter="exampleEntitySelectItemsConverter"</code> instead.
</p>
<h:form>
<p>
<b>Last selected item:</b> <h:outputText id="selected_item" value="#{selectItemsBean.selectedEntity.value}" />
</p>
<h:panelGrid columns="2">
<h:outputLabel for="iterator" value="Items with iterator: " />
<h:selectOneMenu id="iterator" value="#{selectItemsBean.selectedEntity}" converter="omnifaces.SelectItemsConverter">
<f:selectItem itemLabel="Choose item" noSelectionOption="true" />
<f:selectItems value="#{selectItemsBean.exampleEntities}" var="entity" itemLabel="#{entity.value}" itemValue="#{entity}" />
<f:ajax render="selected_item" />
</h:selectOneMenu>
<h:outputLabel for="iterator_def_value" value="Items with iterator (default value): " />
<h:selectOneMenu id="iterator_def_value" value="#{selectItemsBean.selectedEntity}" converter="omnifaces.SelectItemsConverter">
<f:selectItem itemLabel="Choose item" noSelectionOption="true" />
<f:selectItems value="#{selectItemsBean.exampleEntities}" var="entity" itemLabel="#{entity.value}" />
<f:ajax render="selected_item" />
</h:selectOneMenu>
<h:outputLabel for="selectitems" value="Items with SelectItem list: " />
<h:selectOneMenu id="selectitems" value="#{selectItemsBean.selectedEntity}" converter="omnifaces.SelectItemsConverter">
<f:selectItem itemLabel="Choose item" noSelectionOption="true" />
<f:selectItems value="#{selectItemsBean.selectItems}" />
<f:ajax render="selected_item" />
</h:selectOneMenu>
<h:outputLabel for="selectitems_array" value="Items with SelectItem array: " />
<h:selectOneMenu id="selectitems_array" value="#{selectItemsBean.selectedEntity}" converter="omnifaces.SelectItemsConverter">
<f:selectItem itemLabel="Choose item" noSelectionOption="true" />
<f:selectItems value="#{selectItemsBean.selectItemArray}" />
<f:ajax render="selected_item" />
</h:selectOneMenu>
</h:panelGrid>
</h:form>
package org.omnifaces.showcase.converters;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import jakarta.annotation.PostConstruct;
import jakarta.faces.model.SelectItem;
import jakarta.inject.Named;
import org.omnifaces.cdi.ViewScoped;
import org.omnifaces.showcase.model.ExampleEntity;
import org.omnifaces.util.selectitems.SelectItemsBuilder;
@Named
@ViewScoped
public class SelectItemsBean implements Serializable {
private static final long serialVersionUID = 1L;
// 3 different forms of the defining data to which SelectItems can bind.
private List<ExampleEntity> exampleEntities;
private List<SelectItem> selectItems;
private SelectItem[] selectItemArray;
private ExampleEntity selectedEntity;
@PostConstruct
public void init() {
exampleEntities = new ArrayList<>();
exampleEntities.add(new ExampleEntity(1L, "Amsterdam"));
exampleEntities.add(new ExampleEntity(2L, "Frankfurt"));
exampleEntities.add(new ExampleEntity(3L, "London"));
selectItems = new SelectItemsBuilder()
.add(new ExampleEntity(4L, "New York"), "New York")
.add(new ExampleEntity(5L, "Miami"), "Miami")
.add(new ExampleEntity(6L, "Los Angeles"), "Los Angeles")
.buildList();
selectItemArray = new SelectItemsBuilder()
.add(new ExampleEntity(7L, "Willemstad"), "Willemstad")
.add(new ExampleEntity(8L, "Oranjestad"), "Oranjestad")
.add(new ExampleEntity(9L, "Kralendijk"), "Kralendijk")
.build();
}
public List<ExampleEntity> getExampleEntities() {
return exampleEntities;
}
public List<SelectItem> getSelectItems() {
return selectItems;
}
public SelectItem[] getSelectItemArray() {
return selectItemArray;
}
public ExampleEntity getSelectedEntity() {
return selectedEntity;
}
public void setSelectedEntity(ExampleEntity selectedEntity) {
this.selectedEntity = selectedEntity;
}
}
package org.omnifaces.showcase.model;
import java.io.Serializable;
public class ExampleEntity implements Serializable {
private static final long serialVersionUID = 1L;
private Long id;
private String value;
public ExampleEntity() {
//
}
public ExampleEntity(Long id, String value) {
this.id = id;
this.value = value;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
@Override
public boolean equals(Object other) {
return (other instanceof ExampleEntity) && (id != null)
? id.equals(((ExampleEntity) other).id)
: (other == this);
}
@Override
public int hashCode() {
return (id != null)
? (this.getClass().hashCode() + id.hashCode())
: super.hashCode();
}
@Override
public String toString() {
return String.format("ExampleEntity[%d, %s]", id, value);
}
}