23

Is there a way to tell JSF that it should NOT render a <table> element when using <h:selectOneRadio>? I don't use tables and it makes absolutely no sense in this case.

Any help is appreciated!

BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555
m4ri0
  • 597
  • 1
  • 6
  • 10

2 Answers2

27

JSF 2.3 with group attribute

As per JSF spec issue 329 I have added a new group attribute to <h:selectOneRadio> which should make this all much less tedious. All radio button components having the same group value within a parent UIForm will be grouped with each other. Also they won't render any markup besides the radio button itself and the optional label if the select item has a non-null label. If any, the label appears directly after the radio button.

<!-- Just markup them the way you want! -->
<ul>
    <ui:repeat id="items" value="#{bean.items}" var="item">
        <li>
            <h:selectOneRadio group="foo" value="#{bean.selectedItem}">
                <f:selectItem itemValue="#{item}" />
            </h:selectOneRadio>
        </li>
    </ui:repeat>
</ul>

Following scenarios are also possible. When there are multiple components with same group, and the value attribute and/or UISelectItem child is absent, then it will reference those of the first component of the group.

<h:selectOneRadio group="foo" value="#{bean.selectedItem}">
    <f:selectItems value="#{bean.availableItems}" />
</h:selectOneRadio>
<h:selectOneRadio group="foo" />
<h:selectOneRadio group="foo" />

<h:selectOneRadio group="foo" value="#{bean.selectedItem}">
    <f:selectItem itemValue="one" />
    <f:selectItem itemValue="two" />
    <f:selectItem itemValue="three" />
</h:selectOneRadio>
<h:selectOneRadio group="foo" />
<h:selectOneRadio group="foo" />

<h:selectOneRadio group="foo" value="#{bean.selectedItem}">
    <f:selectItem itemValue="one" />
</h:selectOneRadio>
<h:selectOneRadio group="foo">
    <f:selectItem itemValue="two" />
</h:selectOneRadio>
<h:selectOneRadio group="foo">
    <f:selectItem itemValue="three" />
</h:selectOneRadio>

<h:selectOneRadio group="foo" value="#{bean.selectedItem}">
    <f:selectItem itemValue="one" />
</h:selectOneRadio>
<h:selectOneRadio group="foo" value="#{otherBean.selectedItem}">
    <f:selectItem itemValue="two" />
</h:selectOneRadio>
<h:selectOneRadio group="foo" value="#{lastBean.selectedItem}">
    <f:selectItem itemValue="three" />
</h:selectOneRadio>

It will be available in Mojarra as per 2.3.0-m07.

JSF 2.2 with passthrough elements

If you're on JSF 2.2 already, make use of its new passthrough elements/attribtues feature whereby you explicitly set the name attribute as a passthrough attribute. In order to set the submitted value in the model, you only need an additional <h:inputHidden>.

<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:jsf="http://xmlns.jcp.org/jsf"
      xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
      xmlns:a="http://xmlns.jcp.org/jsf/passthrough">

...

<!-- Just markup them the way you want! -->
<ul>
    <ui:repeat id="items" value="#{bean.items}" var="item">
        <li>
            <input type="radio" jsf:id="item" a:name="#{hiddenItem.clientId}"
                value="#{item}" a:checked="#{item eq bean.selectedItem ? 'checked' : null}" />
            <h:outputLabel for="item" value="#{item}" />
        </li>
    </ui:repeat>
</ul>

<!-- This one won't display anything. -->
<h:inputHidden id="selectedItem" binding="#{hiddenItem}" value="#{bean.selectedItem}"
    rendered="#{facesContext.currentPhaseId.ordinal ne 6}" />

In depth technical explanation can be found in this blog: Custom layout with h:selectOneRadio in JSF 2.2.

PrimeFaces (JSF 2.x)

If you happen to use PrimeFaces, then you can also use <p:selectOneRadio layout="custom"> with <p:radioButton>.

<html ... xmlns:p="http://primefaces.org/ui">

<!-- This one won't display anything. -->
<p:selectOneRadio id="foo" value="#{bean.selectedFoo}" layout="custom">
    <f:selectItems value="#{bean.availableFoos}" />
</p:selectOneRadio>

<!-- Just markup them the way you want! -->
<ul>
    <li><p:radioButton for="foo" itemIndex="0" /></li>
    <li><p:radioButton for="foo" itemIndex="1" /></li>
    <li><p:radioButton for="foo" itemIndex="2" /></li>
</ul>

You can also loop over the available items, you only need to do it during view build time:

<ul>
    <c:forEach items="#{bean.availableFoos}" varStatus="loop">
        <li><p:radioButton for="foo" itemIndex="#{loop.index}" /></li>
    </c:forEach>
</ul>

Tomahawk (JSF 1.x or 2.x)

If you're not on JSF 2.2 yet or if you don't like PrimeFaces UI, grab Tomahawk's <t:selectOneRadio> which renders the same plain HTML output as <h:selectOneRadio>, but supports a layout="spread" attribute so that you can position the items by <t:radio> the way you want.

E.g.

<html ... xmlns:t="http://myfaces.apache.org/tomahawk">

<!-- This one won't display anything. -->
<t:selectOneRadio id="foo" value="#{bean.selectedFoo}" layout="spread">
    <f:selectItems value="#{bean.availableFoos}" />
</t:selectOneRadio>

<!-- Just markup them the way you want! -->
<ul>
    <li><t:radio for="foo" index="0" /></li>
    <li><t:radio for="foo" index="1" /></li>
    <li><t:radio for="foo" index="2" /></li>
</ul>

Custom Renderer

Supply a custom Renderer. It's only a bit of work. Start at the first "See also" link shown below:

See also:

Community
  • 1
  • 1
BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555
  • In your second PrimeFaces solution (the one with the forEach), how would you add labels to the radio buttons? I can't seem to get the id of the respective `input` elements to reference in a `h:outputLabel` element. – jessepeng May 30 '16 at 10:15
  • @jessepeng: use e.g. `id="foo_#{loop.index}"`. See also a.o. http://stackoverflow.com/q/3342984 – BalusC May 30 '16 at 10:16
  • Using that approach, only the enclosing div of the radio button is referenced, like this: `
    ...
    `. Btw, I'm using PrimeFaces' plain style.
    – jessepeng May 30 '16 at 11:08
  • I downloaded this implementation:https://mvnrepository.com/artifact/com.sun.faces/jsf-impl/2.2.13 but I can not find the taglib.xml file for namespace: passthrough. Only compsoite, facelets, html_basic, jstl-core jstl-fn, mojarra_ext and ui is here. Where should the passthrough be? – Koray Tugay Jul 24 '16 at 09:12
  • @Koray: That namespace doesn't have any tags/functions in first place. – BalusC Jul 24 '16 at 09:38
  • @BausC Interesting. How does Facelets processor resolve and process this namespace? – Koray Tugay Jul 24 '16 at 11:10
  • @Koray: it's built into `TagDecorator`: https://docs.oracle.com/javaee/7/api/javax/faces/view/facelets/TagDecorator.html – BalusC Jul 24 '16 at 14:36
  • Should thisork wirh a 'dynamic' group too: https://stackoverflow.com/questions/58521146/hselectoneradio-group-problem-within-nested-uirepeat-elements Seems to cause a problem – Kukeltje Oct 23 '19 at 18:14
2

i don't know if it's reasonable enough for you but, here is a sample work-around.

xhtml code:

<ui:repeat value="#{myBean.valueList}" var="radioValue">
    <input type="radio" name="radioSelection" value="#{radioValue}" checked="#{radioValue == myBean.selectedRadioValue ? 'checked' : ''}">#{radioValue}</input>
</ui:repeat>

backing bean:

    private String selectedRadioValue;

    private List<Integer> valueList;

    public MyBean() {
        valueList = new ArrayList<Integer>();
        for (int i = 0; i < 15; i++) {
            valueList.add(i);
        }
    }

    public String getSelectedRadioValue() {
        return selectedRadioValue;
    }

    public List<Integer> getValueList() {
        return valueList;
    }

    public void actionMethod() {
        HttpServletRequest request = (HttpServletRequest)FacesContext.getCurrentInstance().getExternalContext().getRequest();
        selectedRadioValue = request.getParameter("radioSelection");
    }

since you cannot assign a value to the radio selection directly on xhtml, you need to get it from request parameters and assign it manually within your action method.

tt_emrah
  • 1,043
  • 1
  • 8
  • 19