Lightning Web Components (LWC) is current Trending technology in Salesforce Lightning Eco System. I have shared many post related to LWC which you can check to quickly get the idea. Today I will share Multi Select Custom Lookup in LWC.

Previously I have shared Custom Lookup in Lightning web Component but in that we can only select one record, Sometimes there is requirement where we need to aloow users to select multiple records. We don’t have any standard component available for this functionality so I have created below component. As this is generic component so you can use it with any object.
Now we will check the code part.
lwcMultiLookup.html
<template>
<lightning-card>
<h3 slot="title">
<lightning-icon icon-name="utility:connected_apps" size="small"></lightning-icon>
Multi Select Custom Lookup in Lightning Web Components
</h3>
<div slot="footer">
</div>
<div>
<div class="slds-form-element">
<div class="slds-form-element__control">
<div class="slds-combobox_container">
<div class={txtclassname} data-id="resultBox" aria-expanded="false" aria-haspopup="listbox" role="combobox">
<div class="slds-form-element__control slds-input-has-icon slds-input-has-icon slds-input-has-icon_left-right" role="none">
<div>
<span class="slds-icon_container slds-icon-utility-search slds-input__icon iconheight">
<lightning-icon class="slds-icon slds-icon slds-icon_small slds-icon-text-default" icon-name={iconName} size="x-small" alternative-text="icon" ></lightning-icon>
</span>
</div>
<lightning-input required={required} data-id="userinput" label={Label} name="searchText" onchange={searchField} class="leftspace"></lightning-input>
<span class="slds-icon_container slds-icon-utility-search slds-input__icon slds-input__icon_right iconheight">
<lightning-icon class="slds-icon slds-icon slds-icon_small slds-icon-text-default" icon-name="utility:search" size="x-small" alternative-text="icon" ></lightning-icon>
</span>
</div>
<div class="slds-form-element__control slds-input-has-icon slds-input-has-icon slds-input-has-icon_left-right" role="none">
<template for:each={selectedRecords} for:item="serecord">
<span key={serecord.recId}>
<lightning-pill label={serecord.recName} name={serecord.recId} onremove={removeRecord}>
<lightning-icon icon-name={iconName} variant="circle" alternative-text={serecord.recName}></lightning-icon>
</lightning-pill>
</span>
</template>
</div>
<!-- Second part display result -->
<div id="listbox-id-1" class="slds-dropdown slds-dropdown_length-with-icon-7 slds-dropdown_fluid" role="listbox">
<ul class="slds-listbox slds-listbox_vertical" role="presentation">
<template for:each={searchRecords} for:item="serecord">
<li role="presentation" class="slds-listbox__item" key={serecord.recId}>
<div data-id={serecord.recId} data-name={serecord.recName} onclick={setSelectedRecord} 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 slds-icon-standard-account">
<lightning-icon icon-name={iconName} class="slds-icon slds-icon slds-icon_small slds-icon-text-default" size="x-small"></lightning-icon>
</span>
</span>
<span class="slds-media__body">
<span class="slds-listbox__option-text slds-listbox__option-text_entity">{serecord.recName}</span>
<span class="slds-listbox__option-meta slds-listbox__option-meta_entity">{objectName} • {serecord.recName}</span>
</span>
</div>
</li>
</template>
</ul>
</div>
<div if:true={messageFlag}>
No result found.
</div>
<div if:true={LoadingText}>
Loading...
</div>
</div>
</div>
</div>
</div>
</div>
</lightning-card>
</template>
Here we are displaying selected records on UI and also allowing users to select next record. User can easily cick on cancel icon to remove select records.
lwcMultiLookup.js
import { LightningElement,api,track } from 'lwc';
import getResults from '@salesforce/apex/lwcMultiLookupController.getResults';
export default class LwcMultiLookup extends LightningElement {
@api objectName = 'Account';
@api fieldName = 'Name';
@api Label;
@track searchRecords = [];
@track selectedRecords = [];
@api required = false;
@api iconName = 'action:new_account'
@api LoadingText = false;
@track txtclassname = 'slds-combobox slds-dropdown-trigger slds-dropdown-trigger_click';
@track messageFlag = false;
searchField(event) {
var currentText = event.target.value;
var selectRecId = [];
for(let i = 0; i < this.selectedRecords.length; i++){
selectRecId.push(this.selectedRecords[i].recId);
}
this.LoadingText = true;
getResults({ ObjectName: this.objectName, fieldName: this.fieldName, value: currentText, selectedRecId : selectRecId })
.then(result => {
this.searchRecords= result;
this.LoadingText = false;
this.txtclassname = result.length > 0 ? 'slds-combobox slds-dropdown-trigger slds-dropdown-trigger_click slds-is-open' : 'slds-combobox slds-dropdown-trigger slds-dropdown-trigger_click';
if(currentText.length > 0 && result.length == 0) {
this.messageFlag = true;
}
else {
this.messageFlag = false;
}
if(this.selectRecordId != null && this.selectRecordId.length > 0) {
this.iconFlag = false;
this.clearIconFlag = true;
}
else {
this.iconFlag = true;
this.clearIconFlag = false;
}
})
.catch(error => {
console.log('-------error-------------'+error);
console.log(error);
});
}
setSelectedRecord(event) {
var recId = event.currentTarget.dataset.id;
var selectName = event.currentTarget.dataset.name;
let newsObject = { 'recId' : recId ,'recName' : selectName };
this.selectedRecords.push(newsObject);
this.txtclassname = 'slds-combobox slds-dropdown-trigger slds-dropdown-trigger_click';
let selRecords = this.selectedRecords;
this.template.querySelectorAll('lightning-input').forEach(each => {
each.value = '';
});
const selectedEvent = new CustomEvent('selected', { detail: {selRecords}, });
// Dispatches the event.
this.dispatchEvent(selectedEvent);
}
removeRecord (event){
let selectRecId = [];
for(let i = 0; i < this.selectedRecords.length; i++){
if(event.detail.name !== this.selectedRecords[i].recId)
selectRecId.push(this.selectedRecords[i]);
}
this.selectedRecords = [...selectRecId];
let selRecords = this.selectedRecords;
const selectedEvent = new CustomEvent('selected', { detail: {selRecords}, });
// Dispatches the event.
this.dispatchEvent(selectedEvent);
}
}
In Controller SearchField we are passing the current user txet in controller with the already selected record ids. We are passing existing record ids so that we can filter those records. Once user select any record from dropdown we fire the setSelectedRecord method and add the selected record in selectedRecords array. We are also sending an event to pass selected records to parent components. Once user click on remove icon we simply remove that reord from the array.
lwcMultiLookupController.cls
public with sharing class lwcMultiLookupController {
public lwcMultiLookupController() {
}
@AuraEnabled(cacheable=true)
public static List<SObJectResult> getResults(String ObjectName, String fieldName, String value, List<String> selectedRecId) {
List<SObJectResult> sObjectResultList = new List<SObJectResult>();
system.debug(fieldName+'-------------'+ObjectName+'---++----------'+value+'====='+selectedRecId);
if(selectedRecId == null)
selectedRecId = new List<String>();
if(String.isNotEmpty(value)) {
String query = 'Select Id,'+fieldName+' FROM '+ObjectName+' WHERE '+fieldName+' LIKE \'%' + value.trim() + '%\' and ID NOT IN: selectedRecId';
system.debug(query);
for(sObject so : Database.Query(query)) {
String fieldvalue = (String)so.get(fieldName);
sObjectResultList.add(new SObjectResult(fieldvalue, so.Id));
}
}
return sObjectResultList;
}
public class SObjectResult {
@AuraEnabled
public String recName;
@AuraEnabled
public Id recId;
public SObJectResult(String recNameTemp, Id recIdTemp) {
recName = recNameTemp;
recId = recIdTemp;
}
public SObJectResult() {
}
}
}
This is a simple apex controller we are making dynamic SOQL to get records based on user input and sending back to lwccontroller. We are using wrapper to pass data. Wrapper allow us to easily handle the data with any object.
We have created one custom app in lightning, using this app we are passing data from LWC to Lightning component. We have only created this app for demo purpose. You can get the selected records and can use in your existing components.
lwcMultiLookupDemo.app
<aura:attribute name="selectedRecords" type="lwcMultiLookupController.SObjectResult[]" description="text" ></aura:attribute>
<lightning:card title="">
<c:lwcMultiLookup objectName="Account" fieldName="Name"
iconName = "standard:account" onselected="{!c.selectedRecords}"/>
<div class="slds-page-header">
<div class="slds-media">
<div class="slds-media__figure">
<span class="slds-icon_container slds-icon-standard-opportunity" title="Account Record">
</span>
</div>
<div class="slds-media__body">
<h1 class="slds-page-header__title slds-truncate slds-align-middle" title="Account Record">Accounts</h1>
<p class="slds-text-body_small slds-line-height_reset"></p>
</div>
</div>
</div>
<table class="slds-table slds-table_bordered slds-table_cell-buffer">
<thead>
<tr class="slds-text-title_caps">
<th scope="col">
<div class="slds-truncate" title="Account Id">Account Id</div>
</th>
<th scope="col">
<div class="slds-truncate" title="Account Name">Account Name</div>
</th>
</tr>
</thead>
<tbody>
<aura:iteration var="selectRec" items="{!v.selectedRecords}">
<tr>
<th scope="row" data-label="Account Id">
<div class="slds-truncate" title="{!selectRec.recId}"><a id="{!selectRec.recId}">{!selectRec.recId}</a></div>
</th>
<td data-label="Account Name">
<div class="slds-truncate" title="{!selectRec.recName}">{!selectRec.recName}</div>
</td>
</tr>
</aura:iteration>
</tbody>
</table>
</lightning:card>
lwcMultiLokkupDemo.js
({
selectedRecords : function(component, event, helper) {
var selectRecName = event.getParam('selRecords');
if(selectRecName != undefined) {
component.set("v.selectedRecords", selectRecName);
}
}
})
Here I have share complete code for Multi Select Custom Lookup LWC. Code is pretty simple and I have just modify the Lookup code. Now we are filtering the selected records in SOQL and passing a list of array in parent Lightning Component.
In this post we have also covered how to use wrapper in Lightning or Lightning Web Components.
Did you liked this post or have any suggestions, let me know in comments. Happy programming 🙂
Thanks Tushar, I have recently found out your blog and I am very pleased to say that it is one of the best sf blog. I really appreciate your knowledge sharing. Keep rocking bro !!
Thanks Agni
Hi, wanted to understand where is the code to save the Account on the parent object. we are selecting multiple records but where are we saving these. also if i wanted this on the related list functionality, where users can select mutiple records and selected records should be the child records to the parent. For Example on Campaign object I want two related list and both for contact, lets say contact1 and contact2 and these shuld not allow user to create new records they must be able to multiselect contact and save them as related records to campaign.
Hi Nandini, We get the selected records in selectedRecords list. You can use this to create new records or any other functionality you want to develop.
I love this. How much more of an effort would it take to select a range ( click 1 item then shift+click several items below to select the range)?
It it will take some time as we need to update both UI and apex.
Thanks a lot, Tushar for this! It was extremely helpful!
Also, I have a requirement here that I restrict the number of values selected to 2. What will be the best way to handle that, can you please suggest?
In selected records just add check if size is greater then your limit. Don’t allow user to select more. You can also disable text box to do that.
@ Tushar, Thanks a lot for sharing this functionality with us.
Use Case – I have Project__c Object Contact Object
Now business wants functionality to add multiple Contacts to Project from UI quickly.
My Approach – Created Lightning Action button on Project__c which calls my Screen Flow then added it to Project Page Layout. Now when Flow starts it has Project RecordId to start with, On first flow screen I have added your LWC lwcMultiLookup Component via Master Component (copied Demo App Code you provided in Master Component).
So now user can use the search bar to find contacts and quickly select the ones they would like to associate with Project. All works great until the selection part but I am having trouble with storing the selectedRecords List/String as a variable in my Flow to further run Loop on and create Project_Relationship Records.
Steps Followd –
1. Created the LWC as per your instructions above and also created a master component “lwcMultiLookupRecords” (copied Demo App Code you provided in Master Component) which calls the LWC.
2. Replaced all references from Account to Contact in both Master Component and LWC.
3. Updated lwc/lwcMultiLookup/lwcMultiLookup.js-meta.xml file
Added
3.i. true
3.ii. lightning__FlowScreen
4. on Master Component added implements=”lightning:availableForFlowScreens”
5. For Master Component added design “lwcMultiLookupRecords.design”
4. Added the LWC in Flow -> Screen Element.
Saved the Flow and when I run it lets me select all the contacts i want and adds it under the search bar .. but the Contacts table underneath as shown in your demo app doesnt display the records & I cant seem to store the selectedRecords into variable.
Is it because of selectedRecords Type being SObjectResult[] ?
What changes would you suggest I make so that once i select all the contacts the selectedRecords provides me a Array of Ids or Contact records or String collection variable that i can iterate over using Loop and insert Project_Relationship junction association records (one for each contact against the project where user started the flow)
selectRecId.push(this.selectedRecords[i]); In this line pass the recId instead of complete variable and store that in a collection variable.
I was able to get this working in Flow, grab the resulting selected record, but i’m completely stumped on a test class. Any chance you have one lyin around for this?
I don’t have test class, but its super simple. Just create a dummy account record and pass all parameter value. It will cover. You can use assert to validate list size.
Hello Tushar Sharma,
I was able to get the code working and thanks for it, its a huge help.
But if I am using two drop down-list I am facing issue. Each drop down-list holding two different object’s multi lookup values. When I click on the drop-down of second object’s, it is showing me both the objects drop-downs list separately. I would like to have only one drop-down list displayed when i click on a specific object’s multi lookup.
Please help me on this.
I test the same scenario in my org and it worked fine for me. Are you using exact code because I am unable to reproduce it.
selectRecId.push(this.selectedRecords[i].recId); can you explain l=this line?
Here we are passing the selected record to the list. So that we can keep track of selected records.
but what is the .recid. Also how can use this for User object instead of account
Its wrapper variable. Check the app lwcMultiLookupDemo instead of Account use User.
As you told, we are getting the selected record in the list. One things I want to know from you.
1) how to store the multiple record into the Salesforce. You have used for account, I want for user, I have user lookup field in the object.
Check the DemoApp. Instead of Account pass user object
Do you know how i can use this in creation object phase adding the result in a field( a text field for example saving the result as json)
Hi,
Really liked your blog and was very helpful. But i need one help on this… On refresh the selected items are disappearing. Can you share the code for keeping the records with icons on the UI in LWC.
Thanks
You can pass the details and update it similar of what we are doing in setSelectedRecord method
hi, i can able to search multiple names selection but if i remove one value , It is also saving while save the record which was i removed in field
instead of aura component for parent one . Could you please post the LWC component for passing the event from child lwc to parent lwc component
You can followw same approach. Just catch the event in LWC. Check this for reference: https://newstechnologystuff.com/2019/04/30/pass-data-between-components-using-events-in-lightning-web-components/
Hi, I am getting below on saving an app. Any idea ?
Failed to save lwcMultiLookupDemo.app: :lwcMultiLookupDemo:1,108: Invalid attribute “name”: Source