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
- Custom Lookup for All Stadard, Custom and External Object
- Ability to Create a New Record from the Lookup Component itself
- Ability to pass the pre selected record Id and Name
- Ability to use under Iteration in a Custom Table or any custom development
- 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
- 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
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
- valueId; – This field holds the Selected Record Id if the parent record is already there
- valueName; – This field holds the Selected Record Name if the parent record is already there
- objName= ‘Account’; – The Parent Object API Name
- iconName= ‘standard:account’; – The Icon which you wanted to display
- labelName; – The Label for the Search
- currentRecordId; – The child record Id if applicable
- placeholder= ‘Search’; – Search Place holder
- fields= [‘Name’]; – The Fields that You wanted to Query
- displayFields= ‘Name, Rating, AccountNumber’; – The Fields that You wanted to use in the Lookup
- onlookup – the custom event which holds the selected record, SelectedRecordId & the child recorded if applicable
- createRecord – This variable accepts the boolean value that controls either to display the new button in the list or Not.
- recordTypeId – Id of the Record Type if need to create a new Record.
- fieldsToCreate – Accepts the list of fields which needs to be displayed on New Record Form
- 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
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.
Hi @Amit, thank you for sharing with us.
It’s beneficial
Thank You 🙂
Man, you nailed it. !!! Awesome !!!
Looking forward to more such reusable components.
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.
Hello,
We have covered the same in the other mode of conversation.
[…] You can get the complete code for the Search Component from here […]
Thanks Amit for sharing 🙂
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
There was a small change in the JavaScript which has been addressed now. Use the updated JS code and it will work as expected.
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
Unfortunately, I can not write the code on behalf of you. You need to debug it at your side and see what is the error. As this code is being used at multiple projects and working fine with small modifications based on the need.
Thanks, I have changed it in my org.
Thanks. It is really good and reusable component.
Just note that I also got the error above. Should fix the code in handleClose function to something like:
detail: {
record : undefined,
recordId : undefined,
currentRecordId : this.currentRecordId
}
What error are you getting? I have tested for all the objects including standard and custom objects and it is working fine.
Simply select record, and then remove the selection by clicking ‘X’
There is no Error from our side.
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
What is your requirement to use inside for each?
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
Here is the Link which will help you.
https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_testing_SOSL.htm
Can we create multi select look up field. If yes please help Me in finding code.
Check this Link
Can we prepopulate a record in this component?
Yes. You made to make a tweak to code according to yourself
how can I create a table with lookfield list at 1 column and 2 column with checkbox. Want to create a record . please help
For this, you need to develop the custom HTML Table
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?
Hello,
You need to use some browser events like onblur, onfocuschange like that and see is that works.
[…] You can get the complete code for the Search Component from here […]