Sunday, April 26, 2020

Lightning Web Components: Custom Reusable Lookup Field

How to create custom reusable lookup field in LWC? Lets figure out.

Component Bundles:
  1. lookupFieldComponentLWC (Parent Comp)
  2. lookupResultComponentLWC (Child Comp)
  3. ObjectManager (Apex Class)

** components are created as a part of proof of concept. it can be modified and optimized as per requirement.**

References:

Demo:



Code:


lookupFieldComponentLWC.html

<template>
    <lightning-card title="Custom Reusable Lookup Field">
        <lightning-layout>
            <lightning-layout-item>
                <template if:false={selectedValue}>
                    <lightning-input type="text" placeholder="Input text.." onchange={inputTextHandler}></lightning-input>
                    <template if:true={searchKey}>
                        <template if:true={allRecords}>
                            <template for:each={allRecords} for:item= "record">
                                <c-lookup-result-component-l-w-c key={record.Id} record={record} icon-name={iconName} onselect={handleSelect}></c-lookup-result-component-l-w-c>
                            </template>
                        </template>
                    </template>
                </template>
                <template if:true={selectedValue}>
                    <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="Account">
                            <lightning-icon icon-name={iconName} ></lightning-icon>
                        </span>
                        <input class="slds-input slds-combobox__input
                            slds-combobox__input-value" 
                            id="combobox-id-5" aria-controls="listbox-id-5" 
                            autocomplete="off" role="textbox" type="text" 
                            placeholder="Select an Option" readonly=""
                            value={selectedValue.Name}
                            disabled
                            />
                        <button class="sicon_container slds-button slds-button_icon 
                                    slds-input__icon slds-input__icon_right" 
                                title="Remove selected option"
                                onclick={handleRemove}>
                            <lightning-icon icon-name="utility:close" size="small">

                            </lightning-icon>
                            <span class="slds-assistive-text">Remove selected option</span>
                        </button>
                    </div>
                </template>
            </lightning-layout-item>
        </lightning-layout>
    </lightning-card>
</template>

--------------------------------------------------------------------------------------------------------------------------

lookupFieldComponentLWC.js

import { LightningElement, api, track } from 'lwc';
import getAllRecords from '@salesforce/apex/ObjectManager.getRecords';
import { ShowToastEvent } from 'lightning/platformShowToastEvent';

export default class LookupFieldComponentLWC extends LightningElement {

    @track searchKey;
    @track allRecords = [];
    @track recordId;
    @track selectedValue;
    @api objectName = 'Account'; //to reuse for other object change the object here
    @api iconName = 'standard:account'; //use the respective object icon
    @api searchField= 'Name'; // use the field for search

    inputTextHandler(event){
        this.searchKey= event.detail.value;
        console.log('searchKey>>'+this.searchKey);
        if(this.searchKey){
            this.getRecords();
        }
    }

    getRecords(){
        getAllRecords({searchKey : this.searchKey, objectName : this.objectName, searchField : this.searchField}).then((allRecords) =>{
            this.allRecords = allRecords;
            console.log('allRecords>>>'+this.allRecords[0]);
            for(let i=0;i<this.allRecords.length;i++){
                const rec = this.allRecords[i];
                this.allRecords[i].Name= rec[this.searchField];
            }
        }).catch((error)=>{
            this.showToast('ERROR',error.body.message, 'error');
        })
    }

    handleSelect(event){
        this.recordId = event.detail; //selected recordId
        this.selectedValue = this.allRecords.find(record=> record.Id === this.recordId); //selected record
        console.log('this.recordId>>>'+this.recordId);
        
    }

    handleRemove(){
        this.selectedValue = undefined;
        this.allRecords = undefined;
    }

    showToast(title, message, variant) {
        const evt = new ShowToastEvent({
            title: title,
            message: message,
            variant: variant,
        });
        this.dispatchEvent(evt);
    }
}

--------------------------------------------------------------------------------------------------------------------------

lookupResultComponentLWC.html

<template>
    <div >
        <div class="slds-grid slds-wrap 
                        slds-dropdown_length-with-icon-7 
                        slds-dropdown_fluid
                        slds-p-left_small">
                <div class="slds-col slds-size_4-of-4 ">
                    <ul class="slds-listbox slds-listbox_vertical" role="presentation">
                        <li role="presentation" class="slds-listbox__item">
                            <div class="slds-media slds-listbox__option 
                                                        slds-listbox__option_entity 
                                                        slds-listbox__option_has-meta" 
                                                        role="option"
                                onclick={handleSelect}>
                                <span class="slds-media__figure slds-listbox__option-icon">
                                    <lightning-icon icon-name={iconName} size="small"></lightning-icon>
                                </span>
                                <span class="slds-media__body" 
                                    style="padding-top: 9px;font-weight: 600;">
                                    <span class="slds-listbox__option-text slds-listbox__option-text_entity">
                                        {record.Name}
                                    </span>
                                </span>
                            </div>
                        </li>
                    </ul>
                </div>
        </div>
    </div>
</template>

--------------------------------------------------------------------------------------------------------------------------

lookupResultComponentLWC.js

import { LightningElement, api } from 'lwc';

export default class LookupResultComponentLWC extends LightningElement {

    @api record;
    @api iconName;

    handleSelect(event){
        event.preventDefault();
        const selectedRow = new CustomEvent('select',{detail : this.record.Id});
        this.dispatchEvent(selectedRow);
    }
}

--------------------------------------------------------------------------------------------------------------------------

ObjectManager.cls

public with sharing class ObjectManager {

    @AuraEnabled
    public static List<SObject> getRecords(String searchKey, String objectName, String searchField){
        String key = '%' + searchKey + '%';
        String QUERY = 'Select Id, '+searchField+' From '+objectName +' Where '+searchField +' LIKE :key';
        System.debug(System.LoggingLevel.DEBUG, QUERY);
        List<SObject> sObjectList = Database.query(QUERY);
        return sObjectList;
    }
}