6

I have a Map in custom controller as follows

Map<String, SelectOption[]> mapSelectOption;

and populate this Map as follows in constructor of custom controller

Schema.DescribeSobjectResult R = Account.sObjectType.getDescribe();

//Initialize map to store each object and its fields
mapSelectOption = new Map<String, SelectOption[]>();

//Fetch all child relations for Account
List<Schema.ChildRelationship> C = R.getChildRelationships(); 

Integer j = 0;
//Iterate through all Child objects
for(Schema.ChildRelationship ch : C){

    //In case child object is a custom setting ignore it
    if(!ch.getChildSObject().getDescribe().isCustomSetting()){
        //Describe Each child object, add object name and corresponding fields to a map
        Map<String, Schema.SobjectField> fields = (ch.getChildSobject()).getDescribe().fields.getMap();

        SelectOption[] tempList = new SelectOption[fields.size()];
        Integer i = 0;
        for(Schema.SObjectField s : fields.values()){
            Schema.DescribeFieldResult fieldDescribe = s.getDescribe();
            tempList[i] = new SelectOption(fieldDescribe.getName(),fieldDescribe.getName(), false);
            i++;
        }
        if(i > 0) {
            mapSelectOption.put('key'+j,tempList);
        }
        j++;
    }
}

I am using a custom component from this repo.

This custom component has two attributes of SelectOption[] as type

<apex:attribute name="leftOption" description="Options list for left listbox." type="SelectOption[]" required="true" assignTo="{!leftOptions}" />

<apex:attribute name="rightOption" description="Options list for right listbox." type="SelectOption[]" required="true" assignTo="{!rightOptions}" />

and here is controller code from where the assignTo will call the setter method of leftOptions.

/*
 * MultiselectController synchronizes the values of the hidden elements to the
 * SelectOption lists.
 */
public with sharing class MultiselectController {

    // SelectOption lists for public consumption
    public SelectOption[] leftOptions { get; set; }
    public SelectOption[] rightOptions { get; set; }

    // Parse &-separated values and labels from value and 
    // put them in option
    private void setOptions(SelectOption[] options, String value) {
        options.clear();
        String[] parts = value.split('&');
        for (Integer i=0; i<parts.size()/2; i++) {
            options.add(new SelectOption(EncodingUtil.urlDecode(parts[i*2], 'UTF-8'), 
              EncodingUtil.urlDecode(parts[(i*2)+1], 'UTF-8')));
        }
    }

    // Backing for hidden text field containing the options from the
    // left list
    public String leftOptionsHidden { get; set {
           leftOptionsHidden = value;
           setOptions(leftOptions, value);
        }
    }

    // Backing for hidden text field containing the options from the
    // right list
    public String rightOptionsHidden { get; set {
           rightOptionsHidden = value;
           setOptions(rightOptions, value);
        }
    }
}

Now, I try to use this component in my Visualforce page as follows

<apex:repeat value="{!mapSelectOption}" var="mapKey">
    <c:MultiselectPicklist leftLabel="Available fields" leftOption="{!mapSelectOption[mapKey]}" rightLabel="Selected fields" rightOption="{!mapSelectOption[mapKey]}" size="14" width="150px"/>
</apex:repeat>

It results in following error

Cannot convert the value of '{!leftOptions}' to the expected type.

Also, I have tried following code to see if the Map is populated correctly and I see the results as per the attached image.

<apex:repeat value="{!mapSelectOption}" var="mapKey">
    <apex:outputLabel>{!mapKey}</apex:outputLabel>
    <apex:selectList size="10">
        <apex:selectOptions value="{!mapSelectOption[mapKey]}"/>
    </apex:selectList>
    <br />
</apex:repeat>

enter image description here

PS: I want to understand what is the difference between passing {!mapSelectOption[mapKey]} to the custom component and passing the same to value attribute of an apex:selectOptions tag where the later accepts it and the former results in error.

Any help is much appreciated.

3
  • I have copied your code into my own dev org and got exactly the same error. I haven't been able to figure it out yet either but just wanted to confirm that the problem you are having is perfectly valid. May 15 '14 at 22:50
  • Also I have tried changing mapSelectOption to a List<List<SelectOption>> however I still got exactly the same error message. May 15 '14 at 22:51
  • Thank you for looking into this. Even, I tried List<List<SelectOption>> when I was working on this, but it didn't work.
    – jagmohan
    May 16 '14 at 0:54
4

There certainly seems to be some weird bug going on to do with generic types (e.g. using a list or map). After a bit of experimentation I found a workaround, and that is to use a wrapper class as follows:

public class OptionsList {
    public List<SelectOption> options { get; set; }

    public OptionsList() {
        this.options = new List<SelectOption>();
    }

    public void push(SelectOption opt) {
        this.options.add(opt);
    }
}

At the top of your custom controller you can now create a list of this wrapper class:

public List<OptionsList> optionsLists { get; set; }

And your page can iterate over this as follows:

<apex:repeat value="{!listOptions}" var="ol">
        <c:MultiselectPicklist leftLabel="Available fields" leftOptions="{!ol.options}" rightLabel="Selected fields" rightOptions="{!ol.options}" size="14" width="150px"/>
</apex:repeat>

Here is a link to a gist containing your code with my adjustments: https://gist.github.com/lukemcfarlane/a90a78e3a4b1d05e34c2

1
  • Thank You. I will give it a try and accept your answer. And, the project in which I needed this has already been completed, I handled this stuff using JavaScript/HTML.
    – jagmohan
    May 19 '14 at 4:14

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.