Reusable Multi Select Custom Lookup In Salesforce 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 :
Component Blue Print :
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 :
Related Resources :
Custom Single Lookup Lightning Component
https://www.lightningdesignsystem.com/icons/#standard
Salesforce Lightning Design System Lookup
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
piyush soni, thanks so much for the post.Really thank you! Great.
piyush soni,thanks so much for the post.Much thanks again. Really Cool.
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
Hi vijitha, it’s my typo mistake in step 4, line number 14. i have updated it
Thanks
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
Always posts awesome things.. Great work 🙂
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
How can I use this as quick action
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,
did you get any replay on this one? it’s exactly what i’m looking for.
Thanx a lot!
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
How can I save the records selected from multiple select control?
yes you can
How can i save this value
How can I save the records stored in lstSelectedRecords or pass to another component
Has anyone able to get this to go into a Page Layout?
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!
Thank you so much. It saved my lot of effort.. really great full to you…
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, how can we save the picked account and where can we place this component
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.
what is the error ?
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
please post your issue on developer forums in more details and share question URL here, so i can look into your issue. Thanks
https://developer.salesforce.com/forums
Please look in to the issue.
https://developer.salesforce.com/forums#!/feedtype=SINGLE_QUESTION_DETAIL&dc=Lightning&criteria=ALLQUESTIONS&id=9062I000000QuwKQAS
Please look into this issue i’m facing
https://developer.salesforce.com/forums#!/feedtype=SINGLE_QUESTION_DETAIL&dc=Lightning&criteria=ALLQUESTIONS&id=9062I000000QuwKQAS
Awesome!
Made a tweak to flag the field as required.
Add this attribute:
Replace the current label HTML with this:
* {!v.label}
{!v.label}
This is the great post. It helped me lots. Thanks! 🙂
Hi there, do you do consulting work? How much would it cost to hire you to do this for us?
yes we do, you can contact with me on [email protected]
Thanks
This Code is working fine but can i have email also under name like sub heading.
ex..
Sachin
[email protected]
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