7
<apex:page controller="testRadio">
    <apex:form >
         <apex:selectRadio > 
            <apex:pageBlock >
                <apex:pageBlockTable value="{!selectOptions}" var="s">
                    <apex:column >
                        {!s.value} <apex:selectOption value="{!s}" /> 
                    </apex:column>
                </apex:pageBlockTable>
            </apex:pageBlock>
        </apex:selectRadio> 
    </apex:form>
</apex:page>

I want to display multiple rows with a radiobox in each row. End users may only select one of the rows, hence the radio box.

I cannot seem to do this without receiving the visualforce error:

"Select components should have at least one child component of type selectOption or selectOptions"

Is there a workaround to this? I am familiar with wrapper objects for check boxes.

3 Answers 3

7

The error you're seeing is occurring because a selectRadio can only have options as children; they can't wrap a table. That approach won't work because the selectRadio component isn't set up to do that.

Lacey's answer certainly is one solution. When I've needed to do this, I've opted for using a backing wrapper class that has a "selected" Boolean attribute, and then I build the table with javascript-controlled mutually exclusive VF inputCheckboxes that bind to the "selected" attribute of each row. You could do something similar with vanilla HTML radio buttons, but I prefer the checkbox route because in my experience it's less error-prone since you don't have to do any copying logic etc.

Here's an example of the general idea (untested but should show you the general idea):

<apex:pageBlockTable value="{!sObjectWrappers}" var="s">
    <apex:column>
        <apex:inputCheckbox styleClass="mutex" value="{!s.selected}" />
    </apex:column>
    <apex:column >
        {!s.label}
    </apex:column>
</apex:pageBlockTable>

So, now you have a table with a bunch of checkboxes. Use a bit of jQuery magic against that "mutex" CSS class to make the checkboxes mutually exclusive:

$("input.mutex").click(function() {
    var myCb = this;
    $("input.mutex").each(function() {
        if (this != myCb) {
            this.checked = false;
        }
    });
});

And that's it. Now when your form is submitted to your VF controller, you can tell which element was selected by looking at the "selected" attribute of your wrapper class.

1
  • Awesome. In fact I actually have a wrapper/checkbox right now, but the client asked that it be mutually exclusive. This shouldn't be too hard to pull off .. I'll report after attempting it tomorrow. Commented Aug 20, 2013 at 6:01
3

It's a bit nasty but I think your only real option here is to use HTML radio elements. You could use some javascript in the onClick event to copy the current value to an <apex:hidden> form element which is then bound do the desired variable in the controller.

I've split out checkboxes by using a map and dynamic binding in the past, but again, without some javascript you won't get the single selection functionality.

1
  • 1
    Or onClick, call an apex:actionFunction passing the selected ID as a parameter and assign it to an apex variable. Commented Aug 20, 2013 at 5:06
2

I agree with jkraybill and LaceySnr, I think the only way to have/enforce a "selectRadio" is to jimmy rig something out of HTML elements + javascript (jQuery). I recently had the same situation, and I used a wrapper class + <apex:inputCheckbox>s + jQuery to enforce only checkbox can be checked out of my selection.

Here's my example in case it helps:

It's a VF page where a user chooses an option or declines any choice (in this case it's for Employees to choose a Health Benefit for the 2013-2014 enrollment period).

Wrapper class in my controller:

public class myController{

    public class benefitChoice{
        public boolean isSelected {get;set;}
        public Benefit_Type__c benefitType {get;set;}

        public benefitChoice(){} //empty constructor
        public benefitChoice(boolean selection,Benefit_Type__c bt){
            this.isSelected = selection;
            this.benefitType = bt;
        }// a fun constructor
        public benefitChoice(Benefit_Type__c bt,boolean selection){
            this.isSelected = selection;
            this.benefitType = bt;
        }// another fun constructor 
    }
    public list<benefitChoice> AvailableBenefits {get;set;}

    public boolean declineBenefit {get;set;}

    // create available benefits + whatever else you need

}//END class

VF Page sample: Note: I displayed the AvailableBenefits in <div>s rather than a table so jQuery's toggle.('slide') animates smoothly

<div class="ben-avail" >
    <div class="ben-avail-tr">
        <div class="ben-avail-td ben-avail-th ben-avail-td-1"></div>
        <div class="ben-avail-td ben-avail-th ben-avail-td-2">
            Benefit Description
        </div>
        <div class="ben-avail-td ben-avail-th ben-avail-td-3">
            Converage
        </div>
        <div class="ben-avail-td ben-avail-th ben-avail-td-4">
            PR Deduction
        </div>
        <div class="ben-avail-td ben-avail-th ben-avail-td-5">
            Has HSA
        </div>
        <div class="clear"></div>
    </div>    
    <apex:repeat value="{!AvailableBenefits}" var="b">
        <div class="ben-avail-tr">
            <div class="ben-avail-td ben-avail-td-1">
                <apex:inputCheckbox value="{!b.choice}" styleClass="benefitSelection accept"/>
            </div>
            <div class="ben-avail-td ben-avail-td-2">
                <apex:outputField value="{!b.benType.Name}" styleClass="ben-desc"/>
            </div>
            <div class="ben-avail-td ben-avail-td-3">
                <apex:outputField value="{!b.benType.Coverage__c}" styleClass="ben-coverage"/>
            </div>
            <div class="ben-avail-td ben-avail-td-4">
                <apex:outputField value="{!b.benType.EE_PR_Deduction__c}" styleClass="ben-deduction"/>
            </div>
            <div class="ben-avail-td ben-avail-td-5">
                <apex:outputField value="{!b.benType.Include_HSA__c}"/>
            </div>
            <div class="clear"></div>
        </div>    
    </apex:repeat>
</div>   
<br/>                        
<!-- Have a different div to display "Decline All Benefits" option [so it can have a red background-color :) ]-->
<div class="ben-choose-no">
    <span>
        <apex:inputCheckbox value="{!chooseNo}" styleClass="benefitSelection decline" />&nbsp;&nbsp;&nbsp;&nbsp;I decline 2013-2014 Health Benefits.           
    </span>
</div>

With jQuery:

<apex:includeScript value="{!URLFOR($Resource.jQuery,'jQuery/jquery-1.10.1.min.js')}"/>  <!-- I keep my jQuery scripts in a static resource -->
<apex:includeScript value="{!URLFOR($Resource.jQuery,'jQuery/jquery-ui.min.js')}"/>  <!-- jquery UI -->

<script>
    var j$=jQuery.noConflict();

    j$(document).ready(function(){

        j$('.benefitSelection').click(function(){
            //alert('You selected '+j$(this).val());
            var justSelected=j$(this);                //store the which element was selected
            j$('.benefitSelection').prop('checked',false);  //uncheck all elements
            justSelected.prop('checked',true);   //reselect the selected element
        });
    });
</script>                 

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .