Custom Lookup in Lightning Web Components

In Salesforce, we use lookup field to connect objects. We can use inputField to show them but sometimes we need our own custom lookup. Previously I have created Lookup component in Lightning which you can check here. So today I made that Lightning Web Components (LWC) compatible, and made a custom Lookup in Lightning Web Component (LWC).

This is how our output will look using Lightning Web Component. UI is similar in both Lightning and Lightning Web Component.

Custom Lookup in Lightning Web Components

So we will now check the code part.

lwcCustomLookup.html

<template>
    <lightning-card>
        <h3 slot="title">
            <lightning-icon icon-name="utility:connected_apps" size="small"></lightning-icon>
            Custom Lookup in Lightning Web Components
        </h3>
        <div slot="footer">
                
        </div>
        <div>
            <div class="slds-form-element">
                
                <div class="slds-form-element__control">
                    <div class="slds-combobox_container">
                        
                        <div class={txtclassname} data-id="resultBox" aria-expanded="false" aria-haspopup="listbox" role="combobox">
                            <div class="slds-form-element__control slds-input-has-icon slds-input-has-icon slds-input-has-icon_left-right" role="none">
                                
                                <div>
                                    <span class="slds-icon_container slds-icon-utility-search slds-input__icon iconheight">
                                        <lightning-icon class="slds-icon slds-icon slds-icon_small slds-icon-text-default" icon-name={iconName} size="x-small" alternative-text="icon" ></lightning-icon>
                                    </span> 
                                </div>
                                <lightning-input required={required} read-only={inputReadOnly} data-id="userinput" label={Label} name="searchText" onchange={searchField} value={selectRecordName} class="leftspace"></lightning-input>
                                
                                <div if:true={iconFlag}>
                                    <span class="slds-icon_container slds-icon-utility-search slds-input__icon slds-input__icon_right iconheight">
                                        <lightning-icon class="slds-icon slds-icon slds-icon_small slds-icon-text-default" icon-name="utility:search" size="x-small" alternative-text="icon" ></lightning-icon>
                                    </span> 
                                </div>
                                <div if:true={clearIconFlag}>
                                    <button class="slds-input__icon slds-input__icon_right slds-button slds-button_icon iconheight" onclick={resetData}>
                                        <lightning-icon class="slds-icon slds-icon slds-icon_small slds-icon-text-default" icon-name="utility:clear" size="x-small" alternative-text="icon" ></lightning-icon>
                                        <span class="slds-assistive-text">Clear</span></button>
                                </div>
                            </div>
                        
                            <!-- Second part display result -->
                            <div id="listbox-id-1" class="slds-dropdown slds-dropdown_length-with-icon-7 slds-dropdown_fluid" role="listbox">
                                <ul class="slds-listbox slds-listbox_vertical" role="presentation">
                                    <template for:each={searchRecords} for:item="serecord">
                                        <li role="presentation" class="slds-listbox__item" key={serecord.recId}>
                                            
                                            <div data-id={serecord.recId} data-name={serecord.recName} onclick={setSelectedRecord} 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 slds-icon-standard-account">
                                                        <lightning-icon icon-name={iconName} class="slds-icon slds-icon slds-icon_small slds-icon-text-default" size="x-small"></lightning-icon>
                                                    </span>
                                                </span>
                                                <span class="slds-media__body">
                                                    <span class="slds-listbox__option-text slds-listbox__option-text_entity">{serecord.recName}</span>
                                                    <span class="slds-listbox__option-meta slds-listbox__option-meta_entity">{objectName} • {serecord.recName}</span>
                                                </span>
                                            </div>
                                        </li>
                                    </template>
                                </ul>
                            </div> 
                            <div if:true={messageFlag}>
                                No result found.
                            </div>
                            <div if:true={LoadingText}>
                                Loading...
                            </div>
                        </div>
                    </div>
                </div>
            </div>
        </div>
    </lightning-card>
</template>

lwcCustomLookup.js

import { LightningElement,wire,api,track } from 'lwc';
import getResults from '@salesforce/apex/lwcCustomLookupController.getResults';

export default class LwcCustomLookup extends LightningElement {
    @api objectName = 'Account';
    @api fieldName = 'Name';
    @api selectRecordId = '';
    @api selectRecordName;
    @api Label;
    @api searchRecords = [];
    @api required = false;
    @api iconName = 'action:new_account'
    @api LoadingText = false;
    @track txtclassname = 'slds-combobox slds-dropdown-trigger slds-dropdown-trigger_click';
    @track messageFlag = false;
    @track iconFlag =  true;
    @track clearIconFlag = false;
    @track inputReadOnly = false;
   

    searchField(event) {
        var currentText = event.target.value;
        this.LoadingText = true;
        
        getResults({ ObjectName: this.objectName, fieldName: this.fieldName, value: currentText  })
        .then(result => {
            this.searchRecords= result;
            this.LoadingText = false;
            
            this.txtclassname =  result.length > 0 ? 'slds-combobox slds-dropdown-trigger slds-dropdown-trigger_click slds-is-open' : 'slds-combobox slds-dropdown-trigger slds-dropdown-trigger_click';
            if(currentText.length > 0 && result.length == 0) {
                this.messageFlag = true;
            }
            else {
                this.messageFlag = false;
            }

            if(this.selectRecordId != null && this.selectRecordId.length > 0) {
                this.iconFlag = false;
                this.clearIconFlag = true;
            }
            else {
                this.iconFlag = true;
                this.clearIconFlag = false;
            }
        })
        .catch(error => {
            console.log('-------error-------------'+error);
            console.log(error);
        });
        
    }
    
   setSelectedRecord(event) {
        var currentText = event.currentTarget.dataset.id;
        this.txtclassname =  'slds-combobox slds-dropdown-trigger slds-dropdown-trigger_click';
        this.iconFlag = false;
        this.clearIconFlag = true;
        this.selectRecordName = event.currentTarget.dataset.name;
        var selectName = event.currentTarget.dataset.name;
        this.selectRecordId = currentText;
        this.inputReadOnly = true;
        const selectedEvent = new CustomEvent('selected', { detail: {selectName}, });
        // Dispatches the event.
        this.dispatchEvent(selectedEvent);
    }
    
    resetData(event) {
        this.selectRecordName = "";
        this.selectRecordId = "";
        this.inputReadOnly = false;
        this.iconFlag = true;
        this.clearIconFlag = false;
       
    }

}

lwcCustomLookup.css

div input[readonly] {
    padding-left: 1.75rem !important;  
}
.slds-input{
    padding-left: 1.75rem !important;
}

.slds-input-has-icon .slds-input__icon {
    top: 56% !important;
}
.slds-icon_small, .slds-icon--small {
    width: 1.5rem;
    height: 1.5rem;
    line-height: 2;
}

To use this component

<c:lwcCustomLookup objectName="Account" fieldName="Name" selectRecordId="{!v.selectRecordId}" selectRecordName="{!v.selectRecordName}" iconName = "action:new_account" onselected="{!c.selectedRecords}"/>
Selected Record: {!v.selectRecordName}

To pass data to parent component, we have used Events. You can read them about indetail here.

Code is very easy. When user type input we call the controller method and search for related records. We have used Imperative calling. You can read about it here. Then using template we iterate the result. So we have created Custom Lookup in Lightning Web Components.

You can find complete code in github-repo here.

Did you like the post or have any question, let me know in comments. Happy Programming 🙂

Advertisements

17 thoughts on “Custom Lookup in Lightning Web Components

  1. A nice enhancement would be reading icon info from the SObject itself instead of relying on parent component to pass through. Something along the line of

    //controller.js
    @wire(getObjectInfo, { objectApiName: ‘$objectType’ })
    _objectInfo({ data, error }) {
    if (error) { console.error(error); }
    if (data) {
    this._iconColor = ‘background: #’ + data.themeInfo.color;
    this._iconSrc = data.themeInfo.iconUrl
    }
    }

    //template

    The above code could be updated to allow parent component to override the SObject default icon if need to.

  2. Instead of just a single record can you show regarding multiple record.Means when we are searching record we are doing it one at time.I want to see the list of searched records below the lookup field

      1. my concern is if we are selecting record from lookup field instead of showing it in combo-box it should have a add,delete and refresh button.if we are clicking the add button it should display below the combo-box in proper format

  3. You have mentioned about using the component, however I’m unable to relate the
    onselected=”{!c.selectedRecords}” part of it. This piece of code is introducing exception. Is there anything that i’m missing.

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.