custom-lookup-multiselect-lightning

Reusable Multi Select Custom Lookup In Salesforce Lightning Component

Lightning Component

Welcome back guys, today in this post we are going to learn, how we can create custom multi select lookup with salesforce lightning components.

Multi-Select Lookup Component Functionality :

  • Multi Record Section (multi select lookup).
  • Re-usable.
  • Prevent duplicate record selection and more…
Output :

multiselect-lookup-lightning

Component Blue Print :

multiselect lookup blueprint

Step 1 : Create Apex Class Controller.
Apex class [reUsableMultiSelectLookupCtrl.apxc]
public class reUsableMultiSelectLookupCtrl {
    @AuraEnabled
    public static List < sObject > fetchLookUpValues(String searchKeyWord, String ObjectName, List<sObject> ExcludeitemsList) {
        String searchKey = '%' + searchKeyWord + '%';
        List < sObject > returnList = new List < sObject > ();

        List<string> lstExcludeitems = new List<string>();
        for(sObject item : ExcludeitemsList ){
            lstExcludeitems.add(item.id);
        }
        
        // Create a Dynamic SOQL Query For Fetch Record List with LIMIT 5 and exclude already selected records  
        String sQuery =  'select id, Name from ' +ObjectName + ' where Name LIKE: searchKey AND Id NOT IN : lstExcludeitems order by createdDate DESC limit 5';
        List < sObject > lstOfRecords = Database.query(sQuery);
        
        for (sObject obj: lstOfRecords) {
            returnList.add(obj);
        }
        return returnList;
    }
}
Step 2 : Create Lightning Event
  • Next we create a Lightning Event bundle for communicate data(attribute values) between different components. (Child To Parent Component )
  • Events are contain attributes that can be set before the event is fired and read when the event is handled by component.

from developer console >> file >> new >> Lightning Event >> enter name >> selectedsObjectRecordEvent >> save

lightning Event [selectedsObjectRecordsEvent.evt]
<aura:event type="COMPONENT" description="by this event we are pass the selected sObject(lookup list record) in the parent component">
    <aura:attribute name="recordByEvent" type="sObject"/>
</aura:event>
Step 3 : Create Child Component For Display the Search Result List
Lightning Component [reUsableMultiSelectLookupResult.cmp] 
<aura:component access="global">
    <!--aura attributes-->  
    <aura:attribute name="oRecord" type="sObject" />
    <aura:attribute name="IconName" type="string"/> 
    
    <!--Register the component level event-->
    <aura:registerEvent name="oSelectedRecordEvent" type="c:selectedsObjectRecordsEvent"/>
    
    <li role="presentation" class="slds-listbox__item" onclick="{!c.selectRecord}">
        <span id="listbox-option-unique-id-01" class="slds-media slds-listbox__option slds-listbox__option_entity slds-listbox__option_has-meta" role="option">
            <span class="slds-media__figure">
                <span class="slds-icon_container" title="Description of icon when needed">
                    <lightning:icon iconName="{!v.IconName}" class="slds-icon slds-icon_small" size="small" alternativeText="icon"/>
                </span>
            </span>    
            <span class="slds-media__body">  
                <span class="slds-listbox__option-text slds-listbox__option-text_entity">{!v.oRecord.Name}</span>
            </span>
        </span>
    </li>
</aura:component>
client side JavaScript Controller :[reUsableMultiSelectLookupResultController.js]
({
    selectRecord : function(component, event, helper){      
        // get the selected record from list  
        var getSelectRecord = component.get("v.oRecord");
        // call the event   
        var compEvent = component.getEvent("oSelectedRecordEvent");
        // set the Selected sObject Record to the event attribute.  
        compEvent.setParams({"recordByEvent" : getSelectRecord });  
        // fire the event  
        compEvent.fire();
    },
})
Step 4 : Create Parent Component
Lightning Component [reUsableMultiSelectLookup.cmp]
<aura:component controller="reUsableMultiSelectLookupCtrl">
    
    <!--declare attributes--> 
    <aura:attribute name="lstSelectedRecords" type="sObject[]" default="[]" description="Use,for store SELECTED sObject Records"/>
    <aura:attribute name="listOfSearchRecords" type="List" description="Use,for store the list of search records which returns from apex class"/>
    <aura:attribute name="SearchKeyWord" type="string"/>
    <aura:attribute name="objectAPIName" type="string" default=""/>
    <aura:attribute name="IconName" type="string" default=""/>
    <aura:attribute name="label" type="string" default=""/>
    
    <!--declare events hendlers-->  
    <aura:handler name="oSelectedRecordEvent" event="c:selectedsObjectRecordsEvent" action="{!c.handleComponentEvent}"/>
    <aura:attribute name="Message" type="String" default=""/>
    
    <!-- https://www.lightningdesignsystem.com/components/lookups/ --> 
    
    <div onmouseleave="{!c.onblur}" aura:id="searchRes" class="slds-form-element slds-lookup slds-is-close">
        <label class="slds-form-element__label">{!v.label}</label>
        <!--This part is for display search bar for lookup-->  
        <div class="slds-form-element__control">
            <div class="slds-input-has-icon slds-input-has-icon--right">
                <!-- This markup is for when an record is selected -->
                <div aura:id="lookup-pill" class="slds-pill-container">
                    <div aura:id="lookupField" class="slds-combobox__form-element slds-input-has-icon slds-input-has-icon_right"  style="width: 100%;">
                        
                        <ui:inputText click="{!c.onfocus}"
                                      updateOn="keyup"
                                      keyup="{!c.keyPressController}"
                                      class="slds-lookup__search-input slds-input inputSize"
                                      value="{!v.SearchKeyWord}"
                                      placeholder="search.."/>
                        
                        <span class="slds-icon_container slds-icon-utility-search slds-input__icon slds-input__icon_right">
                            <lightning:icon iconName="utility:search" size="x-small" alternativeText="icon"/>
                        </span>
                    </div> 
                <!--Selected Records Pills Section-->
                    <aura:iteration items="{!v.lstSelectedRecords}" var="sr">
                        <lightning:pill class="slds-m-around_xx-small" label="{!sr.Name}" name="{!sr.Id}" onremove="{! c.clear }">
                            <aura:set attribute="media">
                                <lightning:icon iconName="{!v.IconName}" size="x-small" alternativeText="icon"/>
                            </aura:set>
                        </lightning:pill>
                    </aura:iteration>
                </div>
            </div>
        </div>

        <!--This part is for Display typehead lookup result List-->  
        <ul style="min-height:40px;margin-top:0px !important" class="slds-listbox slds-listbox_vertical slds-dropdown slds-dropdown_fluid slds-lookup__menu slds" role="listbox">
            <lightning:spinner class="slds-hide" variant="brand" size="small" aura:id="mySpinner"/>
            <center> {!v.Message}</center>
            <aura:iteration items="{!v.listOfSearchRecords}" var="singleRec">
                <!--Child Component--> 
                <c:reUsableMultiSelectLookupResult oRecord="{!singleRec}" IconName="{!v.IconName}"/>
            </aura:iteration>
        </ul>
    </div>
</aura:component>
client side JavaScript Controller :[reUsableMultiSelectLookupController.js]
({
    onblur : function(component,event,helper){
        // on mouse leave clear the listOfSeachRecords & hide the search result component 
        component.set("v.listOfSearchRecords", null );
        component.set("v.SearchKeyWord", '');
        var forclose = component.find("searchRes");
        $A.util.addClass(forclose, 'slds-is-close');
        $A.util.removeClass(forclose, 'slds-is-open');
    },
    onfocus : function(component,event,helper){
        // show the spinner,show child search result component and call helper function
        $A.util.addClass(component.find("mySpinner"), "slds-show");
        component.set("v.listOfSearchRecords", null ); 
        var forOpen = component.find("searchRes");
        $A.util.addClass(forOpen, 'slds-is-open');
        $A.util.removeClass(forOpen, 'slds-is-close');
        // Get Default 5 Records order by createdDate DESC 
        var getInputkeyWord = '';
        helper.searchHelper(component,event,getInputkeyWord);
    },
    
    keyPressController : function(component, event, helper) {
        $A.util.addClass(component.find("mySpinner"), "slds-show");
        // get the search Input keyword   
        var getInputkeyWord = component.get("v.SearchKeyWord");
        // check if getInputKeyWord size id more then 0 then open the lookup result List and 
        // call the helper 
        // else close the lookup result List part.   
        if(getInputkeyWord.length > 0){
            var forOpen = component.find("searchRes");
            $A.util.addClass(forOpen, 'slds-is-open');
            $A.util.removeClass(forOpen, 'slds-is-close');
            helper.searchHelper(component,event,getInputkeyWord);
        }
        else{  
            component.set("v.listOfSearchRecords", null ); 
            var forclose = component.find("searchRes");
            $A.util.addClass(forclose, 'slds-is-close');
            $A.util.removeClass(forclose, 'slds-is-open');
        }
    },
    
    // function for clear the Record Selaction 
    clear :function(component,event,heplper){
        var selectedPillId = event.getSource().get("v.name");
        var AllPillsList = component.get("v.lstSelectedRecords"); 
        
        for(var i = 0; i < AllPillsList.length; i++){
            if(AllPillsList[i].Id == selectedPillId){
                AllPillsList.splice(i, 1);
                component.set("v.lstSelectedRecords", AllPillsList);
            }  
        }
        component.set("v.SearchKeyWord",null);
        component.set("v.listOfSearchRecords", null );      
    },
    
    // This function call when the end User Select any record from the result list.   
    handleComponentEvent : function(component, event, helper) {
        component.set("v.SearchKeyWord",null);
        // get the selected object record from the COMPONENT event 	 
        var listSelectedItems =  component.get("v.lstSelectedRecords");
        var selectedAccountGetFromEvent = event.getParam("recordByEvent");
        listSelectedItems.push(selectedAccountGetFromEvent);
        component.set("v.lstSelectedRecords" , listSelectedItems); 
        
        var forclose = component.find("lookup-pill");
        $A.util.addClass(forclose, 'slds-show');
        $A.util.removeClass(forclose, 'slds-hide');
        
        var forclose = component.find("searchRes");
        $A.util.addClass(forclose, 'slds-is-close');
        $A.util.removeClass(forclose, 'slds-is-open'); 
    },
})
client side JavaScript Helper :[reUsableMultiSelectLookupHelper.js]
({
    searchHelper : function(component,event,getInputkeyWord) {
        // call the apex class method 
        var action = component.get("c.fetchLookUpValues");
        // set param to method  
        action.setParams({
            'searchKeyWord': getInputkeyWord,
            'ObjectName' : component.get("v.objectAPIName"),
            'ExcludeitemsList' : component.get("v.lstSelectedRecords")
        });
        // set a callBack    
        action.setCallback(this, function(response) {
            $A.util.removeClass(component.find("mySpinner"), "slds-show");
            var state = response.getState();
            if (state === "SUCCESS") {
                var storeResponse = response.getReturnValue();
                // if storeResponse size is equal 0 ,display No Records Found... message on screen.                }
                if (storeResponse.length == 0) {
                    component.set("v.Message", 'No Records Found...');
                } else {
                    component.set("v.Message", '');
                    // set searchResult list with return value from server.
                }
                component.set("v.listOfSearchRecords", storeResponse); 
            }
        });
        // enqueue the Action  
        $A.enqueueAction(action);
    },
})
Component CSS :[reUsableMultiSelectLookup.CSS]
.THIS .inputSize {
    width:100%; 
}
TestApp.app
<aura:application extends="force:slds">
  <!-- Create attribute to store lookup value as a sObject--> 
  <aura:attribute name="selectedLookUpRecords" type="sObject[]" default="[]"/>

  <c:reUsableMultiSelectLookup objectAPIName="account"
                               IconName="standard:account"
                               lstSelectedRecords="{!v.selectedLookUpRecords}"
                               label="Account Name"/>
   <!-- here c: is org. namespace prefix-->
</aura:application>

This is a dynamic Re-Usable Lightning Component you have need to pass the objectAPIName , IconName, selected Records List and Field Label  Attributes when use this Custom Multi-select Lookup component.

Check Also : Single Selection Custom Lookup Component in Lightning Component 
Custom Salesforce Lightning Multi select lookup Output : 

multiselect-lookup-lightning

 

Related Resources :

Custom Single Lookup Lightning Component

https://www.lightningdesignsystem.com/icons/#standard

Salesforce Lightning Design System Lookup

Lightning Component Events

Like our facebook page for new post updates. & Don’t forget to bookmark this site for your future reference.

if you have any suggestions or issue with it, you can post in comment box  🙂

47 comments

  • Hi

    Can this component be used in lightning for outlook?. i have tried using other lightning components in lightning for outlook to be able to customize the email pane, but not this.

  • HI ,

    I am getting bellow error when i use the above code how can solve that. Please help me.

    This page has an error. You might just need to refresh it. Unable to find ‘fetchLookUpValues’ on ‘compound://veeru.reUsableMultiSelectLookup’. Failing descriptor: {markup://veeru:reUsableMultiSelectLookup}

  • I am getting this error. Could you please provide me with this event.

    Failed to save reUsableMultiSelectLookup.cmp: No EVENT named markup://c:post_selectedsObjectRecordsEvent found

  • On clicking the drop down value,

    This page has an error. You might just need to refresh it. Action failed: c:reUsableMultiSelectLookup$controller$handleComponentEvent [Cannot read property ‘Push’ of undefined] Failing descriptor: {c:reUsableMultiSelectLookup$controller$handleComponentEvent}

    Any help?

     

    Thanks

    Heamnth

    • Hi Hemanth, did you find the solution. I am facing the same issue.
      Please let me know if you did.
      Thank you in advance.

      Manohar

  • How to re-use the same component multiple times in the app?

    For Example: I want to display 2 multi select lookups in the same app. How can I extend this ? and also I want to extend it with dependency in below example base on country selection I want to filter states.

    Thanks for your help!!! You post is really great!!

     

    like:
    <aura:application extends=“force:slds”>
      <!– Create attribute to store lookup value as a sObject–>
      <aura:attribute name=“selectedLookUpRecords” type=“sObject[]” default=“[]”/>

      <c:reUsableMultiSelectLookup objectAPIName=Country
                                   IconName=“standard:account”
                                   lstSelectedRecords=“{!v.selectedLookUpRecords}”
                                   label=“Account Name”/>

      <c:reUsableMultiSelectLookup objectAPIName=State
                                   IconName=“standard:account”
                                   lstSelectedRecords=“{!v.selectedLookUpRecords}”
                                   label=“Account Name”/>

       <!– here c: is org. namespace prefix–>
    </aura:application>

  • Hi! Once you’ve entered the code, how do you find the component that was created in Salesforce?
    -> I would like to add this element in my account page to lookup and add several products that are linked to this account.

    How do I do this now that the code is entered?

    Thanks,

  • HI I jus need to find Email of contact object

     <c:reUsableMultiSelectLookup objectAPIName=“Contact“
                                   IconName=“standard:account”
                                   lstSelectedRecords=“{!v.selectedLookUpRecords}”
                                   label=“Account Name”/>

       <!– here c: is org. namespace prefix–>

    What need to change here please help me

  • Hi, how do you launch/implement this after the code is done? I’m not a developer by any stretch and could use help launching

  • Hi This is great. How do I go about saving the value of the pick list into an object – Say if I wanted to place it within a tab

  • Hi,

    quick question about this topic. once we choose specific contact under the account, can we save it some how? and it’ll be attached to the account and updated in the DB?

    for example i need to attach several contacts to the opportunity, and i cant use contact role and i prefer not to create new related list. is that solution can be useful? i need to set several “internal” contacts to one opportunity.

    thanks

  • Sorry, maybe my issue is stupid, but I’m new in lightning, was using mostly Classic UI.
    Straight to point:
    I was able to follow all your steps, reproduce them. After pushed TestApp preview button, App works perfectly!

    Now question, how can I add this into page layout? I would like it to be visible on the Account.
    Inside app builder i’m not able to see custom component.

    Thank you!

    It’s not that simple, like putting inside page layout visual component.
    Can you please share this with me? Thank you in Advance!

  • how to set default values for <aura:attribute name=“lstSelectedRecords” type=“sObject[]” default=“[]” description=“Use,for store SELECTED sObject Records”/>

    can i  know the sysntax?

     

  • Attempting to use this in a flow and save the multi-pick lookup IDs in a collection variable. However, the IDs are not being saved properly – any thoughts?

    • Hi, this was a few years ago but coming across it now, did you ever figure out how to save the multi-pick lookup IDs? Thanks!

  • Hi ,Its is not working for case object. I did below modification.

    <c:reUsableMultiSelectLookup objectAPIName=”case”
    IconName=”standard:case”
    lstSelectedRecords=”{!v.selectedLookUpRecords}”
    label=”Case Subject”/>

    Please help me how to do multi select Custom lookup with case object.

  • Hi team,

    I have implemented your code but modified a little bit and I have doubt how to save the values into custom object??

    I have made an attribute default value as user object

  • Awesome!

    Made a tweak to flag the field as required.

    Add this attribute:

    Replace the current label HTML with this:

    * {!v.label}

    {!v.label}

  • The preview works as intended, but I need this as a component on page layout. I have added to page but app does not function. Please advise

  • Is there any way to support keyboard control where the up and down arrows will move through the lookup and enter will select a value? Thank you.

    • Charley
      did you find any solution ??
      if yes please share me am also facing with same issue not able to select through keyboard up and down arrows.

      Thanks in advance.

  •  

    Hi Mat, try to copy this in the parent controller

    <aura:component controller=”reUsableMultiSelectLookupCtrl” implements=”flexipage:availableForAllPageTypes” access=”global”>

  • Hi ,

    This is working amazing with mouse interaction, but it is not working with keyup and keydown arrows. Is there any suggestion for to using keyboard interaction

Leave a Reply