My JSF Books/Videos My JSF Tutorials OmniFaces/JSF PPTs
JSF 2.3 Tutorial
JSF Caching Tutorial
JSF Navigation Tutorial
JSF Scopes Tutorial
JSF Page Author Beginner's Guide
OmniFaces 2.3 Tutorial Examples
OmniFaces 2.2 Tutorial Examples
JSF Events Tutorial
OmniFaces Callbacks Usages
JSF State Tutorial
JSF and Design Patterns
JSF 2.3 New Features (2.3-m04)
Introduction to OmniFaces
25+ Reasons to use OmniFaces in JSF
OmniFaces Validators
OmniFaces Converters
JSF Design Patterns
Mastering OmniFaces
Reusable and less-verbose JSF code

My JSF Resources ...

Java EE Guardian
Member of JCG Program
Member MVB DZone
Blog curated on ZEEF
OmniFaces is an utility library for JSF, including PrimeFaces, RichFaces, ICEfaces ...

.

.

.

.

.

.

.

.


[OmniFaces Utilities] - Find the right JSF OmniFaces 2 utilities methods/functions

Search on blog

Petition by Java EE Guardians

Twitter

luni, 19 octombrie 2015

[JSF Page Author Beginner's Guide] JSF <selectOneListbox> / HTML5 <select> [one]

The <h:selectOneListbox> renders an HTML "select one" element
Common/basic usage in JSF (I) - using hard-coded <f:selectItem>:

<?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">
 <h:head>
  <title>JSF selectOneListbox examples</title>        
 </h:head>
 <h:body>
  <h:form>          
   <h:selectOneListbox value="#{playerBean.selectedPlayer}">
    <f:selectItem itemValue="Rafael Nadal" itemLabel="Rafa" />
    <f:selectItem itemValue="Roger Federer" itemLabel="Roger F" />
    <f:selectItem itemValue="Novak Djokovic" itemLabel="Nole" />
   </h:selectOneListbox>
   <h:commandButton value="Select" action="#{playerBean.selectedAction()}"/>
  </h:form>
 </h:body>
</html>

Common/basic usage in JSF (II) - "populate" <f:selectItems> from a Map

<?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">
 <h:head>
  <title>JSF selectOneListbox examples</title>        
 </h:head>
 <h:body>
  <h:form>          
   <h:selectOneListbox value="#{playerBean.selectedPlayer}">
    <f:selectItems value="#{playerBean.playersMap}"/>
   </h:selectOneListbox>
   <h:commandButton value="Select" action="#{playerBean.selectedAction()}"/>
  </h:form>
 </h:body>
</html>

Common/basic usage in JSF (III) - "populate" <f:selectItems> from an array of Object

<?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">
 <h:head>
  <title>JSF selectOneListbox examples</title>        
 </h:head>
 <h:body>
  <h:form>          
   <h:selectOneListbox value="#{playerBean.selectedPlayer}">
    <f:selectItems value="#{playerBean.playersArray}" var="t" itemLabel="#{t.label}" itemValue="#{t.value}"/>
   </h:selectOneListbox>
   <h:commandButton value="Select" action="#{playerBean.selectedAction()}"/>
  </h:form>
 </h:body>
</html>

The <h:selectOneListbox> will be rendered in HTML as:
<select size="3" name="j_idt6:j_idt7">  
 <option value="Rafael Nadal">Rafa</option>
 <option value="Roger Federer">Roger F</option>
 <option value="Novak Djokovic">Nole</option>
</select>

The PlayerBean will be:

package beans;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.Map;
import javax.enterprise.context.SessionScoped;
import javax.inject.Named;

@Named
@SessionScoped
public class PlayerBean implements Serializable {

    private static final long serialVersionUID = 1L;

    private String selectedPlayer;
    private static final Player[] playersArray;
    private static final Map<String, Object> playersMap;

    static {
        playersArray = new Player[3];
        playersArray[0] = new Player("Rafa", "Rafael Nadal");
        playersArray[1] = new Player("Roger F", "Roger Federer");
        playersArray[2] = new Player("Nole", "Novak Djokovic");
    }

    static {
        playersMap = new LinkedHashMap<>();
        playersMap.put("Rafa", "Rafael Nadal"); //label, value
        playersMap.put("Roger F", "Roger Federer");
        playersMap.put("Nole", "Novak Djokovic");
    }

    public Map<String, Object> getPlayersMap() {
        return playersMap;
    }

    public Player[] getPlayersArray() {
        return playersArray;
    }

    public String getSelectedPlayer() {
        return selectedPlayer;
    }

    public void setSelectedPlayer(String selectedPlayer) {
        this.selectedPlayer = selectedPlayer;
    }

    public String selectedAction() {
        return "data";
    }
}

The Player simply encapsulate an item label and value (used in case III from above):

package beans;

import java.io.Serializable;
import java.util.Objects;

public class Player implements Serializable {
   
 private String label;
 private String value;

 public Player(String label, String value) {
  this.label = label;
  this.value = 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;
 }

 @Override
 public int hashCode() {
  int hash = 5;
  hash = 29 * hash + Objects.hashCode(this.label);
  hash = 29 * hash + Objects.hashCode(this.value);
  return hash;
 }

 @Override
 public boolean equals(Object obj) {
  if (obj == null) {
      return false;
  }
  if (getClass() != obj.getClass()) {
      return false;
  }
  final Player other = (Player) obj;
  if (!Objects.equals(this.label, other.label)) {
      return false;
  }
  if (!Objects.equals(this.value, other.value)) {
      return false;
  }
  return true;
 }       

 @Override
 public String toString() {
  return label + "/" + value;
 }       
}

The data.xhtml page is:

<?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">
 <h:head>
  <title></title>        
 </h:head>
 <h:body>
  <!-- View submitted data -->           
  Selected player: <br/>
  #{playerBean.selectedPlayer}<br/>
 </h:body>
</html>

Data flow in image:
More examples:

Add xmlns:pt="http://xmlns.jcp.org/jsf/passthrough" for pass-through attributes

Disabled item using 'itemDisabled' attribute

<h:form>          
 <h:selectOneListbox value="#{playerBean.selectedPlayer}">
  <f:selectItem itemValue="Rafael Nadal" itemLabel="Rafa" />
  <f:selectItem itemValue="Roger Federer" itemLabel="Roger F" itemDisabled="true" />
  <f:selectItem itemValue="Novak Djokovic" itemLabel="Nole" />
 </h:selectOneListbox>
 <h:commandButton value="Select" action="#{playerBean.selectedAction()}"/>
</h:form>

 Styling messages with inner CSS

<h:form>          
 <h:selectOneListbox value="#{playerBean.selectedPlayer}" style="color:#00f;">
  <f:selectItem itemValue="Rafael Nadal" itemLabel="Rafa" />
  <f:selectItem itemValue="Roger Federer" itemLabel="Roger F" />
  <f:selectItem itemValue="Novak Djokovic" itemLabel="Nole" />
 </h:selectOneListbox>
 <h:commandButton value="Select" action="#{playerBean.selectedAction()}"/>
</h:form>

Styling messages with CSS class

<h:form>          
 <h:selectOneListbox value="#{playerBean.selectedPlayer}" styleClass="selectOneListbox-css">
  <f:selectItem itemValue="Rafael Nadal" itemLabel="Rafa" />
  <f:selectItem itemValue="Roger Federer" itemLabel="Roger F" />
  <f:selectItem itemValue="Novak Djokovic" itemLabel="Nole" />
 </h:selectOneListbox>
 <h:commandButton value="Select" action="#{playerBean.selectedAction()}"/>
</h:form>

Add a no selection option

<h:form>          
 <h:selectOneListbox value="#{playerBean.selectedPlayer}">
  <f:selectItem itemLabel="Select a player ..." noSelectionOption="true"/>
  <f:selectItems value="#{playerBean.playersArray}" var="t" itemLabel="#{t.label}" itemValue="#{t.value}"/>
 </h:selectOneListbox>
 <h:commandButton value="Select" action="#{playerBean.selectedAction()}"/>
</h:form>

Preselect an item via the bean's (post) constructor

<h:form>          
 <h:selectOneListbox value="#{playerBean.selectedPlayer}">
  <f:selectItems value="#{playerBean.playersArray}" var="t" itemLabel="#{t.label}" itemValue="#{t.value}"/>
 </h:selectOneListbox>
 <h:commandButton value="Select" action="#{playerBean.selectedAction()}"/>
</h:form>

And place initialization in the bean constructor:

@Named
@SessionScoped
public class PlayerBean implements Serializable {
 ...
 public PlayerBean() {
  selectedPlayer = "Roger Federer";
 }
 ...
}

Or in post constructor (this is useful if you need to perform initialization based on some injected artifacts):

@Named
@SessionScoped
public class PlayerBean implements Serializable {

 @Inject
 private SelectedPlayerBean selectedPlayerBean;
 ...
 public void init() {
  selectedPlayer = selectedPlayerBean.getPlayer();
 }
...
}

Keep in mind that constructor (and post constructor) are invoked at each request in case of request scoped beans, so initialization will take place at each request ! This kind of initialization is commonly useful in view/session scoped beans where constructor (and post constructor) are invoked per view/session.

Use a built-in converter

<h:form>          
 <h:selectOneListbox value="#{playerBean.selectedRank}" converter="javax.faces.Integer">               
  <f:selectItems value="#{playerBean.playersRanks}"/>
 </h:selectOneListbox>
 <h:commandButton value="Select" action="#{playerBean.selectedAction()}"/>
</h:form> 

The story behind this example is pretty simple and we start by supposing that the above converter is not specified. Basically, the playersRanks is a list of integers that "populates" our list, and the selectedRank represents the user selections:
private Integer selectedRank;
private static final ArrayList<Integer> playersRanks;

static {
 playersRanks = new ArrayList<>();
 playersRanks.add(1);
 playersRanks.add(2);
 playersRanks.add(3);
}

public ArrayList<Integer> getPlayersRanks() {
 return playersRanks;
}

public Integer getSelectedRank() {
 return selectedRank;
}

public void setSelectedRanks(Integer selectedRank) {
 this.selectedRank = selectedRank;
}

So, the user may select the rank and submit it without issues/errors. Even if no error occurred, we can notice a "strange" behavior if we try to run the following snippet of code:

#{playerBean.selectedRank.getClass()}

The output reveals that the selected rank is a string, not integer as we expected to see:

class java.lang.String

The explanation relies on the fact that "the generic type information of List<Integer> is lost during runtime and therefore JSF/EL who sees only List is not able to identify that the generic type is Integer and assumes it to be default String (as that's the default type of the underlying HttpServletRequest#getParameter() call during apply request values phase) - BalusC".

There are two approaches:
·         explicitly specify a Converter
·         use Integer[] instead

In this case, we can use the built-in javax.faces.Integer built-in converter. Now, we can perform the same test and the output will be:

class java.lang.Integer

Read further explanations here.

Use a custom converter

<h:form>          
 <h:selectOneListbox value="#{playerBean.selectedPlayerObj}" converter="playerConverter">               
  <f:selectItems value="#{playerBean.playersList}" var="t" itemLabel="#{t.label}" itemValue="#{t}"/>
 </h:selectOneListbox>
 <h:commandButton value="Select" action="#{playerBean.selectedAction()}"/>
</h:form>

This use case continue the story from the above use case. Basically, this time we use data for which we don't have a built-in converter available. The custom converter used here is needed because this time the list is "populated" with several Player instances:
private Player selectedPlayerObj;
private static final ArrayList<Player> playersList;

static {
 playersList = new ArrayList<>();
 playersList.add(new Player("Rafa", "Rafael Nadal"));
 playersList.add(new Player("Roger F", "Roger Federer"));
 playersList.add(new Player("Nole", "Novak Djokovic"));
}

public ArrayList<Player> getPlayersList() {
 return playersList;
}

public Player getSelectedPlayerObj() {
 return selectedPlayersList;
}

public void setSelectedPlayerObj(Player selectedPlayerObj) {
 this.selectedPlayerObj = selectedPlayerObj;
}

Remember from the above use case that the generic type of List<> is lost during runtime. Since the selected item is treated as string instead of Player instance, the below code will cause an error because #{playerBean.selectedPlayerObj.label} cannot be evaluated:

#{playerBean.selectedPlayerObj.label} #{playerBean.selectedPlayerObj.value}

Since there is no built-in converter for converting strings to Player instances, we need a custom converter as below:

package beans;

import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.convert.Converter;
import javax.faces.convert.FacesConverter;

@FacesConverter("playerConverter")
public class PlayerConverter implements Converter {

 @Override
 public Object getAsObject(FacesContext context, UIComponent component,String value) {
  String[] parts = value.split("/");
  return new Player(parts[0],parts[1]);
 }

 @Override
 public String getAsString(FacesContext context, UIComponent component,Object value) {
  return value.toString();
 }   
}

Now, everything works as expected!

Use the omnifaces.SelectItemsConverter/omnifaces.SelectItemsIndexConverter converters
<h:form>          
 <h:selectOneListbox value="#{playerBean.selectedPlayerObj}" converter="omnifaces.SelectItemsConverter">               
  <f:selectItems value="#{playerBean.playersList}" var="t" itemLabel="#{t.label}" itemValue="#{t}"/>
 </h:selectOneListbox>
 <h:commandButton value="Select" action="#{playerBean.selectedAction()}"/>
</h:form>

<h:form>          
 <h:selectOneListbox value="#{playerBean.selectedPlayerObj}" converter="omnifaces.SelectItemsIndexConverter">               
  <f:selectItems value="#{playerBean.playersList}" var="t" itemLabel="#{t.label}" itemValue="#{t}"/>
 </h:selectOneListbox>
 <h:commandButton value="Select" action="#{playerBean.selectedAction()}"/>
</h:form>

If you read the above two use cases (recommended) then is pretty easy to intuit that you will to write a custom converter for each type of data that "populates" a list. Well, a generic solution consist in using the OmniFaces, SelectItemsConverter or SelectItemsIndexConverter. The OmniFaces Showcase provides all the information needed to understand how to use them, so go ahead.

Use HTML 5 'required' attribute
(specifies that the user is required to select a value before submitting the form)

<h:form>          
 <h:selectOneListbox value="#{playerBean.selectedPlayer}" pt:required="true" required="true">
  <f:selectItem itemValue="Rafael Nadal" itemLabel="Rafa" />
  <f:selectItem itemValue="Roger Federer" itemLabel="Roger F" />
  <f:selectItem itemValue="Novak Djokovic" itemLabel="Nole" />
 </h:selectOneListbox>
 <h:commandButton value="Select" action="#{playerBean.selectedAction()}"/>
</h:form>
Notice that this is a client side validation, so reinforce it with a JSF server-side validation (e.g. required built-in validator).

Use HTML 5 'autofocus' attribute
(specifies that the drop-down list should automatically get focus when the page loads) 

<h:form>          
 <h:selectOneListbox value="#{playerBean.selectedPlayer}" pt:autofocus="true">
  <f:selectItem itemValue="Rafael Nadal" itemLabel="Rafa" />
  <f:selectItem itemValue="Roger Federer" itemLabel="Roger F" />
  <f:selectItem itemValue="Novak Djokovic" itemLabel="Nole" />
 </h:selectOneListbox>
 <h:commandButton value="Select" action="#{playerBean.selectedAction()}"/>
</h:form>

Use 'valueChangeListener' attribute with <h:selectOneListbox>

<h:form>
  <h:selectOneListbox value="#{playerBean.selectedPlayerObj}" onchange="this.form.submit();" 
                       converter="playerConverter" valueChangeListener="#{playerBean.selectedVCLAction}">               
  <f:selectItems value="#{playerBean.playersList}" var="t" itemLabel="#{t.label}" itemValue="#{t}"/>                 
 </h:selectOneListbox>
</h:form>

The selectedVCLAction() is listed below:

private static final Logger LOG = Logger.getLogger(PlayerBean.class.getName());

public void selectedVCLAction(ValueChangeEvent event) {
 LOG.log(Level.INFO, "selectedVCLAction() called, {0}", ((Player)event.getNewValue()));
}

AJAXify <h:selectOneListbox> with 'valueChangeListener' attribute

<h:form>
 <h:selectOneListbox value="#{playerBean.selectedPlayerObj}" 
                      converter="playerConverter" valueChangeListener="#{playerBean.selectedVCLAction}">               
  <f:selectItems value="#{playerBean.playersList}" var="t" itemLabel="#{t.label}" itemValue="#{t}"/> 
  <f:ajax execute="@this" render="@form"/>
 </h:selectOneListbox>
</h:form>

The selectedVCLAction() is listed below:

private static final Logger LOG = Logger.getLogger(PlayerBean.class.getName());

public void selectedVCLAction(ValueChangeEvent event) {
 LOG.log(Level.INFO, "selectedVCLAction() called, {0}", ((Player)event.getNewValue()));
}

Complete source code on GitHub.
See also Mkyong.com.
More resources on Constantin Alin, ZEEF page.
SelectOneListbox in JSF Extension on JSF ShowCase ZEEF page.

Niciun comentariu :

Trimiteți un comentariu

JSF BOOKS COLLECTION

Postări populare

OmniFaces/JSF Fans

Visitors Starting 4 September 2015

Locations of Site Visitors