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>
Output: