The Blog - Expert Thoughts & Opinions

Custom Multi-User Selection in Visualforce

The idea behind our component is to store a map of the user Ids and the names of the user as you’d want to show the names of the users in the selection but would probably need the user Ids.

We prefer to base all of our Visualforce components on native Visualforce and Apex rather than Javascript. It’s then just easier to test our functionality without using automated UI tests such as Selenium tests. The disadvantage of using native Visualforce rather than Javascript is a bit slower feature since every button click the user will do would require a request to be sent to Salesforce.

public class UsersSelectionController {
    public List selectedUserIds { get; set; }
    public List removedUserIds { get; set; }
    public String whereClause { get; set; }
    private Map availableUsersMap;
    private Map selectedUsersMap;

    public UsersSelectionController() {
        initializeCollections();
        getUsers();
    }
	
	private void initializeCollections() {
        selectedUserIds = new List();
        removedUserIds = new List();
        availableUsersMap = new Map();
        selectedUsersMap = new Map();
    }

    private void getUsers() {
        for (User u : [SELECT id, name FROM user WHERE IsActive = true]) {
            availableUsersMap.put(u.Id, u.Name);
        }
    }
}

We then add 2 methods to add and remove a user. When add is called, we’re looking at which users were selected in the available user's multi picklist. When remove is called, we’re looking for which users were selected in the selected user's multi picklist.

public void add() {
	if (!selectedUserIds.isEmpty()) {
		for (String userId : selectedUserIds) {
			selectedUsersMap.put(userId, availableUsersMap.get(userId));
			availableUsersMap.remove(userId);
		}
	}
}

public void remove() {
	if (!removedUserIds.isEmpty()) {
		for (String userId : removedUserIds) {
			availableUsersMap.put(userId, selectedUsersMap.get(userId));
			selectedUsersMap.remove(userId);
		}
	}
}

Another important part is to add 2 getter methods that return a list of 'SelectOption' for each of the multi picklists.

public List<SelectOption> getAvailableUsers() {
&#9;List<SelectOption> availableUsers = new List();
&#9;for (Id userId : availableUsersMap.keySet()) {
&#9;&#9;availableUsers.add(new SelectOption(userId, availableUsersMap.get(userId)));
&#9;}
&#9;return availableUsers;
}

public List<SelectOption> getSelectedUsers() {
&#9;List<SelectOption> selectedUsers = new List();
&#9;for (String userId : selectedUsersMap.keySet()) {
&#9;&#9;selectedUsers.add(new SelectOption(userId, selectedUsersMap.get(userId)));
&#9;}
&#9;return selectedUsers;
}

Last, we’ll add another filtering method which will allow the user to filter and search for a specific user within the available users's list.

public void filterAvailableOptions() {
&#9;availableUsersMap = new Map<Id, String>();
&#9;selectedUserIds = new List();
&#9;String likeClause = '%' + whereClause + '%';
&#9;for (User u : [SELECT id, name FROM User WHERE name like :likeClause]) {
&#9;&#9;if (!selectedUsersMap.containsKey(u.Id)) {
&#9;&#9;&#9;availableUsersMap.put(u.Id, u.Name);
&#9;&#9;}
&#9;}
}

The Visalforce would have 2 multi select lists, 2 buttons to add and remove and a search text input with a search button.

We used an HTML table to layout the table properly. You can then include this in any of your features and use the 'selectedUserIds' list to process the users that were selected.

<apex:page controller="UsersSelectionController">
    <apex:form >
&#9;&#9;<apex:outputPanel layout="block">
&#9;&#9;&#9;<apex:outPutLabel value="Search for: "/>
&#9;&#9;&#9;<apex:inputText value="{!whereClause}" style=" margin: 10px;"/>
&#9;&#9;&#9;<apex:commandButton action="{!filterAvailableOptions}" value="Find" rerender="availableUsersBlock"/>
&#9;&#9;</apex:outputPanel>
&#9;&#9;<apex:outputPanel id="multiselectPanel" layout="block" styleClass="duelingListBox">
&#9;&#9;&#9;<table class="layout">
&#9;&#9;&#9;&#9;<tbody>
&#9;&#9;&#9;&#9;&#9;<tr>
&#9;&#9;&#9;&#9;&#9;&#9;<td class="selectCell">
&#9;&#9;&#9;&#9;&#9;&#9;&#9;<apex:outputPanel layout="block" styleClass="selectTitle">
&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;<apex:outputLabel value="Available Users" for="multiselectPanel:leftList" />
&#9;&#9;&#9;&#9;&#9;&#9;&#9;</apex:outputPanel>
&#9;&#9;&#9;&#9;&#9;&#9;&#9;<apex:selectList id="availableUsersBlock" value="{!selectedUserIds}" multiselect="true" size="15" style="width: 200px;">
&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;<apex:selectOptions value="{!availableUsers}"/>
&#9;&#9;&#9;&#9;&#9;&#9;&#9;</apex:selectList>
&#9;&#9;&#9;&#9;&#9;&#9;</td>
&#9;&#9;&#9;&#9;&#9;&#9;<td class="buttonCell">
&#9;&#9;&#9;&#9;&#9;&#9;&#9;<apex:outputPanel layout="block" styleClass="text">Add</apex:outputPanel>
&#9;&#9;&#9;&#9;&#9;&#9;&#9;<apex:outputPanel layout="block" styleClass="text">
&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;<apex:commandLink action="{!add}" rerender="availableUsersBlock, selectedUsersBlock" id="btnRight"> 
&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;<apex:image value="/s.gif" alt="Add" styleClass="rightArrowIcon" title="Add" />
&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;</apex:commandLink>
&#9;&#9;&#9;&#9;&#9;&#9;&#9;</apex:outputPanel>
&#9;&#9;&#9;&#9;&#9;&#9;&#9;<apex:outputPanel layout="block" styleClass="text">
&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;<apex:commandLink action="{!remove}" rerender="availableUsersBlock, selectedUsersBlock" id="btnLeft">
&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;<apex:image value="/s.gif" alt="Remove" styleClass="leftArrowIcon" title="Remove" />
&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;</apex:commandLink>
&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;<apex:outputPanel layout="block" styleClass="duelingText">Remove</apex:outputPanel>
&#9;&#9;&#9;&#9;&#9;&#9;&#9;</apex:outputPanel>
&#9;&#9;&#9;&#9;&#9;&#9;</td>
&#9;&#9;&#9;&#9;&#9;&#9;<td class="selectCell">
&#9;&#9;&#9;&#9;&#9;&#9;&#9;<apex:outputPanel layout="block" styleClass="selectTitle">
&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;<apex:outputLabel value="Selected Users" for="multiselectPanel:rightList" />
&#9;&#9;&#9;&#9;&#9;&#9;&#9;</apex:outputPanel>
&#9;&#9;&#9;&#9;&#9;&#9;&#9;<apex:selectList id="selectedUsersBlock" value="{!removedUserIds}" multiselect="true" size="15" style="width: 200px;">
&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;<apex:selectOptions value="{!selectedUsers}"/>
&#9;&#9;&#9;&#9;&#9;&#9;&#9;</apex:selectList>
&#9;&#9;&#9;&#9;&#9;&#9;</td>
&#9;&#9;&#9;&#9;&#9;</tr>
&#9;&#9;&#9;&#9;</tbody>
&#9;&#9;&#9;</table>
&#9;&#9;</apex:outputPanel>
&#9;</apex:form>
</apex:page>

Feel free to check out the full code with our unit tests in our Github.