13

Is there a way to group options in an apex:selectList

I have in my page

      <apex:selectList styleclass="form-control rec" size="1" id="country"  >
              <apex:selectOption itemValue="" itemLabel="--Please Select--"/>
              <apex:selectOptions value="{!CountryName}" />
              <apex:actionSupport event="onchange"  reRender=""/>
      </apex:selectList>

In the controller I have

   public List<SelectOption> getCountryName(){
      List<SelectOption> options = new List<SelectOption>();

      for(Country__c con :[select Id,Name from Country__c where Region__c=:'Americas' order by Name]){
      options.add(new SelectOption(con.Name,con.Name));
      }

Is there a way I can group the selectOptions based on region like with a grouping label similar to html <optgroup label="Americas">

Also is it possible to do something like the below code in case I'm not able to group them using salesforce

 <select id="country" >
    <option value="">--Please Select--</option>
    <optgroup label="Americas">
    <apex:selectOptions value="{!CountryName}" />
     </optgroup>
  </select>
3
  • You'll probably need to custom code this in your VF page.
    – Mike Chale
    Nov 22, 2013 at 21:39
  • you mean using js ?
    – raym0nd
    Nov 22, 2013 at 21:41
  • That'd probably work, but I was thinking of custom code to generate the HTML itself, instead of the apex:selectList.
    – Mike Chale
    Nov 22, 2013 at 21:47

3 Answers 3

12

If you make a small change to your controller's getter method so that the Label value of the option element includes the region in each label, you can then use javascript to build the <optgroup /> elements for you in the page:

public List<SelectOption> getCountryName(){
    List<SelectOption> options = new List<SelectOption>();

    for(Country__c country : [SELECT Id
                                    , Name 
                                    , Region__c
                                FROM Country__c 
                                ORDER BY country.Region__c, Name 
                                LIMIT 1000]) {
        options.add(new SelectOption(country.Name, String.format('{0}_{1}', country.Region__c, country.Name})));
    }

    return options;
}

Using the jQuery library in your page you can use markup like this and achieve your result.

<apex:includeScript value="//ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js" />

<apex:selectList styleclass="form-control rec" size="1" id="country"  >
    <apex:selectOption itemValue="" itemLabel="--Please Select--"/>
    <apex:selectOptions value="{!CountryName}" />
</apex:selectList>
<script>
    // Relinquish jQuery's control of the $ variable
    jQuery.noConflict();

    (function (componentName) {
        // get a reference to the select component in the page
        var selectList = jQuery('[id="' + componentName + '"]');

        // a javascript object to collect the elements in each region
        var regions = {};

        // iterate all of the options and collect them by region
        jQuery('option', selectList).each(function (i) {
            if (i === 0) {
                return; // skip the first element in the list
            }
            var oElement = jQuery(this);
            var regionCountryArray = oElement.text().split('_');

            // create an array entry for the region name if there is not one
            if (!regions[regionCountryArray[0]]) {
                regions[regionCountryArray[0]] = [];
            }

            // add the item to the array for this region
            regions[regionCountryArray[0]].push(oElement);
        });

        // iterate all of the names in the regions object
        for (var region in regions) {

            // make sure the name did not come from the prototype
            if (regions.hasOwnProperty(region)) {

                // turn the array of items into a single jQuery collection
                var groupElements = jQuery(regions[region]).map(function () {
                    return this.toArray();
                });

                // create the group and set the label
                var optgroup = jQuery('<optgroup/>');
                optgroup.attr('label', region);

                // wrap the option elements in an optgroup
                groupElements.wrapAll(optgroup);

                // remove the region text from the label
                groupElements.each(function () {
                    jQuery(this).text(function () {
                        return jQuery(this).text().replace(region + '_', '');
                    });
                });
            }
        }
    })('{!$Component.country}'); // immediate function execution - pass in the id of the select list
</script>

Before & After (using country and state/province)


Before After

1
  • so cool .. thnx a lot for the function, just what I was looking for :-)
    – VarunC
    Sep 11, 2014 at 20:27
1

There's no native visualforce way to do this. (unfortunately).

That said, I'm a huge fan of a library called Select2. (Select2 on Github) Once you've put select2's libs in a static resource you can load it on your page like this:

    <apex:includeScript value="{!URLFOR($Resource.<<<YourResourceName>>>, 'path/to/file/in/resource/select2.min.js')}" />

or you could go the easy route, and use this pre made visualforce component:

PreMade select2 Visualforce component here!!!

Using the component, you'll be able to, out of the box, an autocompleting, auto-searching field with something like this:

<c:AutoCompleteV2 allowClear="true" importJquery="true" labelField="Name" SObject="Account" valueField="Id" targetField="{!targetField}" style="width:200px"/>  

Of note here is the targetField variable, which is the property who's Setter is called when a selection is made.

Now, in order to get the select option group's your looking for, you're gonna need to modify this component -- but considering it's open source, and 92% of the work is done, this shouldn't be to onerous a task. Check out the source of the component (linked above) and the select 2 documentation (also linked above) and remember, share your solution with the rest of us!

1

I did this through a bit of a hack but it's worked really well. The basic idea is that you use a hidden apex:selectList as a proxy to an HTML select with optgroups. Use javascript to update the apex:selectList with the value from the HTML one whenever it changes.

<select id="grouped-select">
    <optgroup label="Group 1">
        <apex:repeat var="opt" value="{!group1SelectOptions}">
            <option value="{!opt.value}">{!opt.label}</option>
        </apex:repeat>
    </optgroup>
    <optgroup label="Group 2">
        <apex:repeat var="opt" value="{!group2SelectOptions}">
            <option value="{!opt.value}">{!opt.label}</option>
        </apex:repeat>
    </optgroup>
</select>

<!-- HIDDEN PROXY SELECTLIST -->
<apex:selectList id='hiddenSelect' style="display:none;" value="{!selectedItem}" >
    <apex:selectOptions value="{!group1SelectOptions}" />
    <apex:selectOptions value="{!group2SelectOptions}" />
</apex:selectList>

<script type='text/javascript'>
    $("#grouped-select").on('change',function(){
        $('[id$=hiddenSelect]').val($(this).val());
    });
</script>

Note: the apex:selectList must have the same options as the HTML one for this to work. That's why I'm still using a List<SelectOption> for each group.

You must log in to answer this question.

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