org.omnifaces.component.model.SelectItemGroups Maven / Gradle / Ivy
/*
* Copyright OmniFaces
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*/
package org.omnifaces.component.model;
import static org.omnifaces.util.Utils.coalesce;
import static org.omnifaces.util.Utils.forEach;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;
import java.util.function.Supplier;
import javax.el.ValueExpression;
import javax.faces.component.FacesComponent;
import javax.faces.component.UIComponent;
import javax.faces.component.UISelectItem;
import javax.faces.component.UISelectItems;
import javax.faces.model.SelectItem;
import javax.faces.model.SelectItemGroup;
import org.omnifaces.el.ScopedRunner;
/**
*
* The o:selectItemGroups
is an extension of {@link UISelectItems} which allows you to iterate over a
* nested collection representing groups of select items. This is basically the {@link UIComponent} counterpart of
* javax.faces.model.SelectItemGroup
. There is no equivalent (yet) in the standard JSF API. Currently the
* only way to represent {@link SelectItemGroup} in UI is to manually create and populate them in a backing bean which
* can end up to be quite verbose.
*
*
Usage
*
* Below example assumes a List<Category>
as value wherein Category
in turn has a
* List<Product>
.
*
* <h:selectOneMenu value="#{bean.selectedProduct}" converter="omnifaces.SelectItemsConverter">
* <f:selectItem itemValue="#{null}" />
* <o:selectItemGroups value="#{bean.categories}" var="category" itemLabel="#{category.name}">
* <f:selectItems value="#{category.products}" var="product" itemLabel="#{product.name}" />
* </o:selectItemGroups>
* </h:selectOneMenu>
*
*
* @author Bauke Scholtz
* @since 3.0
*/
@FacesComponent(SelectItemGroups.COMPONENT_TYPE)
public class SelectItemGroups extends UISelectItems {
// Public constants -----------------------------------------------------------------------------------------------
/** The standard component type. */
public static final String COMPONENT_TYPE = "org.omnifaces.component.model.SelectItemGroups";
// Private constants ----------------------------------------------------------------------------------------------
private static final String ERROR_EXPRESSION_DISALLOWED =
"A value expression is disallowed on 'var' attribute of SelectItemGroups.";
// Actions --------------------------------------------------------------------------------------------------------
/**
* An override which checks if this isn't been invoked on var
attribute.
* Finally it delegates to the super method.
* @throws IllegalArgumentException When this value expression is been set on var
attribute.
*/
@Override
public void setValueExpression(String name, ValueExpression binding) {
if ("var".equals(name)) {
throw new IllegalArgumentException(ERROR_EXPRESSION_DISALLOWED);
}
super.setValueExpression(name, binding);
}
/**
* An override which wraps each UISelectItem(s) child in a {@link SelectItemGroup}.
*/
@Override
public Object getValue() {
List groups = new ArrayList<>();
createSelectItems(this, super.getValue(), SelectItemGroup::new, selectItemGroup -> {
List items = new ArrayList<>();
for (UIComponent child : getChildren()) {
if (child instanceof UISelectItems) {
createSelectItems(child, ((UISelectItems) child).getValue(), SelectItem::new, items::add);
}
else if (child instanceof UISelectItem) {
items.add(createSelectItem(child, null, SelectItem::new));
}
}
selectItemGroup.setSelectItems(items.toArray(new SelectItem[items.size()]));
groups.add(selectItemGroup);
});
return groups;
}
private void createSelectItems(UIComponent component, Object values, Supplier supplier, Consumer callback) {
Map attributes = component.getAttributes();
String var = coalesce((String) attributes.get("var"), "item");
forEach(values, value -> ScopedRunner.forEach(getFacesContext(), var, value, () -> callback.accept(createSelectItem(component, getItemValue(attributes, value), supplier))));
}
private static S createSelectItem(UIComponent component, Object value, Supplier supplier) {
Map attributes = component.getAttributes();
Object itemValue = getItemValue(attributes, value);
Object itemLabel = attributes.get("itemLabel");
Object itemLabelEscaped = coalesce(attributes.get("itemEscaped"), attributes.get("itemLabelEscaped")); // f:selectItem || f:selectItems
Object itemDisabled = attributes.get("itemDisabled");
S selectItem = supplier.get();
selectItem.setValue(itemValue);
selectItem.setLabel(String.valueOf(itemLabel != null ? itemLabel : selectItem.getValue()));
selectItem.setEscape(itemLabelEscaped == null || Boolean.parseBoolean(itemLabelEscaped.toString()));
selectItem.setDisabled(itemDisabled != null && Boolean.parseBoolean(itemDisabled.toString()));
return selectItem;
}
/**
* Returns item value attribute, taking into account any value expression which actually evaluates to null.
*/
private static Object getItemValue(Map attributes, Object defaultValue) {
Object itemValue = attributes.get("itemValue");
return itemValue != null || attributes.containsKey("itemValue") ? itemValue : defaultValue;
}
}