Introduction
Whenever you want to style a HTML element using CSS, you could just use its style or, preferably, class attribute. But in the default Sun JSF Mojarra implementation there is no comparable attribute available for that. The h:selectOneMenu, h:selectManyMenu and f:selectItem tags simply doesn't support it.
When looking at comparable attributes in other elements, you'll notice that h:dataTable has an elegant approach in form of the rowClasses attribute which accepts a commaseparated string of CSS class names which are to be applied on the This can be achieved at two ways: overriding the default renderer class and using the f:attribute to add it as an external component attribute, or overriding the default renderer class, the component class and the tag class to let it support the optionClasses attribute. It might be obvious that the first way is a bit hacky, but it costs much less effort. The second way is more elegant, but it require more code and a custom tld file which should copy all existing component attributes over (tld files unfortunately doesn't know anything about inheritance). BalusC did it and the tld file was almost 500 lines long for only the selectOneMenu and selectManyMenu. Ouch. This article will handle only the first approach in detail. Here is how the extended MenuRenderer look like: Configure it as follows in the faces-config.xml: That's all! And now a basic demonstration example how to use it. The relevant part of the JSF file should look like: Note the f:attribute: this sets the optionClasses attribute value which is been picked up by the ExtendedMenuRenderer. It will apply the given CSS style classes repeatedly on the rendered option elements. You can even use EL in it so that a backing bean can generate the desired String of comma separated CSS style classes based on some conditions. The CSS styles are definied as follows: Note that some web browsers wouldn't apply this on the selected option in the h:selectOneMenu. If desired, you need to add a style class for the element then and apply it as h:selectOneMenu styleClass="className" then. And now the demo backing bean code, just as usual. Nothing special here. Just run it all and you'll see that the options are colored light gray and light red repeatedly! All the stuff is build, compiled and tested successfully with Sun JSF Mojarra 1.2_09 and Apache Tomcat 6.0.14 in Eclipse Europa IDE. The output is rendered nicely in recent versions of all commonly used browsers (FF, IE, Opera and Safari). Indeed, this won't work for the listboxes (among others h:selectOneListbox and h:selectManyListbox). But you can just follow the same approach and create a renderer which extends com.sun.faces.renderkit.html_basic.ListboxRenderer which is to be configured on the renderer-type of javax.faces.Listbox. Copyright - There is no copyright on the code. You can copy, change and distribute it freely. Just mentioning this site should be fair. (C) August 2008, BalusC elements repeatedly. Now, it would be nice to let among others the h:selectOneMenu support a similar optionClasses attribute. Back to top
ExtendedMenuRenderer
package net.balusc.jsf.renderer.html;
import java.io.IOException;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.context.ResponseWriter;
import javax.faces.convert.Converter;
import javax.faces.model.SelectItem;
import com.sun.faces.renderkit.html_basic.MenuRenderer;
/**
* Extended menu renderer which renders the 'optionClasses' attribute above the standard menu
* renderer. To use it, define it as follows in the render-kit tag of faces-config.xml.
*
*
* <renderer>
* <component-family>javax.faces.SelectOne</component-family>
* <renderer-type>javax.faces.Menu</renderer-type>
* <renderer-class>net.balusc.jsf.renderer.html.ExtendedMenuRenderer</renderer-class>
* </renderer>
* <renderer>
* <component-family>javax.faces.SelectMany</component-family>
* <renderer-type>javax.faces.Menu</renderer-type>
* <renderer-class>net.balusc.jsf.renderer.html.ExtendedMenuRenderer</renderer-class>
* </renderer>
*
*
* And define the 'optionClasses' attribute as a f:attribute of the h:selectOneMenu or
* h:selectManyMenu as follows:
*
*
* <f:attribute name="optionClasses" value="option1,option2,option3" />
*
*
* It accepts a comma separated string of CSS class names which are to be applied on the options
* repeatedly (the same way as you use rowClasses in h:dataTable). The optionClasses will be
* rendered only if there is no 'disabledClass' or 'enabledClass' being set as an attribute.
*
* @author BalusC
* @link http://balusc.blogspot.com/styling-options-in-hselectonemenu.html
*/
public class ExtendedMenuRenderer extends MenuRenderer {
// Override -----------------------------------------------------------------------------------
/**
* @see com.sun.faces.renderkit.html_basic.MenuRenderer#renderOption(
* javax.faces.context.FacesContext, javax.faces.component.UIComponent,
* javax.faces.convert.Converter, javax.faces.model.SelectItem, java.lang.Object,
* java.lang.Object[])
*/
protected void renderOption(FacesContext context, UIComponent component, Converter converter,
SelectItem currentItem, Object currentSelections, Object[] submittedValues)
throws IOException
{
// Copied from MenuRenderer#renderOption() (and a bit rewritten, but that's just me) ------
// Get writer.
ResponseWriter writer = context.getResponseWriter();
assert (writer != null);
// Write 'option' tag.
writer.writeText("\t", component, null);
writer.startElement("option", component);
// Write 'value' attribute.
String valueString = getFormattedValue(context, component, currentItem.getValue(), converter);
writer.writeAttribute("value", valueString, "value");
// Write 'selected' attribute.
Object valuesArray;
Object itemValue;
if (containsaValue(submittedValues)) {
valuesArray = submittedValues;
itemValue = valueString;
} else {
valuesArray = currentSelections;
itemValue = currentItem.getValue();
}
if (isSelected(context, itemValue, valuesArray)) {
writer.writeAttribute("selected", true, "selected");
}
// Write 'disabled' attribute.
Boolean disabledAttr = (Boolean) component.getAttributes().get("disabled");
boolean componentDisabled = disabledAttr != null && disabledAttr.booleanValue();
if (!componentDisabled && currentItem.isDisabled()) {
writer.writeAttribute("disabled", true, "disabled");
}
// Write 'class' attribute.
String labelClass;
if (componentDisabled || currentItem.isDisabled()) {
labelClass = (String) component.getAttributes().get("disabledClass");
} else {
labelClass = (String) component.getAttributes().get("enabledClass");
}
// Inserted custom code which checks the optionClasses attribute --------------------------
if (labelClass == null) {
String optionClasses = (String) component.getAttributes().get("optionClasses");
if (optionClasses != null) {
String[] labelClasses = optionClasses.split("\\s*,\\s*");
String indexKey = component.getClientId(context) + "_currentOptionIndex";
Integer index = (Integer) component.getAttributes().get(indexKey);
if (index == null || index == labelClasses.length) {
index = 0;
}
labelClass = labelClasses[index];
component.getAttributes().put(indexKey, ++index);
}
}
// The remaining copy of MenuRenderer#renderOption() --------------------------------------
if (labelClass != null) {
writer.writeAttribute("class", labelClass, "labelClass");
}
// Write option body (the option label).
if (currentItem.isEscape()) {
String label = currentItem.getLabel();
if (label == null) {
label = valueString;
}
writer.writeText(label, component, "label");
} else {
writer.write(currentItem.getLabel());
}
// Write 'option' end tag.
writer.endElement("option");
writer.writeText("\n", component, null);
}
}
Back to top
Basic demonstration example
option.option1 {
background-color: #ccc;
}
option.option2 {
background-color: #fcc;
}package mypackage;
import java.util.ArrayList;
import java.util.List;
import javax.faces.model.SelectItem;
public class MyBean {
// Properties ---------------------------------------------------------------------------------
private List
private String selectedItem;
{
fillSelectItems();
}
// Actions ------------------------------------------------------------------------------------
public void submit() {
System.out.println("Selected item: " + selectedItem);
}
// Getters ------------------------------------------------------------------------------------
public List
return selectItems;
}
public String getSelectedItem() {
return selectedItem;
}
// Setters ------------------------------------------------------------------------------------
public void setSelectedItem(String selectedItem) {
this.selectedItem = selectedItem;
}
// Helpers ------------------------------------------------------------------------------------
private void fillSelectItems() {
selectItems = new ArrayList
selectItems.add(new SelectItem("value1", "label1"));
selectItems.add(new SelectItem("value2", "label2"));
selectItems.add(new SelectItem("value3", "label3"));
selectItems.add(new SelectItem("value4", "label4"));
selectItems.add(new SelectItem("value5", "label5"));
}
}Back to top
And the listboxes then?
Back to top
Source:http://balusc.blogspot.com/2008/08/styling-options-in-hselectonemenu.html
Tidak ada komentar:
Posting Komentar