“Combobox” input in Visualforce

To quote the Force.com documentation:

A combobox is a picklist that also allows users to type a value that is not already specified in the list.

A picklist field itself stores a string value and does not enforce that the value stored is one of the picklist entry values. The “combobox” presentation just makes this flexibility visible in the UI. Here is an example in the Task editing page:

For some reason this presentation type hasn’t been included in the field types that can be specified for custom fields so it isn’t possible to get this sort of editing capability via default (layout-based) UI. But it can be created using Visualforce, and here is one way to do that.

My requirement was a bit more complicated than a basic combobox in that the list of values to be offered also depended on another field on the page. Also to save development work and to make the interface a little cleaner I didn’t use a pop-up window. Here is an example of the resulting page:

When the “Type” field is changed alternate candidate “Document Name” links are presented and when a link is clicked the text is transferred to the “Document Name” field. The square brackets around each candidate is copied from how the default date is presented next to a date field. The user can of course enter any other “Document Name” value they like by just typing into the field directly.

Here is the Visualforce with the Javascript that uses JSON data provided by the controller to determine what links to write into the HTML to the right of the “Document Name” field:

<apex:page id="p" standardController="cve__Document__c" extensions="DocumentController">
    <apex:sectionHeader title="Document Edit" subtitle="New Document" />
    <apex:pageMessages />
    <apex:form id="f">
        <apex:pageBlock id="pb" title="Document Edit" mode="edit">
            <apex:pageBlockButtons >
                <apex:commandButton value="Save" action="{!save}"/>
                <apex:commandButton value="Cancel" action="{!cancel}"/>
            </apex:pageBlockButtons>
            <apex:pageBlockSection id="pbs" title="Information" columns="1">
                <apex:inputField value="{!cve__Document__c.cve__Claim__c}"/>
                <apex:inputField id="type" value="{!cve__Document__c.cve__Type__c}" onchange="cvOfferOptions();"/>
                <apex:pageBlockSectionItem id="pbsi">
                    <apex:outputLabel value="{!$ObjectType.cve__Document__c.fields.Name.label}"/>
                    <apex:panelGrid columns="2">
                        <apex:inputField id="name" value="{!cve__Document__c.Name}" style="width: 20em" required="true"/>
                        <apex:outputPanel id="panel">
                        </apex:outputPanel>
                    </apex:panelGrid>
                </apex:pageBlockSectionItem>
                <apex:inputField value="{!cve__Document__c.cve__Required__c}"/>
                <apex:inputField value="{!cve__Document__c.cve__Received__c}"/>
            </apex:pageBlockSection>
        </apex:pageBlock>
    </apex:form>
<script type="text/javascript">
function cvDocuments() {
    var documentNames = {!documentNamesJson};
    var type = document.getElementById('p:f:pb:pbs:type');
    var selectedType = type.options[type.selectedIndex].text;
    return documentNames[selectedType];
}
function cvOfferOptions() {
    var documents = cvDocuments();
    var html = '';
    if (documents != null) {
        for (var i = 0; i < documents.length; i++) {
            html += ''
                    + '<div>[ <a href="javascript:cvPickValue('
                    + i
                    + ');" title="Click to set as \'Document Name\'">'
                    + documents[i]
                    + '</a> ]</div>'
                    ;
        }
    }
    var panel = document.getElementById('p:f:pb:pbs:pbsi:panel');
    panel.innerHTML = html;
}
function cvPickValue(index) {
    var name = document.getElementById('p:f:pb:pbs:pbsi:name');
    name.value = cvDocuments()[index];
}
function cvMakeDoubleDelegate(function1, function2) {
    return function() {
        if (function1) {
            function1();
        }
        if (function2) {
            function2();
        }
    }
}
window.onload = cvMakeDoubleDelegate(window.onload, cvOfferOptions);
</script>
</apex:page>

The controller extension is just responsible for providing the JSON string, with the data access and “save” and “cancel” actions left to the standard controller:

public with sharing class DocumentController {

    private ApexPages.StandardController standardController;

    public DocumentController(ApexPages.StandardController standardController) {
        this.standardController = standardController;
    }

    // This controller extension just supplies this data with all other functionality using the standard controller
    public String documentNamesJson {
        get {
            Map<String, List<String>> m = new Map<String, List<String>>();
            m.put('Legal', new String[] {
                    'Appeal',
                    'Complaints',
                    'Other'
                    });
            m.put('Medical', new String[] {
                    'Attending Physician\'s Statement',
                    'Medical Records',
                    'Medical Reviews',
                    'Pharmacy',
                    'Toxicology',
                    'Autopsy/Coroner\'s Report',
                    'Other'
                    });
            m.put('Financial', new String[] {
                    'Income Records',
                    'Interest Calculation',
                    'Collection Referral',
                    'Refund',
                    'Other'
                    });
            return json(m);
        }
    }
    
    private String json(Object o) {
        JSONGenerator generator = JSON.createGenerator(false);
        generator.writeObject(o);
        return generator.getAsString();
    }
}

Note that while in this example the candidate values are expressed in Apex code, DescribeFieldResult.getPicklistValues can be used to obtain the entry values defined for a picklist field.

Advertisements

One thought on ““Combobox” input in Visualforce

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