How to create a Reusable Custom Lookup Search Component in Lightning Web Component

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>.

/**
* @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;
}
}
Step 2: Create a Lightning Web Component named "customLookupSearchComponent"
  • customLookupSearchComponent.html
<!--
@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
/**
* @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
<!--
@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
/**
* @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
<?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: 
  1. This component works for both standard and custom objects.
  2. You can create, update and delete records using the lookup component as a child component.
  3. 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

Kamal Kant said…
Very Useful and Provides a Dynamic usecase to the scenarios ,
Thanks Ayush