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>
  • You'll probably need to custom code this in your VF page. – Mike Chale Nov 22 '13 at 21:39
  • you mean using js ? – raym0nd Nov 22 '13 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 '13 at 21:47
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

  • so cool .. thnx a lot for the function, just what I was looking for :-) – VarunC Sep 11 '14 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.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.