custom reusable lookup in lwc using sosl

Lightning, Lightning Web Components, Salesforce

Hey there,

Welcome back 🙂 . Here in this blog post, we are going to develop a custom reusable lookup using SOSL.

Step 1 – Create ‘lwcsearchcomponent‘ component and use below code

lwcsearchcomponent.html

<template>
    <lightning-input 
        type="search" 
        value={searckKeyword}
        onchange={handleChange}
        label={searchLabel}
    > </lightning-input>
</template>

lwcsearchcomponent.js

import { LightningElement, api, track } from 'lwc';

export default class Lwcsearchcomponent extends LightningElement {

    @track searckKeyword;
    @api isrequired = 'false';
    @api searchLabel = 'Search Account';
    @api showLabel   = 'true';

    /* Check the isrequired prop is true then set the prop to true*/
    renderedCallback() {
        if ( this.isrequired === "false" )
            return;
        if ( this.isrequired === "true") {
            let picklistInfo = this.template.querySelector('lightning-input');
            picklistInfo.required = true;
            this.isrequired = "false";
        }
        
    }

    handleChange(event) {
        var keyword = event.target.value;
        /* Create & dispatch the event to parent component with the search keyword */
        if ( keyword && keyword.length >= 2 ) {
            let searchEvent = new CustomEvent('search',{
                detail : { value : keyword }
            });
            this.dispatchEvent(searchEvent);
        }
        
    }
}

Step 2 – Create ‘lwcrecordlist‘ component and use below code.

lwcrecordlist.html

<template>
    <li role="presentation" class="slds-listbox__item slds-p-top_small" onclick={handleSelect}>
        <span id="listbox-option-unique-id-01"
            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" title="Icon">
                    <lightning-icon icon-name={iconname} size="small"></lightning-icon>
                    <span class="slds-assistive-text">Icon</span>
                </span>
            </span>
            <span class="slds-media__body">
                <span class="slds-listbox__option-text slds-listbox__option-text_entity">{rec.Name}</span>
            </span>
        </span>
    </li>
</template>

lwcrecordlist.js

import { LightningElement, api } from 'lwc';

export default class Lwcrecordlist extends LightningElement {
    /* Public Property to pass the single record & iconname */
    @api rec;
    @api iconname = 'standard:account';

    handleSelect(  ) {
        let selectEvent = new CustomEvent('select',{
            detail : { selRec : this.rec }
        });
        this.dispatchEvent( selectEvent );
    }

    handleRemove(  ) {
        let selectEvent = new CustomEvent('select',{
            detail : { selRec : undefined }
        });
        this.dispatchEvent( selectEvent );
    }
}

Step 3 – create ‘CustomSearchController‘ apex class & use below code

CustomSearchController.apxc

public with sharing class CustomSearchController {
    
    /* Create an Aura Enabled Method which tooks objectName, fieldtoSearch and the text to search */
    /* Develop the Dynamic SOSL and then use Query method of Search Class to return the result */
    @AuraEnabled
    public static String searchRecords(String objName, String fieldName, 
                                     String searchKey){
        String searchKeyword = searchKey + '*';
        String returningQuery = objName+' ( Id, '+fieldName+')';
        String query = 'FIND :searchKeyword IN ALL FIELDS RETURNING '+returningQuery+' LIMIT 2000';
        List<List<sObject>> sobjectList = Search.query(Query);
        return JSON.serialize(sobjectList);
    }
}

Step 4 – Create ‘customlwclookup‘ component & use below code

customlwclookup.html

<template>
    <div class="slds-m-padding_around">
        <template if:false={selectedRecord} >
            <c-lwcsearchcomponent onsearch={hanldeSearch} search-label={label}
                isrequired="true" ></c-lwcsearchcomponent>
        </template>
    </div>
    <div class="slds-m-padding_around">
        <template if:false={selectedRecord} >
            <template if:true={records}>
                <template for:each={records} for:item="rec" for:index="index">
                    <ul key={rec.Id} 
                        class="slds-listbox " >
                    <c-lwcrecordlist key={rec.Id} rec={rec}
                         iconname={iconname}
                         onselect={handleSelect}>
                    </c-lwcrecordlist>
                    </ul>
                </template>
            </template>
        </template>
        <template if:true={selectedRecord} >
            <label class="slds-form-element__label" for="unique-id-of-input">
                    {label}
            </label>
            <div class="slds-pill-container">
                <lightning-pill class="pillSize" 
                        href="JavaScript:void(0);"
                        label={selectedRecord.Name} 
                        name={selectedRecord.Name} 
                        onremove={handleRemove}>
                    <lightning-icon icon-name={iconname} variant="circle"  
                            alternative-text={objectName}></lightning-icon>        
                </lightning-pill>
            </div>
        </template>
    </div>
</template>

customlwclookup.js

This component is required 4 public property to work the lookup for any object.

  1. objectName
  2. fieldName
  3. iconname
  4. label
  5. parentidfield

See the comments for more information

import { LightningElement, api, track } from 'lwc';
import searchRecords from '@salesforce/apex/CustomSearchController.searchRecords';
export default class Customlwclookup extends LightningElement {

    /* public property */
    /* these public property will be used when using this component inside other component for the lookup functionality */
    /* objectName is the name of the Object which is parent either master-detail or lookup */
    /* fieldName is the field of parent object in which text needs to be searched */
    /* iconname - icon to display in the list and after selection of the record */
    /* label - to show the label for the lookup */
    /* parentidfield - the apiname of lookup/matser-detail in the child object this field is useful to indentify which parent record has been select if there are multiple lookup for a single child record */
    @api objectName = 'Account';
    @api fieldName  = 'Name';
    @api iconname   = 'standard:record';
    @api label      = 'Account';
    @api parentidfield = 'AccountId';

    /* private property */
    @track records;
    @track selectedRecord;

    hanldeSearch(event) {

        var searchVal = event.detail.value;

        searchRecords({
            objName   : this.objectName,
            fieldName : this.fieldName,
            searchKey : searchVal
        })
        .then( data => {
            if ( data ) {
                let parsedResponse = JSON.parse(data);
                let searchRecordList = parsedResponse[0];
                for ( let i=0; i < searchRecordList.length; i++ ) {
                      let record = searchRecordList[i];
                      record.Name = record[this.fieldName];
                }
                //window.console.log(' data ', searchRecordList);
                this.records = searchRecordList;
            }
        })
        .catch( error => {
            window.console.log(' error ', error);
        });
    }

    handleSelect(event) {
        var selectedVal = event.detail.selRec;
        this.selectedRecord =  selectedVal;
        let finalRecEvent = new CustomEvent('select',{
            detail : { selectedRecordId : this.selectedRecord.Id, parentfield : this.parentidfield }
        });
        this.dispatchEvent(finalRecEvent);
    } 

    handleRemove() {
        this.selectedRecord =  undefined;
        this.records = undefined;
        let finalRecEvent = new CustomEvent('select',{
            detail : { selectedRecordId : undefined, parentfield : this.parentidfield }
        });
        this.dispatchEvent(finalRecEvent);
    }
    
}

customlwclookup.css

.pillSize{
    width: 100%;
}

Step 5 – Test the component

Create another component and use the below code for the same. In the below example, I have used the customlwclookup component 3 times. to test the functionality for Account, Case & Contact Object.

<template>
    <lightning-card title="Lookup Demo" icon-name="standard:record">
        <div class="slds-p-around_small">
            <c-customlwclookup object-name="Account" field-name="Name"
                label="Account" parentidfield="AccountId"
                iconname="standard:account" ></c-customlwclookup>
        </div>

        <div class="slds-p-around_small">
                <c-customlwclookup object-name="Case" field-name="Subject"
                    label="Case" parentidfield="Case__c"
                    iconname="standard:case" ></c-customlwclookup>
        </div>

        <div class="slds-p-around_small">
                <c-customlwclookup object-name="Contact" field-name="Name"
                    label="Contact" parentidfield="ContactId"
                    iconname="standard:contact" ></c-customlwclookup>
        </div>
    </lightning-card>
</template>

Use below code for .xml file & after deploying the code into org. You can add this component inside home/record/app page and test the functionality

<?xml version="1.0" encoding="UTF-8"?>
<LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata">
    <apiVersion>47.0</apiVersion>
    <isExposed>true</isExposed>
    <targets>
        <target>lightning__AppPage</target>
        <target>lightning__HomePage</target>
        <target>lightning__RecordPage</target>
        <target>lightning__Tab</target>
    </targets>
</LightningComponentBundle>

Happy Learning 🙂 Keep Sharing 😉

#Salesforce #Trainhead #AskPanther #BeASalesforceChamp

Leave a Reply