TriStateManyCheckbox is a group of checkboxes with three states each. The value is a Map of the item values to their states: "0" (unchecked), "1" (checked) and "2" (crossed), or objects with a converter.
<p:growl showDetail="true">
<p:autoUpdate/>
</p:growl>
<h:form>
<div class="card">
<h5 class="mt-0">Basic</h5>
<p class="mt-0">A click, Space or Enter moves an item to the next state, the arrow keys move forward and back.</p>
<p:triStateManyCheckbox id="basic" value="#{triStateManyCheckboxView.basic}">
<f:selectItems value="#{triStateManyCheckboxView.toppings}"/>
<p:ajax update="basicValue"/>
</p:triStateManyCheckbox>
<p class="mb-0">Value: <h:outputText id="basicValue" value="#{triStateManyCheckboxView.basic}"/></p>
</div>
<div class="card">
<h5 class="mt-0">Vertical with Titles</h5>
<p:triStateManyCheckbox value="#{triStateManyCheckboxView.vertical}" layout="pageDirection"
stateOneTitle="Don't care" stateTwoTitle="Love it" stateThreeTitle="Hate it">
<f:selectItems value="#{triStateManyCheckboxView.toppings}"/>
</p:triStateManyCheckbox>
</div>
<div class="card">
<h5 class="mt-0">Ajax and Custom Icons</h5>
<p class="mt-0">The <i>change</i> event fires when the state of an item changes.</p>
<p:triStateManyCheckbox value="#{triStateManyCheckboxView.ajax}"
stateOneIcon="pi pi-circle" stateTwoIcon="pi pi-heart-fill" stateThreeIcon="pi pi-ban">
<f:selectItem itemLabel="Cheese" itemValue="Cheese"/>
<f:selectItem itemLabel="Olives" itemValue="Olives"/>
<p:ajax event="change" listener="#{triStateManyCheckboxView.onChange}"/>
</p:triStateManyCheckbox>
</div>
<div class="card">
<h5 class="mt-0">Converter</h5>
<p class="mt-0">The converter turns the states into objects of the model, here an enum.</p>
<p:triStateManyCheckbox value="#{triStateManyCheckboxView.converted}"
converter="#{triStateManyCheckboxView.preferenceConverter}">
<f:selectItems value="#{triStateManyCheckboxView.toppings}"/>
<p:ajax update="convertedValue"/>
</p:triStateManyCheckbox>
<p class="mb-0">Value: <h:outputText id="convertedValue" value="#{triStateManyCheckboxView.converted}"/></p>
</div>
<div class="card">
<h5 class="mt-0">Sizes</h5>
<div class="flex flex-column gap-3">
<p:triStateManyCheckbox value="#{triStateManyCheckboxView.basic}" styleClass="p-chkbox-sm">
<f:selectItems value="#{triStateManyCheckboxView.toppings}"/>
</p:triStateManyCheckbox>
<p:triStateManyCheckbox value="#{triStateManyCheckboxView.basic}" styleClass="p-chkbox-lg">
<f:selectItems value="#{triStateManyCheckboxView.toppings}"/>
</p:triStateManyCheckbox>
</div>
</div>
<div class="card">
<h5 class="mt-0">Disabled and Readonly</h5>
<div class="flex flex-column gap-3">
<p:triStateManyCheckbox value="#{triStateManyCheckboxView.basic}" disabled="true">
<f:selectItems value="#{triStateManyCheckboxView.toppings}"/>
</p:triStateManyCheckbox>
<p:triStateManyCheckbox value="#{triStateManyCheckboxView.basic}" readonly="true">
<f:selectItems value="#{triStateManyCheckboxView.toppings}"/>
</p:triStateManyCheckbox>
<p:triStateManyCheckbox value="#{triStateManyCheckboxView.basic}">
<f:selectItems value="#{triStateManyCheckboxView.toppings}" var="topping" itemDisabled="#{topping eq 'Cheese'}"/>
</p:triStateManyCheckbox>
</div>
</div>
</h:form>
<h:form>
<div class="card">
<h5 class="mt-0">Validation</h5>
<p class="mt-0">Required: at least one item must not be unchecked.</p>
<div class="flex flex-wrap align-items-start gap-3">
<div>
<p:triStateManyCheckbox id="required" value="#{triStateManyCheckboxView.required}" required="true"
label="Toppings">
<f:selectItems value="#{triStateManyCheckboxView.toppings}"/>
</p:triStateManyCheckbox>
<p:message for="required"/>
</div>
<p:commandButton value="Submit" action="#{triStateManyCheckboxView.submit}" update="@form"/>
</div>
</div>
</h:form>
package org.primefaces.showcase.view.input;
import java.io.Serializable;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import jakarta.annotation.PostConstruct;
import jakarta.faces.application.FacesMessage;
import jakarta.faces.component.UIComponent;
import jakarta.faces.context.FacesContext;
import jakarta.faces.convert.Converter;
import jakarta.faces.view.ViewScoped;
import jakarta.inject.Named;
@Named
@ViewScoped
public class TriStateManyCheckboxView implements Serializable {
public enum Preference {
NEUTRAL, LIKE, DISLIKE
}
private List<String> toppings;
private Map<String, String> basic;
private Map<String, String> vertical;
private Map<String, String> ajax;
private Map<String, Preference> converted;
private Map<String, String> required;
@PostConstruct
public void init() {
toppings = List.of("Cheese", "Mushroom", "Olives", "Onion", "Pepperoni");
basic = new LinkedHashMap<>();
basic.put("Cheese", "1");
basic.put("Onion", "2");
vertical = new LinkedHashMap<>(basic);
ajax = new LinkedHashMap<>();
converted = new LinkedHashMap<>();
converted.put("Mushroom", Preference.LIKE);
converted.put("Pepperoni", Preference.DISLIKE);
required = new LinkedHashMap<>();
}
public void onChange() {
FacesContext.getCurrentInstance().addMessage(null,
new FacesMessage(FacesMessage.SEVERITY_INFO, "State changed", String.valueOf(ajax)));
}
public void submit() {
FacesContext.getCurrentInstance().addMessage(null,
new FacesMessage(FacesMessage.SEVERITY_INFO, "Submitted", String.valueOf(required)));
}
/**
* Converts the states (0, 1, 2) to a {@link Preference} and back.
*/
public Converter<Preference> getPreferenceConverter() {
return new Converter<>() {
@Override
public Preference getAsObject(FacesContext context, UIComponent component, String value) {
return Preference.values()[Integer.parseInt(value)];
}
@Override
public String getAsString(FacesContext context, UIComponent component, Preference value) {
return String.valueOf(value.ordinal());
}
};
}
public List<String> getToppings() {
return toppings;
}
public Map<String, String> getBasic() {
return basic;
}
public void setBasic(Map<String, String> basic) {
this.basic = basic;
}
public Map<String, String> getVertical() {
return vertical;
}
public void setVertical(Map<String, String> vertical) {
this.vertical = vertical;
}
public Map<String, String> getAjax() {
return ajax;
}
public void setAjax(Map<String, String> ajax) {
this.ajax = ajax;
}
public Map<String, Preference> getConverted() {
return converted;
}
public void setConverted(Map<String, Preference> converted) {
this.converted = converted;
}
public Map<String, String> getRequired() {
return required;
}
public void setRequired(Map<String, String> required) {
this.required = required;
}
}