Can't bind SelectItem list to <f:selectItem - jsf

I am using IceFaces components and I am trying to fill up a select with some values that correspond to a MangedBean property.
<h:form>
<ice:selectOneMenu size="1" style="width: 180px">
<f:selectItem value="#{stockManagedBean.listeCategoriesItem}"></f:selectItem>
</ice:selectOneMenu>
</h:form>
listeCategoriesItem is a property of StockManagedBean and is an ArrayList of SelectItem.
#ManagedBean
public class StockManagedBean {
CategorieDAO categorieDAO;
List<SelectItem> listeCategoriesItem;
public StockManagedBean() {
categorieDAO = new CategorieDAO();
listeCategoriesItem = new ArrayList<SelectItem>();
List<Categorie> listeCategories = categorieDAO.selectAllCat();
for(Categorie categorie: listeCategories) {
listeCategoriesItem.add(new SelectItem(categorie.getCatId(), categorie.getCatNom()));
}
}
public List<SelectItem> getListeCategoriesItem() {
return listeCategoriesItem;
}
public void setListeCategoriesItem(List<SelectItem> listeCategoriesItem) {
this.listeCategoriesItem = listeCategoriesItem;
}
}
I tested the values that come from my DAO and they are all correct. I also tested the values of the list in the getter and they are also correct, but when I load my html page, nothing is in the select list...

Use <f:selectItems> instead of <f:selectItem>. Note the s at the end of the former component.
<ice:selectOneMenu size="1" style="width: 180px">
<f:selectItems value="#{stockManagedBean.listeCategoriesItem}" />
</ice:selectOneMenu>
Also, it would be good to also have a field in your bean that will handle the value of the selected item in your selectOneMenu.
<ice:selectOneMenu size="1" style="width: 180px"
value="#{stockManagedBean.selectedCategory}">
<f:selectItems value="#{stockManagedBean.listeCategoriesItem}" />
</ice:selectOneMenu>
And in your managed bean:
#ManagedBean
public class StockManagedBean {
private String selectedCategory;
//rest of your code
//getters and setters...
}

Related

Pass value of p:selectOneMenu item in parameter function

I want to refresh my table when I select a value in the dropdown, and in the table I want to call a function with the selected value as a parameter.
The value I select has to come in the function calculateAveragePrice as a parameter.
This is my dropdown with all the different regions.
<p:selectOneMenu id="list" value="#{regionController.region}">
<f:selectItems value="#{regionController.regions}" var="region" itemLabel="#{region.name}"/>
<f:ajax execute="list" render="myTable" />
</p:selectOneMenu>
This is the table I want to refresh:
<table id="myTable" class="table table-responsive">
<tr>
<td>#{msg.averagePrice}:</td>
<td>#{flightController.calculateAveragePrice(regionController.region)}</td>
</tr>
</table>
RegionController bean:
#Named
#RequestScoped
public class RegionController {
#Inject
RegionService regionService;
private Region region;
private Collection<Region> regions;
#PostConstruct
public void initialize() {
regions = regionService.findAll();
}
public Region getRegion() {
return region;
}
public void setRegion(Region region) {
this.region = region;
}
public void setRegions(Collection<Region> regions) {
this.regions = regions;
}
public Collection<Region> getRegions() {
return regions;
}
}
If i got it right, it's just a design issue.
You could just create a property called region inside flightController.
Then, you would have:
<p:selectOneMenu id="list" value="#{FLIGHTCONTROLLER.region}">
<f:selectItems value="#{regionController.regions}" var="region" itemLabel="#{region.name}"/>
<f:ajax execute="list" render="myTable" />
</p:selectOneMenu>
Now, you don't have to pass the regions as parameter, because FlightController already have that property setted via ajax.
The way you did, you're setting regionController.Region for no reason.

How to display all enum values as <f:selectItems> with enum property as label

I have a RoleStatus enum which is as being a property of Role entity mapped to an integer in the DB (but that part is irrelevant). I'd like to present a List<Role> in a <p:dataTable> in which one column should have a <h:selectOneMenu> for the RoleStatus property of the Role entity. How can I implement this with or without OmniFaces?
Here's the enum:
public enum RoleStatus {
ACTIVE(1, "Active"),
DISABLE(2, "Disable");
private final int intStatus;
private final String status;
private RoleStatus(int intStatus, String status) {
this.intStatus = intStatus;
this.status = status;
}
public int getIntStatus() {
return status;
}
public String getStatus() {
return status;
}
}
Here's the backing bean:
#ManagedBean
#ViewScoped
public class RoleController {
private List<Role> roles;
#ManagedProperty("#{roleService}")
private IRoleService roleService;
#PostConstruct
public void init() {
roles = roleService.getRoles();
}
public List<Role> getRoles() {
return roles;
}
}
Finally, the data table where I'd like to have a <h:selectOneMenu> for the RoleStatus property of Role entity, showing all available enum values as select item options.
<h:form id="roleForm">
<p:dataTable value="#{roleController.roles}" var="role">
<p:column>
<h:outputText value="#{role.roleid}" />
</p:column>
<p:column>
<h:inputText value="#{role.role}" />
</p:column>
<p:column>
<h:inputText value="#{role.description}" />
</p:column>
<p:column>
<h:selectOneMenu value="#{role.roleStatus}">
<!-- How??? -->
</h:selectOneMenu>
</p:column>
</p:dataTable>
</h:form>
How can I achieve this? Do I need the OmniFaces SelectItemsConverter?
You don't need a converter. JSF has already a builtin enum converter.
Without OmniFaces, here's how you could provide the enum values as available items of <h:selectOneMenu>:
Add this method to RoleController:
public RoleStatus[] getRoleStatuses() {
return RoleStatus.values();
}
This way, all enum values are available by #{roleController.roleStatuses}.
Then use this dropdown:
<h:selectOneMenu value="#{role.roleStatus}">
<f:selectItems value="#{roleController.roleStatuses}" var="roleStatus"
itemValue="#{roleStatus}" itemLabel="#{roleStatus.status}" />
</h:selectOneMenu>
Note: as those values are static/applicationwide, it doesn't hurt to move the method to a separate #ApplicationScoped bean.
With OmniFaces, you could remove the additional getter and just import the enum directly via <o:importConstants>:
Add this somewhere in top of your template (assuming that it's in com.example package):
<o:importConstants type="com.example.RoleStatus" />
This way, the enum class itself is available by #{RoleStatus} (note the capitalization!).
Then use this dropdown:
<h:selectOneMenu value="#{role.roleStatus}">
<f:selectItems value="#{RoleStatus.values()}" var="roleStatus"
itemValue="#{roleStatus}" itemLabel="#{roleStatus.status}" />
</h:selectOneMenu>

p:selectOneMenu value still required

In my xhtml page i have two dependant selectOneMenu, with the second one being filled in an ajax call. Here's the jsf code fragment:
<h:panelGrid columns="2" cellpadding="5">
<h:outputLabel value="Dirección:" for="direccion"/>
<p:inputText id="direccion" value="#{datosInstitucion.institucion.direccion}" required="true" label="Dirección"/>
<h:outputLabel value="Departamento:" for="departamento"/>
<p:selectOneMenu id="departamento" value="#{datosInstitucion.idDepartamento}" required="true" label="Departamento">
<f:selectItem itemLabel="Seleccione el departamento" itemValue="#{null}"/>
<c:forEach items="#{datosInstitucion.departamentos}" var="departamento">
<f:selectItem itemLabel="#{departamento.nombre}" itemValue="#{departamento.id}"/>
</c:forEach>
<f:ajax render="municipio" listener="#{datosInstitucion.cargarMunicipios()}"/>
</p:selectOneMenu>
<h:outputLabel value="Municipio:" for="municipio"/>
<p:selectOneMenu id="municipio" value="#{datosInstitucion.idMunicipio}" required="true" label="Municipio">
<f:selectItem itemLabel="Seleccione el municipio" itemValue="#{null}"/>
<c:forEach items="#{datosInstitucion.municipios}" var="municipio">
<f:selectItem itemLabel="#{municipio.nombre}" itemValue="#{municipio.id}"/>
</c:forEach>
</p:selectOneMenu>
</h:panelGrid>
This fragment of code is inside a primefaces wizard component, so when the 'next' button is pressed a validation error is caused for the second selectOneMenu even when there's a value set.
What could be causing this behavior?
Relevant backing bean code:
#ManagedBean(name = "datosInstitucion")
#ViewScoped
public class DatosInstitucion implements Serializable{
#EJB
private Instituciones instituciones;
#EJB
private Parametros parametros;
#Inject
private Mensajes mensajes;
private List<Departamento> departamentos;
private List<Municipio> municipios;
private Map<Integer, Departamento> mapaDepartamentos;
private Integer idDepartamento;
private Integer idMunicipio;
private Institucion institucion;
#PostConstruct
private void inicializar(){
this.mapaDepartamentos = new HashMap<>();
this.departamentos = parametros.consultarDepartamentos();
for(Departamento departamento : departamentos){
this.mapaDepartamentos.put(departamento.getId(), departamento);
}
this.prepararInstitucion();
}
private void prepararInstitucion(){
this.institucion = new Institucion();
this.institucion.setResponsable(new Persona());
}
public Institucion getInstitucion() {
return institucion;
}
public List<Departamento> getDepartamentos(){
return departamentos;
}
public TipoIdentificacion[] getTiposIdentificacion(){
return TipoIdentificacion.deResponsables();
}
public Integer getIdDepartamento() {
return idDepartamento;
}
public void setIdDepartamento(Integer idDepartamento) {
this.idDepartamento = idDepartamento;
}
public Integer getIdMunicipio() {
return idMunicipio;
}
public void setIdMunicipio(Integer idMunicipio) {
this.idMunicipio = idMunicipio;
}
public void cargarMunicipios(){
idMunicipio = null;
if(idDepartamento != null){
this.municipios = mapaDepartamentos.get(idDepartamento).getMunicipios();
}else{
this.municipios = Collections.emptyList();
}
}
public List<Municipio> getMunicipios() {
return municipios;
}
public void confirmar(){
this.instituciones.guardar(institucion);
this.mensajes.exito("La institución ha sido registrada en el sistema");
this.prepararInstitucion();
}
}
This is because you are using JSTL <c:foreach> with JSF. The life cycle of JSTL vs JSF matters. JSTL is executed when the view is being built, while JSF is executed when the view is being rendered. The two do not work in synch with each other. In your case, you need to use <f:selectItems> instead of <c:foreach>
Replace:
<c:forEach items="#{datosInstitucion.municipios}" var="municipio">
<f:selectItem itemLabel="#{municipio.nombre}" itemValue="#{municipio.id}"/>
</c:forEach>
with:
<f:selectItems value="#{datosInstitucion.municipios}"
var="municipio" itemLabel="#{municipio.nombre}"
itemValue="#{municipio.id}"/>
For more reading, I suggest you to read the following answer

JSF h:selectOneMenu is never set even when using f:ajax

I want to implement a filtering facility in a JSF web application as follows: The users can add as many filters as they want. They can also delete them. So I am having a dataTable of filters. Each row consists of one h:selectOneMenu which has an ajax “change” event in order to make a second h:selectOneMenu visible in the same row. The options of the second h:selectOneMenu are calculated dynamically according to the selected option of the first.
The problem is that the value of second h:selectOneMenu is never set to the back-end object even if I added an ajax event. However the value of the first h:selectOneMenu is set.
I have the following fragment of code in an .xhtml page:
<h:form id="filterForm">
<h:dataTable id="filterTable" value="#{filterManager.filters}" var="filter">
<h:column>
<h:outputLabel value="#{msgs.filterBy}:" for="availableFilters" />
<h:selectOneMenu id="availableFilters" value="#{filter.filter}">
<f:selectItems value="#{filterManager.getProperties(typeSelector.typeSelected)}" />
<f:ajax event="change" render=":filterForm" />
</h:selectOneMenu>
</h:column>
<h:column>
<h:panelGroup id="filterValuesPanel" >
<h:outputLabel value="#{msgs.value}:" for="filterValues" rendered="#{!filter.filterEmpty}" />
<h:selectOneMenu value="#{filter.value}" id="filterValues" rendered="#{!filter.filterEmpty}" >
<f:selectItems value="#{filterManager.getPossibleAnswers(filter)}" />
<f:ajax event="change" render=":filterForm" />
</h:selectOneMenu>
</h:panelGroup>
</h:column>
<h:column>
<h:commandButton value="#{msgs.delete}" title="#{msgs.deleteFilter}">
<f:ajax event="click" listener="#{filterManager.removeFilter(filter)}" render=":filterForm" />
</h:commandButton>
</h:column>
</h:dataTable>
<h:commandButton value="#{msgs.addNewFilter}">
<f:ajax event="click" listener="#{filterManager.addNewFilter}" render=":filterForm" />
</h:commandButton>
</h:form>
I have a bean called “FilterManager” which has a ViewScoped. Important parts are shown below:
#ManagedBean
#ViewScoped
public class FilterManager implements Serializable {
private List<Filter> filters; // it has a getter
private int currentFilterId;
public void addNewFilter(AjaxBehaviorEvent event) {
this.currentFilterId++;
this.filters.add(Filter.getEmptyFilter(this.currentFilterId));
}
public void removeFilter(Filter filter) {
this.filters.remove(filter);
}
...
}
The Filter class is a normal class (not a bean) and is shown below:
public class Filter implements Serializable {
private int id;
private String filter;
private String value;
public String getFilter() {
return filter;
}
public void setFilter(String theFilter) {
if (theFilter != null && !theFilter.isEmpty())
this.filter = theFilter;
}
public String getValue() {
return value;
}
public void setValue(String theValue) {
this.value = theValue;
}
public boolean isFilterEmpty() {
return this.filter == null || this.filter.isEmpty();
}
...
}
Notice that TypeSelector is a SessionScoped bean which has a typeSelected property along with getter and setter.
The problem is: filter.filter is set correctly whereas filter.value is never set. I can't find the problem so I need your help please. Apologies for all this code but I needed to provide you with all the necessary details.
Thanks in advance!
Okay guys that was my fault. I had a bug in FilterManager.getPossibleAnswers(Filter filter) method. Basically, at the end of the method, I was setting filter.value to the first element of List unconditionally. Eg instead of writing
if (filter.getValue() == null || filter.getValue().isEmpty()) {
SelectItem first = answers.get(0);
filter.setValue((String) first.getValue());
}
I just wrote:
SelectItem first = answers.get(0);
filter.setValue((String) first.getValue());
Although filter.value was updating as normal, the value was changing back to default (first element in list) during re-rendering of dataTable component.

Prevent cache values in the browser

I have a selectOneMenu and a dataTable. When I change the selected value, dataTable will repopulate.
A one of column of the dataTable is an inputText. The problem is that the inputText field doesn't call the getter, but caches the previous value.
I want to either call the getter by force or set inputText to not be cached.
How do I get rid of this problem?
<h:body>
<f:view>
<h:form id="headForm">
<ice:selectOneMenu id="item"
value="#{outerBean.selectedItem}"
valueChangeListener="#{outerBean.itemListValueChanged}"
style="width: 158px;" required="true"
label="Supplier Id" partialSubmit="true">
<f:selectItems value="#{outerBean.itemList}" />
</ice:selectOneMenu>
<ice:dataTable id="pdet" value="#{outerBean.nestedClassList}" var="nestedObject" rendered="true">
<ice:column>
<f:facet name="header">Order Number</f:facet>
<ice:outputText value="#{nestedObject.orderNumber}" />
</ice:column>
<ice:column>
<f:facet name="header">Qty</f:facet>
<ice:inputText value="#{nestedObject.qty}" id="qty" label="'Qty' FOR 'Order Number':#{nestedObject.orderNumber} "
partialSubmit="true"
valueChangeListener="#{nestedObject.qtyChanged}"
validator="#{nestedObject.validateQty}">
</ice:inputText>
</ice:column>
</ice:dataTable>
</h:form>
</h:view>
public class OuterBean{
private List<String> itemList = new ArrayList<String>();
private String selectedItem;
private List<NestedClass> nestedClassList = new ArrayList<NestedClass>();
public OuterBean(){
//init values
}
public void itemListValueChanged(ValueChangeEvent e){
selectedItem = (String)e.getNewValue();
//reset the dataTable record list
nestedClassList = getRandomRecords();
}
//---getters-setters-----//
private class NestedClass{
private int orderNumber;
private int qty;
NestedClass(){
orderNumber = generateOrderNumber();
qty = getRandomQty();
}
//-----qtyChangedListener----//
//-----qtyValidator----------//
//-----getters-setters-------//
}
}
I don't see where you are chainging nestedObject.qty in itemListValueChanged
The component will not rerender unless it is changed .
Partial Submit in Icefaces will only update the components whose value have changed.
You can make Partial Submit false on selectonemenu or you can reinitialize the value of the inputText in itemListValueChanged.

Resources