In Salesforce Lightning we don’t have any lookup field component like Salesforce classic. So, here is a generic lookup Lightning Component, which can be used for any sObject lookup present in our org without changing any major code.
sObjectLookupController:
Create below apex controller to use it in sObjectLookup.cmp
component.
/** * @Author : Biswajeet Samal * @Date : 9 Dec 2017 * @Desc : Controller for sObject Lookup Lightning Component * */ public with sharing class sObjectLookupController { @AuraEnabled public static List<sObject> searchRecord(String objectAPIName, String fieldAPIName, List<String> moreFields, String searchText, Integer recordLimit) { List<sObject> objectList = new List<sObject>(); searchText='\'%' + String.escapeSingleQuotes(searchText.trim()) + '%\''; String soqlQuery = 'SELECT Id, Name'; if(!moreFields.isEmpty()){ soqlQuery = soqlQuery + ',' + String.join(moreFields, ',') ; } soqlQuery = soqlQuery + ' FROM ' + objectAPIName + ' WHERE ' + + fieldAPIName +' LIKE '+ searchText + ' LIMIT '+ recordLimit; objectList = Database.query(soqlQuery); return objectList; } }
sObjectLookupSelectEvent.evt:
Create below Lightning Event, which is used to store and fill the input field with selected record Id and Name. Fired from sObjectLookupItem.cmp
component, handled at Lookup component.
<!--sObjectLookupSelectEvent.evt--> <aura:event type="COMPONENT" description="sObjectLookupSelectEvent"> <aura:attribute name="recordId" type="String" required="true" description="Used to send selected record Id"/> <aura:attribute name="recordName" type="String" required="true" description="Used to send selected record Name" /> </aura:event>
sObjectLookup.cmp:
Create below Lightning Component, which will be used for sObject lookup.
<!--sObjectLookup.cmp--> <aura:component controller="sObjectLookupController" description="Lightning component for lookup fields"> <!--Declare Attributes--> <aura:attribute name="objectAPIName" type="String" required="true" description="Object API name used for searching records"/> <aura:attribute name="fieldAPIName" type="String" required="true" description="API Name of field to be searched"/> <aura:attribute name="lookupIcon" type="String" default="standard:contact" description="Icon for lookup records"/> <aura:attribute name="placeholder" type="String" default="Search..." description="Placeholder text for input search filed"/> <aura:attribute name="fieldLabel" type="String" required="true" description="input search field Label"/> <aura:attribute name="selectedRecordId" type="String" description="Used to store the selected record id. While calling this component from other component, set this attribute to the lookup field API name"/> <aura:attribute name="selectedRecordName" type="String" description="This is used to show the selected record Name in search input"/> <aura:attribute name="subHeadingFieldsAPI" type="String[]" description="Field API for the fields to be shown under the record Name. Must be comma separated. Example: Email,Phone"/> <aura:attribute name="matchingRecords" type="Object[]" access="private" description="List of records returned from server side call"/> <aura:attribute name="recordLimit" type="Integer" access="public" default="5" description="Total number of record to be returned"/> <!--Declare Handlers--> <aura:handler name="lookupSelect" event="c:sObjectLookupSelectEvent" action="{!c.handleLookupSelectEvent}" description="Event handler to get the selected record Id and Name from LookupItem component"/> <!--Component--> <div class="slds-form-element__control"> <div class="slds-combobox_container slds-has-inline-listbox"> <div aura:id="divLookup" class="slds-combobox slds-dropdown-trigger slds-dropdown-trigger_click slds-combobox-lookup" aria-expanded="false" aria-haspopup="listbox" role="combobox"> <div class="slds-combobox__form-element"> <lightning:input type="search" aura:id="searchinput" label="{!v.fieldLabel}" name="{!v.fieldLabel}" value="{!v.selectedRecordName}" onchange="{!c.handleSearchRecords}" isLoading="false" placeholder="{!v.placeholder}" onfocus="{!c.handleSearchRecords}" onblur="{!c.hideList}"/> </div> <div id="listbox-unique-id" role="listbox"> <ul class="slds-listbox slds-listbox_vertical slds-dropdown slds-dropdown_fluid" role="presentation"> <aura:iteration var="rec" items="{!v.matchingRecords}"> <c:sObjectLookupItem record="{!rec}" subHeadingFieldsAPI="{!v.subHeadingFieldsAPI}" iconCategoryName="{!v.lookupIcon}"/> </aura:iteration> </ul> </div> </div> </div> </div> </aura:component>
sObjectLookupController.js:
({ handleSearchRecords : function (component, event, helper) { var searchText = component.find("searchinput").get("v.value"); if(searchText){ helper.searchRecord(component,searchText); }else{ helper.searchRecord(component, ''); } }, handleLookupSelectEvent : function (component, event, helper) { var selectedRecordId = event.getParam("recordId"); var selectedrecordName = event.getParam("recordName"); component.set("v.selectedRecordId", selectedRecordId); component.set("v.selectedRecordName", selectedrecordName); helper.toggleLookupList(component, false, 'slds-combobox-lookup', 'slds-is-open'); }, hideList :function (component,event,helper) { window.setTimeout( $A.getCallback(function() { if (component.isValid()) { helper.toggleLookupList(component, false, 'slds-combobox-lookup','slds-is-open'); } }), 200 ); } })
sObjectLookupHelper.js:
({ toggleLookupList : function (component, ariaexpanded, classadd, classremove) { component.find("divLookup").set("v.aria-expanded", true); $A.util.addClass(component.find("divLookup"), classadd); $A.util.removeClass(component.find("divLookup"), classremove); }, searchRecord : function (component, searchText) { component.find("searchinput").set("v.isLoading", true); var action = component.get("c.searchRecord"); action.setParams({ "objectAPIName": component.get("v.objectAPIName"), "fieldAPIName":component.get("v.fieldAPIName"), "moreFields":component.get("v.subHeadingFieldsAPI"), "searchText":searchText, "recordLimit":component.get("v.recordLimit") }); action.setCallback(this, function(response) { var state = response.getState(); if(component.isValid() && state === "SUCCESS") { if(response.getReturnValue()){ component.set("v.matchingRecords", response.getReturnValue()); if(response.getReturnValue().length > 0){ this.toggleLookupList(component, true, 'slds-is-open', 'slds-combobox-lookup'); } component.find("searchinput").set("v.isLoading", false); } } }); $A.enqueueAction(action); } })
sObjectLookupItem.cmp:
Create below lightning component, which will be used for creating list elements for records in sObjectLookup.cmp
component.
<!--sObjectLookupItem.cmp--> <aura:component description="Component used for creating list elements for records"> <!--Declare Attributes--> <aura:attribute name="record" type="Object" required="true" description="Holds the single record instance"/> <aura:attribute name="subHeadingFieldsAPI" type="String[]" description="Holds the field API names to show as meta entity in list"/> <aura:attribute name="subHeadingFieldValues" type="String" description="Used to construct the meta entity value. Works as subheading in record option"/> <aura:attribute name="iconCategoryName" type="String" description="Lightning icon category and icon name to show with each record element"/> <!--Declare Events--> <aura:registerEvent name="lookupSelect" type="c:sObjectLookupSelectEvent" description="Event used to send the selected record Id and Name to Lookup component"/> <!--Declare Handlers--> <aura:handler name="init" value="{!this}" action="{!c.getValues}" description="standard init event to prepare the sub heading mete entity value"/> <!--Component--> <li role="presentation" class="slds-listbox__item" onclick="{!c.handleSelect}"> <span class="slds-media slds-listbox__option slds-listbox__option_entity slds-listbox__option_has-meta" role="option"> <!--Lightning Icon--> <span class="slds-media__figure"> <lightning:icon iconName="{!v.iconCategoryName}" size="small" alternativeText="{!v.record.Name}"/> </span> <!--Option--> <span class="slds-media__body"> <span class="slds-listbox__option-text slds-listbox__option-text_entity"> {!v.record.Name} </span> <!--Option sub heading--> <span class="slds-listbox__option-meta slds-listbox__option-meta_entity"> {!v.subHeadingFieldValues} </span> </span> </span> </li> </aura:component>
sObjectLookupItemController.js:
({ getValues : function (component) { var record = component.get("v.record"); var subheading = ''; for(var i=0; i<component.get("v.subHeadingFieldsAPI").length; i++ ){ if(record[component.get("v.subHeadingFieldsAPI")[i]]){ subheading = subheading + record[component.get("v.subHeadingFieldsAPI")[i]] + ' - '; } } subheading = subheading.substring(0,subheading.lastIndexOf('-')); component.set("v.subHeadingFieldValues", subheading); }, handleSelect : function (component,event) { var chooseEvent = component.getEvent("lookupSelect"); chooseEvent.setParams({ "recordId" : component.get("v.record").Id, "recordName":component.get("v.record").Name }); chooseEvent.fire(); } })
Usage:
<!--sObjectLookupApp.app--> <aura:application extends="force:slds"> <c:sObjectLookup fieldLabel = "Contact" objectAPIName = "Contact" fieldAPIName = "Name" subHeadingFieldsAPI = "Email,Phone" lookupIcon = "standard:contact" placeholder = "Search Contact"/> </aura:application>