The <h:selectManyCheckbox> renders an HTML "input" of type "checkbox"
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 selectManyCheckbox
examples</title>
</h:head>
<h:body>
<h:form>
<h:selectManyCheckbox
value="#{playerBean.selectedPlayers}">
<f:selectItem
itemValue="Rafael Nadal" itemLabel="Rafa" />
<f:selectItem
itemValue="Roger Federer" itemLabel="Roger F" />
<f:selectItem
itemValue="Novak Djokovic" itemLabel="Nole" />
</h:selectManyCheckbox>
<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 selectManyCheckbox
examples</title>
</h:head>
<h:body>
<h:form>
<h:selectManyCheckbox
value="#{playerBean.selectedPlayers}">
<f:selectItems
value="#{playerBean.playersMap}"/>
</h:selectManyCheckbox>
<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 selectManyCheckbox
examples</title>
</h:head>
<h:body>
<h:form>
<h:selectManyCheckbox
value="#{playerBean.selectedPlayers}">
<f:selectItems
value="#{playerBean.playersArray}" var="t"
itemLabel="#{t.label}" itemValue="#{t.value}"/>
</h:selectManyCheckbox>
<h:commandButton
value="Select" action="#{playerBean.selectedAction()}"/>
</h:form>
</h:body>
</html>
The <h:selectManyCheckbox> will be rendered
in HTML as:
<table>
<tbody>
<tr>
<td>
<input
id="j_idt6:j_idt7:0" type="checkbox" value="Rafael
Nadal" name="j_idt6:j_idt7">
<label
class="" for="j_idt6:j_idt7:0"> Rafa</label>
</td>
<td>
<input
id="j_idt6:j_idt7:1" type="checkbox" value="Roger
Federer" name="j_idt6:j_idt7">
<label
class="" for="j_idt6:j_idt7:1"> Roger F</label>
</td>
<td>
<input
id="j_idt6:j_idt7:2" type="checkbox" value="Novak
Djokovic" name="j_idt6:j_idt7">
<label class=""
for="j_idt6:j_idt7:2"> Nole</label>
</td>
</tr>
</tbody>
</table>
The PlayerBean will be:
package beans;
import java.io.Serializable;
import javax.enterprise.context.SessionScoped;
import javax.inject.Named;
import java.util.LinkedHashMap;
import java.util.Map;
@Named
@SessionScoped
public class PlayerBean implements Serializable {
private String[]
selectedPlayers;
private static final
Map<String, Object> playersMap;
private static final Player[]
playersArray;
static {
playersMap = new
LinkedHashMap<>();
playersMap.put("Rafa",
"Rafael Nadal"); //label, value
playersMap.put("Roger
F", "Roger Federer");
playersMap.put("Nole",
"Novak Djokovic");
}
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");
}
public Map<String, Object>
getPlayersMap() {
return playersMap;
}
public Player[] getPlayersArray()
{
return playersArray;
}
public String[]
getSelectedPlayers() {
return selectedPlayers;
}
public void
setSelectedPlayers(String[] selectedPlayers) {
this.selectedPlayers =
selectedPlayers;
}
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 players:
#{playerBean.selectedPlayers[0]}
#{playerBean.selectedPlayers[1]}
#{playerBean.selectedPlayers[2]}
</h:body>
</html>
Data flow in image:
More
examples:
Add xmlns:pt="http://xmlns.jcp.org/jsf/passthrough" for
pass-through attributes
Styling messages with inner CSS
<h:form>
<h:selectManyCheckbox
value="#{playerBean.selectedPlayers}" 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:selectManyCheckbox>
<h:commandButton value="Select"
action="#{playerBean.selectedAction()}"/>
</h:form>
Styling messages with CSS class
<h:form>
<h:selectManyCheckbox
value="#{playerBean.selectedPlayers}" styleClass="selectManyCheckbox-css">
<f:selectItem itemValue="Rafael
Nadal" itemLabel="Rafa" />
<f:selectItem itemValue="Roger
Federer" itemLabel="Roger F" />
<f:selectItem itemValue="Novak
Djokovic" itemLabel="Nole" />
</h:selectManyCheckbox>
<h:commandButton value="Select"
action="#{playerBean.selectedAction()}"/>
</h:form>
<h:form>
<h:selectManyCheckbox
value="#{playerBean.selectedPlayers}">
<f:selectItem itemLabel="Select players ..."/>
<f:selectItems value="#{playerBean.playersArray}"
var="t" itemLabel="#{t.label}" itemValue="#{t.value}"/>
</h:selectManyCheckbox>
<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() {
selectedPlayers
= new String[]{"Rafael Nadal","Novak Djokovic"};
}
...
}
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 SelectedPlayersBean selectedPlayersBean;
...
@Inject
private SelectedPlayersBean selectedPlayersBean;
...
@PostConstruct
public
void init() {
selectedPlayers =
selectedPlayersBean.getPlayers(); // must return String[] with valid items
}
...
}
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.
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.
<h:form>
<h:selectManyCheckbox
value="#{playerBean.selectedPlayers}">
<f:selectItem
itemLabel="Select players ..."
noSelectionOption="true"/>
<f:selectItems value="#{playerBean.playersArray}"
var="t" itemLabel="#{t.label}"
itemValue="#{t.value}"/>
</h:selectManyCheckbox>
<h:commandButton value="Select"
action="#{playerBean.selectedAction()}"/>
</h:form>
Use a
built-in converter
<h:selectManyCheckbox
value="#{playerBean.selectedRanks}" converter="javax.faces.Integer">
<f:selectItems
value="#{playerBean.playersRanks}"/>
<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 selectedRanks
represents the user selections:
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 ArrayList<Integer> getSelectedRanks() {
return selectedRanks;
}
public void
setSelectedRanks(ArrayList<Integer> selectedRanks) {
this.selectedRanks = selectedRanks;
}
So, the user may select the ranks and submit them
without issues/errors. Even if no error occurred, we can notice a
"strange" behavior if we try to run the following snippet of code:
<ui:repeat
value="#{playerBean.selectedRanks}" var="i">
#{i}: #{i.getClass()}
</ui:repeat>
The output reveals that the selected ranks are
strings, not integers as we expected to see:
1: class java.lang.String
3: 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:
1: class java.lang.Integer
3: class java.lang.Integer
Read further explanations here.
Use a
custom converter
<h:form>
<h:selectManyCheckbox value="#{playerBean.selectedPlayersList}"
converter="playerConverter">
<f:selectItems
value="#{playerBean.playersList}" var="t"
itemLabel="#{t.label}" itemValue="#{t}"/>
</h:selectManyCheckbox>
<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 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 ArrayList<Player>
getSelectedPlayersList() {
return selectedPlayersList;
}
public void setSelectedPlayersList(ArrayList<Player>
selectedPlayersList) {
this.selectedPlayersList =
selectedPlayersList;
}
Remember from the above use case that the generic
type of List<>
is lost during runtime. Since the selected items are treated as strings instead
of Player
instances, the below code will cause an error because #{i.label} cannot be
evaluated:
<ui:repeat
value="#{playerBean.selectedPlayersList}" var="i">
#{i.label}: #{i.getClass()}
</ui:repeat>
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:selectManyCheckbox
value="#{playerBean.selectedPlayersList}" converter="omnifaces.SelectItemsConverter">
<f:selectItems
value="#{playerBean.playersList}" var="t"
itemLabel="#{t.label}" itemValue="#{t}"/>
</h:selectManyCheckbox>
<h:commandButton value="Select"
action="#{playerBean.selectedAction()}"/>
</h:form>
<h:form>
<h:selectManyCheckbox
value="#{playerBean.selectedPlayersList}" converter="omnifaces.SelectItemsIndexConverter">
<f:selectItems
value="#{playerBean.playersList}" var="t"
itemLabel="#{t.label}" itemValue="#{t}"/>
</h:selectManyCheckbox>
<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:selectManyCheckbox
value="#{playerBean.selectedPlayers}" 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:selectManyCheckbox>
<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:selectManyCheckbox
value="#{playerBean.selectedPlayers}" 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:selectManyCheckbox>
<h:commandButton value="Select"
action="#{playerBean.selectedAction()}"/>
</h:form>
Complete source code on GitHub.
See also Mkyong.com.
More resources on Constantin Alin, ZEEF page.
SelectManyCheckbox in JSF Extension on JSF ShowCase ZEEF page.
More resources on Constantin Alin, ZEEF page.
SelectManyCheckbox in JSF Extension on JSF ShowCase ZEEF page.
Niciun comentariu :
Trimiteți un comentariu