Hello to all learners, today we are going to see how to create a reusable custom lookup search component in LWC.
Step 1: Create an Apex Class and name it "CustomLookupSearchController"
This class has a method named as lookupSearch to search the lookup object records and return List<sObject>.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* @author : Ayush Saxena | |
* @last modified on : 12-01-2022 | |
* @last modified by : Ayush Saxena | |
**/ | |
public with sharing class CustomLookupSearchController { | |
@AuraEnabled(cacheable=true) | |
public static List<sObject> lookUpSearch(String searchValue, string objectLabel) { | |
String queryString = null; | |
if(searchValue == null || searchValue == ''){ | |
queryString = 'Select Id, Name from '+objectLabel+' ORDER BY LastViewedDate LIMIT 5'; | |
} | |
else { | |
queryString = 'Select Id, Name from '+objectLabel+' Where Name Like \'%' + searchValue + '%\' LIMIT 5'; | |
} | |
List<sObject> returnLookUpList = database.query(queryString); | |
return returnLookUpList; | |
} | |
} |
- customLookupSearchComponent.html
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!-- | |
@author : Ayush Saxena | |
@last modified on : 12-01-2022 | |
@last modified by : Ayush Saxena | |
--> | |
<template> | |
<div> | |
<div class="slds-form-element"> | |
<div class="slds-form-element__control"> | |
<div class="slds-combobox_container"> | |
<div id="box" class={Class} aria-expanded="true" aria-haspopup="listbox" role="combobox"> | |
<div class="slds-combobox__form-element slds-input-has-icon slds-input-has-icon_right" role="none"> | |
<template if:true={isValSelect}> | |
<!-- Show Selected value --> | |
<lightning-pill class="pillSize" label={selectedValue} onremove={handleRemove}> | |
<lightning-icon icon-name={iconName}></lightning-icon> | |
</lightning-pill> | |
</template> | |
<template if:false={isValSelect}> | |
<div class="slds-p-top_none"> | |
<!-- Search input box --> | |
<lightning-input class="slds-has-focus" type="search" value={searchValue} placeholder={searchPlaceholder} | |
onchange={handleOnChange} onblur={blurtime} onclick={handleOnClick} variant="label-hidden"></lightning-input> | |
</div> | |
</template> | |
<div id="listbox-id-11" class="slds-dropdown slds-dropdown_fluid" role="listbox"> | |
<ul class="slds-listbox slds-listbox_vertical slds-dropdown_length-5 customClass" tabindex="0" role="presentation"> | |
<template for:each={optionsToDisplay} for:item="option"> | |
<!-- li to display each option in the list --> | |
<li key={option.Id} role="presentation" class="slds-listbox__item" | |
data-label={option.Name} data-name={option.Name} data-id={option.Id} onclick={optionsClickHandler}> | |
<div id={option.Id} class="slds-media slds-listbox__option slds-listbox__option_plain slds-media_small" role="option"> | |
<lightning-icon class="slds-icon slds-icon--small slds-icon-text-default" icon-name={iconName} size="small"></lightning-icon> | |
<span class="slds-p-left_medium slds-truncate"> | |
<span>{option.Name}</span> | |
</span> | |
</div> | |
</li> | |
<!--/ li to display each option in the list --> | |
</template> | |
</ul> | |
</div> | |
</div> | |
</div> | |
</div> | |
</div> | |
</div> | |
</div> | |
</template> |
- customLookupSearchComponent.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* @author : Ayush Saxena | |
* @last modified on : 12-01-2022 | |
* @last modified by : Ayush Saxena | |
**/ | |
import { LightningElement,api,wire } from 'lwc'; | |
import lookUpSearch from '@salesforce/apex/CustomLookupSearchController.lookUpSearch'; | |
export default class CustomLookupSearchComponent extends LightningElement { | |
@api objName; | |
@api iconName; | |
@api searchPlaceholder='Search'; | |
isValSelect; | |
selectedValue; | |
searchValue; | |
optionsToDisplay; | |
blurTimeout; | |
Class = 'slds-combobox slds-dropdown-trigger slds-dropdown-trigger_click slds-has-focus'; | |
@wire(lookUpSearch, {searchValue : '$searchValue', objectLabel : '$objName'}) | |
wiredRecords({ error, data }) { | |
if (data) { | |
this.error = undefined; | |
this.optionsToDisplay = data; | |
} else if (error) { | |
this.error = error; | |
this.optionsToDisplay = undefined; | |
} | |
} | |
blurtime(){ | |
this.blurTimeout = setTimeout(() => {this.Class = 'slds-combobox slds-dropdown-trigger slds-dropdown-trigger_click slds-has-focus'}, 400); | |
} | |
optionsClickHandler(event){ | |
let Id = event.currentTarget.dataset.id; | |
let Name = event.currentTarget.dataset.name; | |
const selectedEvent = new CustomEvent('lookup', { | |
detail: { | |
data : { | |
id : Id, | |
name : Name, | |
} | |
} | |
}); | |
this.dispatchEvent(selectedEvent); | |
this.selectedValue = Name; | |
this.isValSelect = true; | |
if(this.blurTimeout) { | |
clearTimeout(this.blurTimeout); | |
} | |
this.Class = 'slds-combobox slds-dropdown-trigger slds-dropdown-trigger_click slds-has-focus'; | |
} | |
handleOnChange(event){ | |
this.searchValue = event.target.value; | |
} | |
handleOnClick(){ | |
this.searchValue = ''; | |
this.Class = 'slds-combobox slds-dropdown-trigger slds-dropdown-trigger_click slds-has-focus slds-is-open'; | |
} | |
handleRemove(){ | |
this.isValSelect = false; | |
} | |
} |
Now, It's time to test the above component. For that purpose, you can use the above component inside the parent component.
Step 3: Create one more LWC component named "demoCustomLookupSearchComponent".
- demoCustomLookupSearchComponent.html
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!-- | |
@author : Ayush Saxena | |
@last modified on : 12-01-2022 | |
@last modified by : Ayush Saxena | |
--> | |
<template> | |
<lightning-card title="Create New Contact" icon-name="standard:record"> | |
<div class="slds-m-around_medium"> | |
<lightning-input label="Contact First Name" onchange={handleFirstNameChange} class="slds-m-bottom_x-small"></lightning-input> | |
<lightning-input label="Contact Last Name" onchange={handleLastNameChange} class="slds-m-bottom_x-small"></lightning-input> | |
<span class="slds-m-bottom_small">Account Name</span> | |
<c-custom-lookup-search-component label="Account Name" obj-name="Account" icon-name="standard:account" search-placeholder="Search Account" onlookup={handlelookupchange} > </c-custom-lookup-search-component> | |
<div class="slds-m-top_medium" > | |
<lightning-button label="Save" variant="brand" onclick={createContact}></lightning-button> | |
</div> | |
</div> | |
</lightning-card> | |
<div> | |
</div> | |
</template> |
- demoCustomLookupSearchComponent.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* @author : Ayush Saxena | |
* @last modified on : 12-01-2022 | |
* @last modified by : Ayush Saxena | |
**/ | |
import { LightningElement } from 'lwc'; | |
import { createRecord } from 'lightning/uiRecordApi'; | |
import { ShowToastEvent } from 'lightning/platformShowToastEvent'; | |
import CONTACT_OBJECT from '@salesforce/schema/Contact'; | |
import FIRSTNAME_FIELD from '@salesforce/schema/Contact.FirstName'; | |
import LASTNAME_FIELD from '@salesforce/schema/Contact.LastName'; | |
import ACCOUNT_FIELD from '@salesforce/schema/Contact.AccountId' | |
export default class DemoCustomLookupSearchComponent extends LightningElement { | |
firstname; | |
lastname; | |
accountId; | |
contactId; | |
handleFirstNameChange(event){ | |
this.firstname = event.target.value; | |
} | |
handleLastNameChange(event){ | |
this.lastname = event.target.value; | |
} | |
handlelookupchange(event){ | |
this.accountId = event.detail.data.id; | |
} | |
createContact() { | |
const fields = {}; | |
fields[FIRSTNAME_FIELD.fieldApiName] = this.firstname; | |
fields[LASTNAME_FIELD.fieldApiName] = this.lastname; | |
fields[ACCOUNT_FIELD.fieldApiName] = this.accountId; | |
const recordInput = { apiName: CONTACT_OBJECT.objectApiName, fields }; | |
createRecord(recordInput) | |
.then(contact => { | |
this.contactId = contact.id; | |
this.dispatchEvent( | |
new ShowToastEvent({ | |
title: 'Success', | |
message: 'Contact created successfully!', | |
variant: 'success', | |
}), | |
); | |
}) | |
.catch(error => { | |
this.dispatchEvent( | |
new ShowToastEvent({ | |
title: 'Error creating record', | |
message: error.body.message, | |
variant: 'error', | |
}), | |
); | |
}); | |
this.firstname = ''; | |
this.lastname = ''; | |
this.accountId = ''; | |
} | |
} |
- demoCustomLookupSearchComponent.js-meta.xml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?xml version="1.0" encoding="UTF-8"?> | |
<LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata"> | |
<apiVersion>52.0</apiVersion> | |
<isExposed>true</isExposed> | |
<targets> | |
<target>lightning__AppPage</target> | |
<target>lightning__RecordPage</target> | |
<target>lightning__HomePage</target> | |
</targets> | |
</LightningComponentBundle> |
Let's see it in action.
FEATURES:
- This component works for both standard and custom objects.
- You can create, update and delete records using the lookup component as a child component.
- Display recently viewed records when the focus is on input.
REFERENCES:
That's all for today. still, if you have any queries, suggestions, and thoughts on this, or if you want me to write a blog on some custom topic then just make sure to comment down and let me know.
Thank You!!
Comments
Thanks Ayush