Breaking up radio buttons into columns in JSF

前端 未结 3 2008
执念已碎
执念已碎 2020-12-15 01:43

When I am using h:selectOneRadio and supplying the list of values in a list as the entire radio button section is exposed as a single unbroken list. I need to arrange i

相关标签:
3条回答
  • 2020-12-15 02:28

    The h:panelGrid contains only one child (a h:selectOneRadio), so it will only ever render one column. The h:selectOneRadio renders a HTML table too. Its renderer only offers two layouts (lineDirection and pageDirection).

    You have a few options

    • use JavaScript to modify the table after page load
    • find a 3rd party control that implements the functionality you want
    • write your own selectOneRadio control
    0 讨论(0)
  • 2020-12-15 02:38

    I've adapted the code given by Damo, to work with h:selectOneRadio instead of h:selectManycheckbox. To get it working you will need to register it in your faces-config.xml, with:

    <render-kit>
        <renderer>
            <component-family>javax.faces.SelectOne</component-family>
            <renderer-type>javax.faces.Radio</renderer-type>
            <renderer-class>test.components.SelectOneRadiobuttonListRenderer</renderer-class>
        </renderer>
    </render-kit>
    

    To compile it, you will also need the JSF implementation (typically found in some sort of jsf-impl.jar in you app server).

    The code outputs the radiobuttons in divs, instead of a table. You can then use CSS to style them however you would like. I would suggest giving a fixed width to the checkboxDiv and inner divs, and then having the inner divs display as inline blocks:

    div.radioButtonDiv{
        width: 300px;
    }
    
    div.radioButtonDiv div{
        display: inline-block;
        width: 100px;
    }
    

    Which should give the 3 columns you are looking for

    The code:

    package test.components;
    
    import java.io.IOException;
    import java.lang.reflect.Array;
    import java.util.Collection;
    import java.util.Iterator;
    
    import javax.faces.component.NamingContainer;
    import javax.faces.component.UIComponent;
    import javax.faces.component.UISelectMany;
    import javax.faces.component.UISelectOne;
    import javax.faces.context.FacesContext;
    import javax.faces.context.ResponseWriter;
    import javax.faces.model.SelectItem;
    
    import com.sun.faces.renderkit.RenderKitUtils;
    import com.sun.faces.renderkit.html_basic.MenuRenderer;
    import com.sun.faces.util.MessageUtils;
    import com.sun.faces.util.Util;
    
    /**
     * This component ensures that h:selectOneRadio doesn't get rendered using
     * tables.  It is adapted from the code at:
     * http://www.blog.locuslive.com/?p=15
     * 
     * To register it for use, place the following in your faces config:
     * 
     * <render-kit>
     *      <renderer>
     *         <component-family>javax.faces.SelectOne</component-family>
     *         <renderer-type>javax.faces.Radio</renderer-type>
     *         <renderer-class>test.components.SelectOneRadiobuttonListRenderer</renderer-class>
     *      </renderer>
     * </render-kit>
     *  
     * The original comment is below:
     * 
     * ----------------------------------------------------------------------------- *  
     * This is a custom renderer for the h:selectManycheckbox
     * It is intended to bypass the incredibly sucky table based layout used
     * by the standard component.
     *
     * This layout uses an enclosing div with divs for each input.
     * This gives a default layout similar to a vertical layout
     * The layout can then be controlled by css
     *
     * This renderer assigns an class of "checkboxDiv" to the enclosing div
     * The class and styleClass attributes are then applied to the internal
     * divs that house the inputs
     *
     * The following attributes are ignored as they are no longer required when using CSS:
     * - pageDirection
     * - border
     *
     * Note that I am not supporting optionGroups at this stage. They would be relatively
     * easy to implement with another enclosing div
     *
     * @author damianharvey
     *
     */
    public class SelectOneRadiobuttonListRenderer extends MenuRenderer {
    
        public void encodeEnd(FacesContext context, UIComponent component)
                throws IOException {
    
            if (context == null) {
                throw new NullPointerException(
                        MessageUtils.getExceptionMessageString(MessageUtils.NULL_PARAMETERS_ERROR_MESSAGE_ID,
                                                                            "context"));
            }
            if (component == null) {
                throw new NullPointerException(
                        MessageUtils.getExceptionMessageString(MessageUtils.NULL_PARAMETERS_ERROR_MESSAGE_ID,
                                                                            "component"));
            }
    
            // suppress rendering if "rendered" property on the component is
            // false.
            if (!component.isRendered()) {
                return;
            }
    
            ResponseWriter writer = context.getResponseWriter();
            assert(writer != null);
    
            writer.startElement("div", component);
            if (shouldWriteIdAttribute(component)) {
                writeIdAttributeIfNecessary(context, writer, component);
            }
            writer.writeAttribute("class", "radioButtonDiv", "class");
    
            Iterator items = RenderKitUtils.getSelectItems(context, component).iterator();
            SelectItem curItem = null;
            int idx = -1;
            while (items.hasNext()) {
                curItem = (SelectItem) items.next();
                idx++;
                renderOption(context, component, curItem, idx);
            }
    
            writer.endElement("div");
    
        }
    
        protected void renderOption(FacesContext context, UIComponent component, SelectItem curItem, int itemNumber)
                throws IOException {
    
            ResponseWriter writer = context.getResponseWriter();
            assert(writer != null);
    
            // disable the check box if the attribute is set.
            String labelClass = null;
            boolean componentDisabled = Util.componentIsDisabled(component);
    
            if (componentDisabled || curItem.isDisabled()) {
                labelClass = (String) component.
                        getAttributes().get("disabledClass");
            } else {
                labelClass = (String) component.
                        getAttributes().get("enabledClass");
            }
    
            writer.startElement("div", component);  //Added by DAMIAN
    
            String styleClass = (String) component.getAttributes().get("styleClass");
            String style = (String) component.getAttributes().get("style");
    
            if (styleClass != null) {
                writer.writeAttribute("class", styleClass, "class");
            }
            if (style != null) {
                writer.writeAttribute("style", style, "style");
            }
    
            writer.startElement("input", component);
            writer.writeAttribute("name", component.getClientId(context), "clientId");
            String idString = component.getClientId(context) + NamingContainer.SEPARATOR_CHAR + Integer.toString(itemNumber);
            writer.writeAttribute("id", idString, "id");
            String valueString = getFormattedValue(context, component, curItem.getValue());
            writer.writeAttribute("value", valueString, "value");
            writer.writeAttribute("type", "radio", null);
    
            Object submittedValues[] = getSubmittedSelectedValues(context, component);
            boolean isSelected;
    
            Class type = String.class;
            Object valuesArray = null;
            Object itemValue = null;
            if (submittedValues != null) {
                valuesArray = submittedValues;
                itemValue = valueString;
            } else {
                valuesArray = getCurrentSelectedValues(context, component);
                itemValue = curItem.getValue();
            }
            if (valuesArray != null) {
                type = valuesArray.getClass().getComponentType();
            }
    
            // I don't know what this does, but it doens't compile.  Commenting it
            // out doesn't seem to hurt
            // Map<String, Object> requestMap = context.getExternalContext().getRequestMap();
            // requestMap.put(ConverterPropertyEditorBase.TARGET_COMPONENT_ATTRIBUTE_NAME,
            //      component);
    
            Object newValue = context.getApplication().getExpressionFactory().
                    coerceToType(itemValue, type);
    
            isSelected = isSelected(newValue, valuesArray);
    
            if (isSelected) {
                writer.writeAttribute(getSelectedTextString(), Boolean.TRUE, null);
            }
    
            // Don't render the disabled attribute twice if the 'parent'
            // component is already marked disabled.
            if (!Util.componentIsDisabled(component)) {
                if (curItem.isDisabled()) {
                        writer.writeAttribute("disabled", true, "disabled");
                }
            }
    
            // Apply HTML 4.x attributes specified on UISelectMany component to all
            // items in the list except styleClass and style which are rendered as
            // attributes of outer most table.
            RenderKitUtils.renderPassThruAttributes(writer, component, new String[] { "border", "style" });
            RenderKitUtils.renderXHTMLStyleBooleanAttributes(writer, component);
    
            writer.endElement("input");
            writer.startElement("label", component);
            writer.writeAttribute("for", idString, "for");
            // if enabledClass or disabledClass attributes are specified, apply
            // it on the label.
            if (labelClass != null) {
                writer.writeAttribute("class", labelClass, "labelClass");
            }
            String itemLabel = curItem.getLabel();
            if (itemLabel != null) {
                writer.writeText(" ", component, null);
                if (!curItem.isEscape()) {
                    // It seems the ResponseWriter API should
                    // have a writeText() with a boolean property
                    // to determine if it content written should
                    // be escaped or not.
                    writer.write(itemLabel);
                }
                else {
                    writer.writeText(itemLabel, component, "label");
                }
            }
            writer.endElement("label");
    
            writer.endElement("div");   //Added by Damian
        }
    
        // ------------------------------------------------- Package Private Methods
    
    
        String getSelectedTextString() {
    
            return "checked";
    
        }
    
        /** For some odd reason this is a private method in the MenuRenderer superclass
         *
         * @param context
         * @param component
         * @return
         */
        private Object getCurrentSelectedValues(FacesContext context,
                UIComponent component) {
    
            if (component instanceof UISelectMany) {
                UISelectMany select = (UISelectMany) component;
                Object value = select.getValue();
    
                if (value instanceof Collection) {
    
                    Collection<?> list = (Collection) value;
                    int size = list.size();
                    if (size > 0) {
                        // get the type of the first element - Should
                        // we assume that all elements of the List are
                        // the same type?
                        return list.toArray((Object[]) Array.newInstance(list.iterator().next().getClass(), size));
                    }
                    else {
                        return ((Collection) value).toArray();
                    }
    
                }
                else if (value != null && !value.getClass().isArray()) {
                    logger.warning("The UISelectMany value should be an array or a collection type, the actual type is " + value.getClass().getName());
                }
    
                return value;
            }
    
            UISelectOne select = (UISelectOne) component;
            Object returnObject;
            if (null != (returnObject = select.getValue())) {
                Object ret = Array.newInstance(returnObject.getClass(), 1);
                Array.set(ret, 0, returnObject);
                return ret;
            }
            return null;
    
        }
    
        /** For some odd reason this is a private method in the MenuRenderer superclass
         *
         * @param context
         * @param component
         * @return
         */
        private Object[] getSubmittedSelectedValues(FacesContext context, UIComponent component) {
    
            if (component instanceof UISelectMany) {
                UISelectMany select = (UISelectMany) component;
                return (Object[]) select.getSubmittedValue();
            }
    
            UISelectOne select = (UISelectOne) component;
            Object returnObject;
            if (null != (returnObject = select.getSubmittedValue())) {
                return new Object[] { returnObject };
            }
            return null;
    
        }
    
        /** For some odd reason this is a private method in the MenuRenderer superclass
         *
         * @param itemValue
         * @param valueArray
         * @return
         */
        private boolean isSelected(Object itemValue, Object valueArray) {
    
            if (null != valueArray) {
                if (!valueArray.getClass().isArray()) {
                    logger.warning("valueArray is not an array, the actual type is " + valueArray.getClass());
                    return valueArray.equals(itemValue);
                }
                int len = Array.getLength(valueArray);
                for (int i = 0; i < len; i++) {
                    Object value = Array.get(valueArray, i);
                    if (value == null) {
                        if (itemValue == null) {
                            return true;
                        }
                    }
                    else if (value.equals(itemValue)) {
                        return true;
                    }
                }
            }
            return false;
    
        }
    
    
    }
    
    0 讨论(0)
  • 2020-12-15 02:39

    Tomahawk does the magic! Check it out!

    http://wiki.apache.org/myfaces/Display_Radio_Buttons_In_Columns

    0 讨论(0)
提交回复
热议问题