Tuesday, September 27, 2011

MultiSelect Picklist Solution for a VisualForce Page

I had to use a VF Page for Person Accounts. On adding an InputField bound to a picklist to the VisualForce Page - I kept getting this error 'The first validation error encountered was "Record Type ID: value not valid for the entity: Account".' on loading the VF Page. I had to resort to using a picklist solution as given here

But when it comes to MultiSelect Picklists, the UI was not much great, having multiselect = "True" on the VF SelectList Component - It just served the purpose. The UI required, using the Shift + a Select with your mouse or Ctrl + a Select with your mouse. Not very User-Friendly!! :(

A multiselect component which has two list boxes - one containing the available values and the other containing the chosen values and a VF Page which uses the same for displaying the multiselect values is worth a try. A User-Friendly Solution! Just works like how any multi-select field would behave on an Edit Page! :)

The component for MultiSelect can be used on the Person Account VF Page, just like any other component.

<c:MultiselectComponent AvailableList="{!availableList}" ChosenList="{!chosenList}"/>


The below code can be used to get the picklist values

Schema.DescribeFieldResult optionFieldDescription = Account.MultiSelect__c.getDescribe();
for(Schema.PicklistEntry pleOptions : optionFieldDescription.getPicklistValues()){
availableList.add(new SelectOption(pleOptions.getvalue(),pleOptions.getLabel()));
}


When there is a need for an update to the field value:
The multi-select picklist options are saved in salesforce as a string, separated by a semi-colon. A method can be used to combine the options before saving into the database.

private static String MULTIPICKLIST_SEPERATOR = ';';
private String combineOptions(List<SelectOption> values) {
String result = '';
for(SelectOption s: values) {
result = result == '' ? s.getValue() : result + MULTIPICKLIST_SEPERATOR + s.getValue();
}
return result.length() > 0 ? result.substring(0, result.length()): result;
}


The multiSelect component

<apex:component controller="MultiSelectComponentController">
<apex:attribute name="AvailableList" type="selectOption[]" description="Available List from the Page" assignTo="{!options}" required="True"/>
<apex:attribute name="ChosenList" type="selectOption[]" description="Chosen List from the Page" assignTo="{!selectedOptions}" required="True"/>

<script type="text/javascript">
function selection() {
selection();
}
function deselection() {
deselection();
}
</script>
<!-- Apex function called to move the selected values from available list to chosen list and vice versa -->
<apex:actionFunction name="selection" action="{!selecting}" reRender="multiselect"/>
<apex:actionFunction name="deselection" action="{!deselecting}" reRender="multiselect"/>
<apex:outputPanel id="panel">
<apex:pageBlockSection columns="4" >
<apex:selectList multiselect="true" size="5" value="{!selected}">
<apex:selectOption value="{!Available}"/>
<apex:selectOptions value="{!options}" />
<apex:actionSupport event="ondblclick" action="{!selecting}" rerender="panel" status="waitingStatus" />
</apex:selectList>
<apex:pageBlockSection columns="1">
<apex:commandButton reRender="panel" id="select" action="{!selecting}" value=">" status="waitingStatus"/>
<apex:commandButton reRender="panel" id="deselect" action="{!deselecting}" value="<" status="waitingStatus"/>
</apex:pageBlockSection>
<!-- An action status to show that the operation of moving between the lists is in progress--->
<apex:actionStatus id="waitingStatus" startText="Please wait..." stopText=""/>
<apex:selectList multiselect="true" size="5" value="{!deselected}">
<apex:selectOption value="{!Chosen}"/>
<apex:selectOptions value="{!selectedOptions}" />
<apex:actionSupport event="ondblclick" action="{!deselecting}" rerender="panel" status="waitingStatus" />
</apex:selectList>
</apex:pageBlockSection>
</apex:outputPanel>
</apex:component>


Corresponding Apex functions in the MultiSelectComponentController:

public void selecting() {
for(String toSelect: selected) {
Integer i = 0;
While(i<options.size()) {
if(options[i].getvalue()==toSelect) {
selectedOptions.add(new SelectOption(toSelect,toSelect));
options.remove(i);
}
i++;
}
}
}

public void deselecting() {
for(String toDeselect: deselected) {
Integer i = 0;
While(i<selectedOptions.size()) {
if(selectedOptions[i].getvalue()==toDeselect) {
options.add(new SelectOption(toDeselect, toDeselect));
selectedOptions.remove(i);
}
i++;
}
}
}