Filtering picklists by RecordType in Visualforce

When you add a record type to an object, you can sub-set the picklist entries per record type. The default UI respects this configuration but Visualforce will not until this idea visualforce please respect profile/record type for picklists is delivered; it is presently marked “Coming in the Next Release”; it is now marked “Delivered in Winter ’11” so the work-around below should no longer be needed.

Here is an example of the sort of work-around that you have to resort to at present as a stop-gap.

First, in the Visualforce itself, instead of:

<apex:inputField value="{!adjustment.Type__c}" required="true"/>

you need this (made messier by the panels that achieve the “required” styling):

<apex:pageBlockSectionItem >
    <apex:outputLabel value="{!$ObjectType.Adjustment__c.fields.Type__c.label}"/>
    <apex:outputPanel layout="block" styleClass="requiredInput">
        <apex:outputPanel layout="block" styleClass="requiredBlock"/>
        <apex:selectList value="{!adjustment.Type__c}" size="1">
            <apex:selectOptions value="{!typeOptions}"/>
        </apex:selectList>
    </apex:outputPanel>
</apex:pageBlockSectionItem>

Then controller code needs to be added to build the option values. The real killer here is that there is presently no API to obtain the picklist entries for each record type. That means that the mapping must be duplicated in code (the accept method in the code below). Perhaps this is tolerable for a picklist or two with simple mapping, but crazy where many picklists, picklist entries, record types and different mappings are involved.

So for a simple mapping case, instead of no code in the Apex controller, code and data like this has to be added:

public List<SelectOption> typeOptions {
    get {
        if (typeOptions == null) {
            typeOptions = new List<SelectOption>();
            typeOptions.add(new SelectOption('', '--None--'));
            DescribeFieldResult d = Adjustment__c.Type__c.getDescribe();
            for(PicklistEntry e : d.getPicklistValues()) {
                if (e.isActive() && accept(e)) {
                    typeOptions.add(new SelectOption(e.getValue(), e.getLabel()));
                }
            }
        }
        return typeOptions;
    }
    set;
}
private Boolean accept(PicklistEntry e) {
    if (adjustment.RecordTypeId != AdjustmentRecordType.getTermLife()) {
        // Only show the entries not in the hide set
        Set<String> hide = new Set<String>{
            'State Income Tax',
            'Life Interest (Pre-Tax)',
            'Loan (Pre-Tax)',
            'Dividends (Pre-Tax)'
            };
        return !hide.contains(e.getValue());
    } else {
        // Show all entries
        return true;
    }
}
public PageReference save() {
    // Work-around for ugly id being displayed to user if required="true" is used on the apex:selectList.
    if (adjustment.Type__c == null) {
        adjustment.Type__c.addError('You must enter a value');
        return null;
    }
    return standardController.save();
}

See this post for some background on doing the required check in code in the save method.

If you find that you are having to do a lot of this sort of coding, you might be able to build on Sam Arjmandi’s A Picklist Component For Your Visualforce Pages by finding a way to hook in the record type to picklist entry mapping. Also remember that there is presently a governor limit of 10 on field describes.

Advertisements

7 thoughts on “Filtering picklists by RecordType in Visualforce

  1. Thanks! This is a good workaround. But still I wish I could avoid hard coding the Record Type Pick list hide value in accept() method!

    • Yes. I noticed that. But the following code still gives me only the master list of pick list values and not pick list value defined under the Record type available for that particular user profile.
      Schema.getGlobalDescribe().get(‘SObject’).getDescribe().fields.getMap().get(‘Custom_Field__c’).getDescribe().getPicklistValues()
      Am I missing something? Please help.

  2. The case I’ve seen the picklist filtering work was on a picklist directly displayed using an apex:inputField. Can you simplify to that? From your experience looks like the describe API does not filter.

  3. Thanks for your followup!
    You are correct. I am using the simple apex:inputField in VF page & gets the values from apex class with the above mentioned code. Somehow the describe API still does not filter & always gives the set of pick list values defined in master record type 😦

  4. You should not need any controller code if the underlying SObject field is of type picklist; Force.com will do the filtering for your provided the record type is set on the SObject.

  5. Wow! you are great! This worked like magic.
    So all I had to do is not to use my own custom apex method to fetch pick list value.
    Thanks for your time. I really appreciate it.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s