How to create a reusable Custom Lookup In Lightning Web Component Using SOSL?

29
3710

Hello Dear #Trailblazers,

In this blog post, we are doing to learn how we can create reusable Custom Lookup using Lightning Web Component and we will use SOSL to develop the Lightning Web Component

Features

  1. Custom Lookup for All Stadard, Custom and External Object
  2. Ability to Create a New Record from the Lookup Component itself
  3. Ability to pass the pre selected record Id and Name
  4. Ability to use under Iteration in a Custom Table or any custom development
  5. Displays Recently Viewed Record when focus is on input

Step1 – Create an Apex Class and name it “SearchController”

This class will have a method to search the records and return the List<List<sObject>> here is the code for the same

/**
 * @description       :
 * @author            : Amit Singh
 * @group             :
 * @last modified on  : 12-21-2021
 * @last modified by  : Amit Singh
**/
public with sharing class SearchController {
    @AuraEnabled
    public static List<sObject> search(String objectName, List<String> fields, String searchTerm){
        String searchKeyword = searchTerm + '*';
        String returningQuery = '';
        returningQuery = objectName+' ( Id, '+String.join(fields,',')+')';
        String query = 'FIND :searchKeyword IN ALL FIELDS RETURNING '+returningQuery+' LIMIT 20';
        List<List<sObject>> searchRecords = new List<List<sObject>>();
        List<SObject> sobjList = new List<SObject>();
        if(String.isBlank(searchTerm)){
            String soqlQuery = 'SELECT Id, Name, Type, LastViewedDate FROM RecentlyViewed WHERE Type =\''+objectName+'\' ORDER BY LastViewedDate DESC LIMIT 5';
            sobjList = Database.query( soqlQuery );
            searchRecords.add(sobjList);
        }else{
            searchRecords = Search.Query(Query);
        }
        return searchRecords.get(0);
    }

    @AuraEnabled
    public static sObject getRecentlyCreatedRecord(String recordId, List<String> fields, String objectName){
        sObject createdRecord;
        try {
            String query = 'SELECT Id, '+String.join(fields,',')+' FROM '+objectName+' WHERE Id = \''+recordId+'\'';
            List<SObject> sobjList = Database.query( query );
            createdRecord = sobjList.get(0);
        } catch (Exception e) {
            throw new AuraHandledException(e.getMessage());
        }
        return createdRecord;
    }
}
/**
 * @description       : 
 * @author            : Amit Singh
 * @group             : 
 * @last modified on  : 12-29-2021
 * @last modified by  : Amit Singh
**/
@IsTest
public with sharing class SearchControllerTest {

    @IsTest
    public static void searchTest(){
        Account accuntRecord = createAccountRecord();
        List<String> fields = new List<String>();
        fields.add('Name');
        fields.add('Phone');
        fields.add('Rating');
        List<String> searchResultsIds = new List<String>();
        searchResultsIds.add(accuntRecord.Id);
        Test.startTest();
        Test.setFixedSearchResults(searchResultsIds);
        SearchController.search('Account', fields, 'Salesforce');
        Test.stopTest();
    }

    @IsTest
    public static void searchTest1(){
        Account accuntRecord = createAccountRecord();
        List<String> fields = new List<String>();
        fields.add('Name');
        fields.add('Phone');
        fields.add('Rating');
        List<String> searchResultsIds = new List<String>();
        searchResultsIds.add(accuntRecord.Id);
        Test.startTest();
        Test.setFixedSearchResults(searchResultsIds);
        SearchController.search('Account', fields, '');
        Test.stopTest();
    }

    @IsTest
    public static void getRecentlyCreatedRecordTest(){
        Account accuntRecord = createAccountRecord();
        List<String> fields = new List<String>();
        fields.add('Name');
        fields.add('Phone');
        fields.add('Rating');
        String accountId = accuntRecord.Id;
        Test.startTest();
        SObject account = SearchController.getRecentlyCreatedRecord( accountId , fields, 'Account');
        String fetchedAccountId = (String)account.get('Id');
        System.assertEquals( fetchedAccountId , accountId , 'Id is not matching' );
        Test.stopTest();
    }

    private static Account createAccountRecord(){
        Account acc = new Account();
        acc.Name = 'Salesforce.com';
        acc.Rating = 'Hot';
        acc.Industry = 'Technology';
        acc.Description = 'This is a test account';
        acc.BillingCity = 'San Francisco';
        acc.BillingState = 'CA';
        acc.BillingPostalCode = '94105';
        acc.BillingCountry = 'USA';
        acc.Phone = '4158889999';
        acc.Type = 'Customer';
        insert acc;
        return acc;
    }
}

Step2 Create a Lightning Web Component

  1. selectedRecord Component – First, we need to create a Child Component that will display the selected record in the lookup component.

Create an LWC Component and name it selectedRecord and use the code from the below files

<!-- sldsValidatorIgnore -->
<!-- sldsValidatorIgnore -->
<!--
@description :
@author : Amit Singh
@group :
@last modified on : 12-21-2021
@last modified by : Amit Singh
-->
<template>
<div class="slds-form-element">
<label if:false={showLabel} class="slds-form-element__label" for="combobox-id-5" id="combobox-label-id-32">{objectLabel}</label>
<div class="slds-form-element__control">
<div class="slds-combobox_container slds-has-selection">
<div class="slds-combobox slds-dropdown-trigger slds-dropdown-trigger_click">
<div class="slds-combobox__form-element slds-input-has-icon slds-input-has-icon_left-right" role="none">
<span class="slds-icon_container slds-icon-standard-account slds-combobox__input-entity-icon" title={objectLabel}>
<svg class="slds-icon slds-icon_small" aria-hidden="true">
<use xlink:href={iconUrl}></use>
</svg>
<!-- sldsValidatorIgnoreNextLine -->
<span class="slds-assistive-text">Account</span>
<!-- sldsValidatorIgnoreNextLine -->
</span>
<button type="button" class="slds-input_faux slds-combobox__input slds-combobox__input-value" aria-labelledby="combobox-label-id-32 combobox-id-5-selected-value" id="combobox-id-5-selected-value" aria-controls="listbox-id-5" aria-expanded="false"
aria-haspopup="listbox">
<span class="slds-truncate" id="combobox-value-id-19">
{record.FIELD1}
</span>
</button>
<button onclick={handleRemove} class="slds-button slds-button_icon slds-input__icon slds-input__icon_right" title="Remove selected option">
<svg class="slds-button__icon" aria-hidden="true">
<use xlink:href="/apexpages/slds/latest/assets/icons/utility-sprite/svg/symbols.svg#close"></use>
</svg>
<span class="slds-assistive-text">Remove selected option</span>
</button>
</div>
</div>
</div>
</div>
</div>
</template>
/**
* @description : This component is used to display the selected record in the selected record section.
* @author : Amit Singh
* @group :
* @last modified on : 12-21-2021
* @last modified by : Amit Singh
**/
import { LightningElement, api } from 'lwc';
export default class SelectedRecord extends LightningElement {
@api iconUrl;
@api objectLabel;
@api record;
@api index;
@api showLabel = false;
handleRemove = (event) => {
event.preventDefault();
const closeEvent = new CustomEvent('close', {
bubbles : true,
composed : true,
cancelable : true,
detail: {
data : {
record : undefined,
recordId : undefined
}
}
});
this.dispatchEvent(closeEvent);
}
}

2. searchComponent

This is the main component that will have complete functionality for the lookup. This component has below main public property which is important to use

  1. valueId; – This field holds the Selected Record Id if the parent record is already there
  2. valueName; – This field holds the Selected Record Name if the parent record is already there
  3. objName= ‘Account’; – The Parent Object API Name
  4. iconName= ‘standard:account’; – The Icon which you wanted to display
  5. labelName; – The Label for the Search
  6. currentRecordId; – The child record Id if applicable
  7. placeholder= ‘Search’; – Search Place holder
  8. fields= [‘Name’]; – The Fields that You wanted to Query
  9. displayFields= ‘Name, Rating, AccountNumber’; – The Fields that You wanted to use in the Lookup
  10. onlookup – the custom event which holds the selected record, SelectedRecordId & the child recorded if applicable
  11. createRecord – This variable accepts the boolean value that controls either to display the new button in the list or Not.
  12. recordTypeId – Id of the Record Type if need to create a new Record.
  13. fieldsToCreate – Accepts the list of fields which needs to be displayed on New Record Form
  14. index – The Row no if the Lookup component is used inside the Custom data tables

Usage

Below is the code on how you can use this component inside your parent component.

<c-search-component
                obj-name={typeAttributes.object}
                icon-name={typeAttributes.icon}
                label-name={typeAttributes.label}
                placeholder={typeAttributes.placeholder} 
                fields={typeAttributes.fields}
                display-fields={typeAttributes.displayFields}
                value-id={typeAttributes.valueId}
                value-name={typeAttributes.valueName}
                onlookup={handleLookup}
                fields-to-create={fieldsToCreate} 
                index={index}
                create-record=false
                current-record-id={typeAttributes.currentRecordId} >
</c-search-component>

You can find the complete Code here

<!--
@File Name : demo.html
@Description :
@Author : A Singh
@Group :
@Last Modified By : Amit Singh
@Last Modified On : 12-29-2021
@Modification Log :
Ver Date Author Modification
1.0 6/7/2020 A Singh Initial Version
-->
<template>
<lightning-card variant="Narrow" title="Lookup Demo" icon-name="standard:record">
<div class="slds-var-m-around_small">
<p>Search Component</p>
<c-search-component
onlookup={handleLookup}
fields-to-create={fieldsToCreate}
create-record=true
parent-a-p-i-name="ParentId"
obj-name="Account"
display-ields="Name, Rating, AccountNumber"
fields = {fields}
show-label=true
label-name="Account Name"
data-index={index}
index={index}>
</c-search-component>
</div>
<div class="slds-var-m-around_small">
<p>Lookup Component With Pre Selcted </p>
<c-search-component
onlookup={handleLookup}
fields-to-create={fieldsToCreate}
create-record=false
data-index={index}
parent-a-p-i-name="ParentId"
obj-name="Account"
display-ields="Name, Rating, AccountNumber"
show-label=true
label-name="Account Name"
fields = {fields}
value-id="001sdf4424"
value-name="Salesforce.org"
index={index}>
</c-search-component>
</div>
</lightning-card>
</template>
view raw Demo.html hosted with ❤ by GitHub
/**
* @description :
* @author : Amit Singh
* @group :
* @last modified on : 12-29-2021
* @last modified by : Amit Singh
**/
import { LightningElement } from 'lwc';
export default class Lookupdemol extends LightningElement {
fieldsToCreate = ['Name','Rating','Phone','Industry']
fields = ['Name'];
handleLookup = (event) => {
let data = event.detail.data;
if(data && data.record){
// populate the selected record in the correct parent Id field
// this.allRecord[data.index][data.parentAPIName] = data.record.Id;
}else{
// clear the parent Id field
//this.allRecord[data.index][data.parentAPIName] = undefined;
}
}
}
view raw demo.js hosted with ❤ by GitHub
<!-- sldsValidatorIgnore -->
<!--
@description :
@author : Amit Singh
@group :
@last modified on : 12-29-2021
@last modified by : Amit Singh
Modifications Log
Ver Date Author Modification
1.0 12-20-2020 Amit Singh Initial Version
-->
<template>
<lightning-spinner if:true={isLoading} alternative-text="Loading" size="small"></lightning-spinner>
<div class="slds-form-element">
<template if:true={showLabel}>
<label class="slds-form-element__label" for="combobox-id-1">{labelName}</label>
</template>
<!-- Display the create new record modal -->
<div if:true={showModal}>
<section if:true={showModal} role="dialog" tabindex="-1" aria-labelledby="modal-heading-01" aria-modal="true"
aria-describedby="modal-content-id-1" class="slds-modal slds-fade-in-open">
<div class="slds-modal__container">
<header class="slds-modal__header">
<button onclick={handleCancel} class="slds-button slds-button_icon slds-modal__close slds-button_icon-inverse" title="Close">
<svg class="slds-button__icon slds-button__icon_large" aria-hidden="true">
<use xlink:href={ICON_URL_CLOSE}></use>
</svg>
<span class="slds-assistive-text">Close</span>
</button>
<h2 id="modal-heading-01" class="slds-modal__title slds-hyphenate">Add New Record</h2>
</header>
<div class="slds-modal__content slds-p-around_medium" id="modal-content-id-1">
<lightning-record-form
object-api-name={objName}
record-type-id={recordTypeId}
oncancel={handleCancel}
fields={fieldsToCreate}
onsuccess={handleSuccess}
columns="2">
</lightning-record-form>
</div>
</div>
</section>
<div class="slds-backdrop slds-backdrop_open"></div>
</div>
<div class="slds-form-element__control">
<div class="slds-combobox_container">
<div class="slds-combobox slds-dropdown-trigger slds-dropdown-trigger_click" aria-expanded="false" aria-haspopup="listbox" role="combobox">
<template if:false={selectedRecord}>
<div class="slds-combobox__form-element slds-input-has-icon slds-input-has-icon_right" role="none">
<input type="text" onfocus={handleInputChange} onchange={handleInputChange} onkeyup={handleInputChange} onkeydown={handleInputChange}
class="slds-input slds-combobox__input"
id="combobox-id-1" aria-autocomplete="list" aria-controls="listbox-id-1" autocomplete="off"
role="textbox" placeholder={placeholder} />
<span class="slds-icon_container slds-icon-utility-search slds-input__icon slds-input__icon_right">
<svg class="slds-icon slds-icon slds-icon_x-small slds-icon-text-default" aria-hidden="true">
<use xlink:href="/apexpages/slds/latest/assets/icons/utility-sprite/svg/symbols.svg#search"></use>
</svg>
</span>
</div>
<div style="background: white;" id="listbox-id-1" class="slds-dropdown_length-with-icon-7 slds-dropdown_fluid" role="listbox">
<ul class="slds-listbox slds-listbox_vertical" role="presentation">
<li onclick={handleNewRecord} if:true={showButton} role="presentation" class="slds-listbox__item">
<div class="slds-media slds-listbox__option slds-listbox__option_entity slds-listbox__option_has-meta" role="option">
<span class="slds-media__figure slds-listbox__option-icon">
<span class="slds-icon_container slds-icon-standard-account">
<svg class="slds-icon slds-icon_small" aria-hidden="true">
<use xlink:href={ICON_URL_NEW}></use>
</svg>
</span>
</span>
<span class="slds-media__body">
<span class="slds-listbox__option-text slds-listbox__option-text_entity">
New {objectLabel}
</span>
</span>
</div>
</li>
<template if:true={searchRecords} for:each={searchRecords} for:item="record" for:index="index">
<li onclick={handleSelect} role="presentation" class="slds-listbox__item" data-record-id={record.Id} key={record.Id}>
<div data-id={record.Id} class="slds-media slds-listbox__option slds-listbox__option_entity slds-listbox__option_has-meta" role="option">
<span class="slds-media__figure slds-listbox__option-icon">
<span class="slds-icon_container slds-icon-standard-account">
<svg class="slds-icon slds-icon_small" aria-hidden="true">
<use xlink:href={ICON_URL}></use>0
</svg>
</span>
</span>
<span class="slds-media__body">
<span class="slds-listbox__option-text slds-listbox__option-text_entity">
{record.FIELD1}
</span>
<span class="slds-listbox__option-meta slds-listbox__option-meta_entity">
{objectLabel} • {record.FIELD2} &nbsp; {record.FIELD3}
</span>
</span>
</div>
</li>
</template>
</ul>
</div>
</template>
<template if:true={selectedRecord}>
<c-selected-record icon-url={ICON_URL}
record={selectedRecord}
onclose={handleClose}
show-label=false
index={index} object-label={objectLabel} >
</c-selected-record>
</template>
</div>
</div>
</div>
</div>
</template>
/**
* @description :
* @author : Amit Singh
* @group :
* @last modified on : 12-29-2021
* @last modified by : Amit Singh
* Modifications Log
* Ver Date Author Modification
* 1.0 12-19-2020 Amit Singh Initial Version
**/
import { LightningElement, api, track, wire } from 'lwc';
import search from '@salesforce/apex/SearchController.search';
import getRecentlyCreatedRecord from '@salesforce/apex/SearchController.getRecentlyCreatedRecord';
const DELAY = 10;
import { NavigationMixin } from 'lightning/navigation';
export default class SearchComponent extends NavigationMixin(LightningElement) {
/* values for an existing selected record */
@api valueId;
@api valueName;
@api objName = 'Account';
@api iconName = 'standard:account';
@api labelName;
@api currentRecordId;
@api placeholder = 'Search';
@api fields = ['Name'];
@api displayFields = 'Name, Rating, AccountNumber';
@api showLabel = false;
@api parentAPIName = 'ParentId';
@api createRecord = false;
/* values to be passed to create the new record */
@api recordTypeId;
@api fieldsToCreate = [];
/* Create fields for using in Datatable for Multiple In-line Edit */
@api index;
@track error;
searchTerm;
delayTimeout;
searchRecords;
selectedRecord;
objectLabel;
isLoading = false;
showButton = false;
showModal = false;
field;
field1;
field2;
ICON_URL = '/apexpages/slds/latest/assets/icons/{0}-sprite/svg/symbols.svg#{1}';
ICON_URL_NEW = '/apexpages/slds/latest/assets/icons/utility-sprite/svg/symbols.svg#add';
ICON_URL_CLOSE = '/apexpages/slds/latest/assets/icons/utility-sprite/svg/symbols.svg#close';
connectedCallback(){
let icons = this.iconName.split(':');
this.ICON_URL = this.ICON_URL.replace('{0}',icons[0]);
this.ICON_URL = this.ICON_URL.replace('{1}',icons[1]);
if(this.objName.includes('__c')){
let obj = this.objName.substring(0, this.objName.length-3);
this.objectLabel = obj.replaceAll('_',' ');
}else{
this.objectLabel = this.objName;
}
if( this.valueId && this.valueName ){
this.selectedRecord = {
FIELD1 : this.valueName,
Id : this.valueId
}
}
this.objectLabel = this.titleCase(this.objectLabel);
let fieldList;
if( !Array.isArray(this.displayFields)){
fieldList = this.displayFields.split(',');
}else{
fieldList = this.displayFields;
}
if(fieldList.length > 1){
this.field = fieldList[0].trim();
this.field1 = fieldList[1].trim();
}
if(fieldList.length > 2){
this.field2 = fieldList[2].trim();
}
let combinedFields = [];
fieldList.forEach(field => {
if( !this.fields.includes(field.trim()) ){
combinedFields.push( field.trim() );
}
});
this.fields = combinedFields.concat( JSON.parse(JSON.stringify(this.fields)) );
if(this.valueId && this.valueName){
this.selectedRecord = {
FIELD1 : this.valueName,
recordId : this.valueId
}
}
}
handleInputChange(event){
window.clearTimeout(this.delayTimeout);
const searchKey = event.target.value;
//this.isLoading = true;
this.delayTimeout = setTimeout(() => {
//if(searchKey.length >= 2){
search({
objectName : this.objName,
fields : this.fields,
searchTerm : searchKey
})
.then(result => {
let stringResult = JSON.stringify(result);
let allResult = JSON.parse(stringResult);
allResult.forEach( record => {
record.FIELD1 = record[this.field];
record.FIELD2 = record[this.field1];
if( this.field2 ){
record.FIELD3 = record[this.field2];
}else{
record.FIELD3 = '';
}
});
this.searchRecords = allResult;
})
.catch(error => {
console.error('Error:', error);
})
.finally( ()=>{
this.showButton = this.createRecord;
});
//}
}, DELAY);
}
handleSelect(event){
let recordId = event.currentTarget.dataset.recordId;
let selectRecord = this.searchRecords.find((item) => {
return item.Id === recordId;
});
this.selectedRecord = selectRecord;
const selectedEvent = new CustomEvent('lookup', {
bubbles : true,
composed : true,
cancelable : true,
detail: {
data : {
record : selectRecord,
recordId : recordId,
currentRecordId : this.currentRecordId,
parentAPIName : this.parentAPIName,
index : this.index
}
}
});
this.dispatchEvent(selectedEvent);
}
handleClose(){
this.selectedRecord = undefined;
this.searchRecords = undefined;
this.showButton = false;
const selectedEvent = new CustomEvent('lookup', {
bubbles : true,
composed : true,
cancelable : true,
detail: {
data : {
record : undefined,
recordId : undefined,
currentRecordId : this.currentRecordId,
parentAPIName : this.parentAPIName,
index : this.index
}
}
});
this.dispatchEvent(selectedEvent);
}
titleCase(string) {
var sentence = string.toLowerCase().split(" ");
for(var i = 0; i< sentence.length; i++){
sentence[i] = sentence[i][0].toUpperCase() + sentence[i].slice(1);
}
return sentence;
}
handleNewRecord = event => {
event.preventDefault();
this.showModal = true;
}
handleCancel = event => {
event.preventDefault();
this.showModal = false;
}
handleSuccess = event => {
event.preventDefault();
this.showModal = false;
let recordId = event.detail.id;
this.hanleCreatedRecord(recordId);
}
hanleCreatedRecord = (recordId) => {
getRecentlyCreatedRecord({
recordId : recordId,
fields : this.fields,
objectName : this.objName
})
.then(result => {
if(result){
this.selectedRecord = {
FIELD1 : result[this.field],
Id : recordId
};
const selectedEvent = new CustomEvent('lookup', {
bubbles : true,
composed : true,
cancelable : true,
detail: {
data : {
record : this.selectedRecord,
recordId : recordId,
currentRecordId : this.currentRecordId,
parentAPIName : this.parentAPIName,
index : this.index
}
}
});
this.dispatchEvent(selectedEvent);
}
})
.catch(error => {
console.error('Error: \n ', error);
})
.finally( ()=>{
this.showModal = false;
});
}
}
/**
* @description :
* @author : Amit Singh
* @group :
* @last modified on : 12-21-2021
* @last modified by : Amit Singh
**/
public with sharing class SearchController {
@AuraEnabled
public static List<sObject> search(String objectName, List<String> fields, String searchTerm){
String searchKeyword = searchTerm + '*';
String returningQuery = '';
returningQuery = objectName+' ( Id, '+String.join(fields,',')+')';
String query = 'FIND :searchKeyword IN ALL FIELDS RETURNING '+returningQuery+' LIMIT 20';
List<List<sObject>> searchRecords = new List<List<sObject>>();
List<SObject> sobjList = new List<SObject>();
if(String.isBlank(searchTerm)){
String soqlQuery = 'SELECT Id, Name, Type, LastViewedDate FROM RecentlyViewed WHERE Type =\''+objectName+'\' ORDER BY LastViewedDate DESC LIMIT 5';
sobjList = Database.query( soqlQuery );
searchRecords.add(sobjList);
}else{
searchRecords = Search.Query(Query);
}
return searchRecords.get(0);
}
@AuraEnabled
public static sObject getRecentlyCreatedRecord(String recordId, List<String> fields, String objectName){
sObject createdRecord;
try {
String query = 'SELECT Id, '+String.join(fields,',')+' FROM '+objectName+' WHERE Id = \''+recordId+'\'';
List<SObject> sobjList = Database.query( query );
createdRecord = sobjList.get(0);
} catch (Exception e) {
throw new AuraHandledException(e.getMessage());
}
return createdRecord;
}
}
/**
* @description :
* @author : Amit Singh
* @group :
* @last modified on : 12-29-2021
* @last modified by : Amit Singh
**/
@IsTest
public with sharing class SearchControllerTest {
@IsTest
public static void searchTest(){
Account accuntRecord = createAccountRecord();
List<String> fields = new List<String>();
fields.add('Name');
fields.add('Phone');
fields.add('Rating');
List<String> searchResultsIds = new List<String>();
searchResultsIds.add(accuntRecord.Id);
Test.startTest();
Test.setFixedSearchResults(searchResultsIds);
SearchController.search('Account', fields, 'Salesforce');
Test.stopTest();
}
@IsTest
public static void searchTest1(){
Account accuntRecord = createAccountRecord();
List<String> fields = new List<String>();
fields.add('Name');
fields.add('Phone');
fields.add('Rating');
List<String> searchResultsIds = new List<String>();
searchResultsIds.add(accuntRecord.Id);
Test.startTest();
Test.setFixedSearchResults(searchResultsIds);
SearchController.search('Account', fields, '');
Test.stopTest();
}
@IsTest
public static void getRecentlyCreatedRecordTest(){
Account accuntRecord = createAccountRecord();
List<String> fields = new List<String>();
fields.add('Name');
fields.add('Phone');
fields.add('Rating');
String accountId = accuntRecord.Id;
Test.startTest();
SObject account = SearchController.getRecentlyCreatedRecord( accountId , fields, 'Account');
String fetchedAccountId = (String)account.get('Id');
System.assertEquals( fetchedAccountId , accountId , 'Id is not matching' );
Test.stopTest();
}
private static Account createAccountRecord(){
Account acc = new Account();
acc.Name = 'Salesforce.com';
acc.Rating = 'Hot';
acc.Industry = 'Technology';
acc.Description = 'This is a test account';
acc.BillingCity = 'San Francisco';
acc.BillingState = 'CA';
acc.BillingPostalCode = '94105';
acc.BillingCountry = 'USA';
acc.Phone = '4158889999';
acc.Type = 'Customer';
insert acc;
return acc;
}
}
<!-- sldsValidatorIgnore -->
<!-- sldsValidatorIgnore -->
<!--
@description :
@author : Amit Singh
@group :
@last modified on : 12-21-2021
@last modified by : Amit Singh
-->
<template>
<div class="slds-form-element">
<label if:false={showLabel} class="slds-form-element__label" for="combobox-id-5" id="combobox-label-id-32">{objectLabel}</label>
<div class="slds-form-element__control">
<div class="slds-combobox_container slds-has-selection">
<div class="slds-combobox slds-dropdown-trigger slds-dropdown-trigger_click">
<div class="slds-combobox__form-element slds-input-has-icon slds-input-has-icon_left-right" role="none">
<span class="slds-icon_container slds-icon-standard-account slds-combobox__input-entity-icon" title={objectLabel}>
<svg class="slds-icon slds-icon_small" aria-hidden="true">
<use xlink:href={iconUrl}></use>
</svg>
<!-- sldsValidatorIgnoreNextLine -->
<span class="slds-assistive-text">Account</span>
<!-- sldsValidatorIgnoreNextLine -->
</span>
<button type="button" class="slds-input_faux slds-combobox__input slds-combobox__input-value" aria-labelledby="combobox-label-id-32 combobox-id-5-selected-value" id="combobox-id-5-selected-value" aria-controls="listbox-id-5" aria-expanded="false"
aria-haspopup="listbox">
<span class="slds-truncate" id="combobox-value-id-19">
{record.FIELD1}
</span>
</button>
<button onclick={handleRemove} class="slds-button slds-button_icon slds-input__icon slds-input__icon_right" title="Remove selected option">
<svg class="slds-button__icon" aria-hidden="true">
<use xlink:href="/apexpages/slds/latest/assets/icons/utility-sprite/svg/symbols.svg#close"></use>
</svg>
<span class="slds-assistive-text">Remove selected option</span>
</button>
</div>
</div>
</div>
</div>
</div>
</template>
/**
* @description : This component is used to display the selected record in the selected record section.
* @author : Amit Singh
* @group :
* @last modified on : 12-21-2021
* @last modified by : Amit Singh
**/
import { LightningElement, api } from 'lwc';
export default class SelectedRecord extends LightningElement {
@api iconUrl;
@api objectLabel;
@api record;
@api index;
@api showLabel = false;
handleRemove = (event) => {
event.preventDefault();
const closeEvent = new CustomEvent('close', {
bubbles : true,
composed : true,
cancelable : true,
detail: {
data : {
record : undefined,
recordId : undefined
}
}
});
this.dispatchEvent(closeEvent);
}
}

Step3 – Demo Time

Create a Lightning Web Component and use the SearchComponent that you have created

<!--
    @description       : 
    @author            : Amit Singh
    @group             : 
    @last modified on  : 03-24-2021
    @last modified by  : Amit Singh
    Modifications Log 
    Ver   Date         Author       Modification
    1.0   03-24-2021   Amit Singh   Initial Version
-->
<template>
    <lightning-card  variant="Narrow"  title="Custom Lookup" icon-name="standard:account">
        <p class="slds-p-horizontal_small">
            <c-search-component
                obj-name="Contact"
                icon-name="standard:contact"
                label-name="Contact"
                placeholder="Search" 
                fields={fields}
                display-fields={displayFields}
                onlookup={handleLookup} >
            </c-search-component>
        </p>
        
    </lightning-card>
</template>
/**
 * @description       : 
 * @author            : Amit Singh
 * @group             : 
 * @last modified on  : 03-24-2021
 * @last modified by  : Amit Singh
 * Modifications Log 
 * Ver   Date         Author       Modification
 * 1.0   03-24-2021   Amit Singh   Initial Version
**/
import { LightningElement } from 'lwc';

export default class CustomLookup extends LightningElement {
    fields = ["Name","Email","Phone"];
    displayFields = 'Name, Email, Phone'

    handleLookup(event){
        console.log( JSON.stringify ( event.detail) )
    }
}

Once you select any record below is the JSON that you get using event.detail

{
  "data": {
    "record": {
      "Id": "0034x000004r7FvAAI",
      "Name": "Ashley James",
      "Email": "ajames@uog.com",
      "Phone": "+44 191 4956203",
      "FIELD1": "Ashley James",
      "FIELD2": "ajames@uog.com",
      "FIELD3": "+44 191 4956203"
    },
    "recordId": "0034x000004r7FvAAI"
  }
}

Thanks for reading 🙂

#Salesforce #DeveloperGeeks #Trailblazer

Please do like share subscribe to the blog post and YouTube channel.

29 COMMENTS

  1. Hi Amit,

    This is awesome. Thank you very much. I recently purchased your Udemy course and I just love it. Just a quick question.

    How can I prepopulate a record in the Search component? For example I’m using the Search component inside another component and if a certain account exists in the org I want to have it selected when I launch the component otherwise I want to be able to search for an account. Is that possible?

    Thanks in advance.

  2. Hi Amit, while using custom object(__c), I am getting error as undefined property to lowercase. Can u pls provide custom object with custom fields code in the same way.Thanks

  3. Hi Thanku so much, but if we are removing one selected record and typing another input..getting error as record id not defined. Please help

  4. Hi Amit,

    Using this inside a for:each, whenever I try to open the Lookup component on a specific record, it opens the component in all of the records of the for:each record List.

    Do you know how can this be avoided?

    Thank you

  5. This was awesome – thank you so much.
    Do you possibly have the test class you used for the Search Component? I’m fairly new to dev and I cannot get my test to pass so this would be a good learning opportunity for me if you could post it

  6. how can I create a table with lookfield list at 1 column and 2 column with checkbox. Want to create a record . please help

  7. When I’m using this inside an Aura component the dropdown doesn’t close when clicked outside. Can you tell me how to fix this?

LEAVE A REPLY

Please enter your comment!
Please enter your name here