How to create custom lookup related dependent field in lightning aura.
- Subham Patel
- May 11, 2020
- 4 min read
custom lookup related dependent field in lightning aura.
(If user selects an account in the first custom lookup field
then, second fields should have only related contacts of above
account field.)
APEX Controller
===============
public with sharing class CustomLookupControllerCC {
@AuraEnabled
public static List<RecordsData> fetchRecords(String objectName, String filterField, String searchString, String value, String relatedRecordParentId, String parentFieldAPIName) {
try {
system.debug('APEX method call => '+relatedRecordParentId+' ==>'+ parentFieldAPIName);
List<RecordsData> recordsDataList = new List<RecordsData>();
String query ='';
if(relatedRecordParentId=='' && parentFieldAPIName==''){
query = 'SELECT Id, ' + filterField + ' FROM '+objectName;
}
else{
query = 'SELECT Id, '+ parentFieldAPIName + ' ,' + filterField + ' FROM ' + objectName +
' WHERE '+ parentFieldAPIName + '= \'' + relatedRecordParentId + '\'' ;
}
if(relatedRecordParentId=='' && parentFieldAPIName==''){
if(String.isNotBlank(value)) {
query += ' WHERE Id = \''+ value + '\' LIMIT 49999';
}
if(String.isNotBlank(filterField)) {
query += ' WHERE '+filterField+
' LIKE ' + '\'' + String.escapeSingleQuotes(searchString.trim()) + '%\' LIMIT 49999';
}
}
else{
if(String.isNotBlank(value)) {
query += ' AND Id = \''+ value + '\' LIMIT 49999';
}
if(String.isNotBlank(filterField)) {
query += ' AND '+filterField+
' LIKE ' + '\'' + String.escapeSingleQuotes(searchString.trim()) + '%\' LIMIT 49999';
}
}
System.debug('Query=> =>'+query);
for(SObject s : Database.query(query)) {
recordsDataList.add( new RecordsData((String)s.get(filterField), (String)s.get('id')) );
}
return recordsDataList;
} catch (Exception err) {
if ( String.isNotBlank( err.getMessage() ) && err.getMessage().contains( 'error:' ) ) {
throw new AuraHandledException(err.getMessage().split('error:')[1].split(':')[0] + '.');
} else {
throw new AuraHandledException(err.getMessage());
}
}
}
public class RecordsData {
@AuraEnabled public String label;
@AuraEnabled public String value;
public RecordsData(String label, String value) {
this.label = label;
this.value = value;
}
}
}
Parent Component that has two dependent custom lookup fields
============================================================
<aura:component implements="flexipage:availableForRecordHome,force:hasRecordId" access="global" >
<aura:attribute name="accountValue" type="String" />
<aura:attribute name="contactValue" type="String" />
<c:CustomLookup value="{!v.accountValue}" objectName="Account" fieldName="Name" label="Parent Account Name" iconName="standard:account" placeholder="Search Accounts.." />
<c:CustomLookup value="{!v.contactValue}" objectName="Contact" fieldName="Name" label="Related Contact Of Above Account " iconName="standard:contact" placeholder="Search Contacts.." parentFieldAPIName="accountId" relatedRecordParentId="{!v.accountValue}" />
<br/>
Account Id => {!v.accountValue} <br/>
Contact Id => {!v.contactValue}
</aura:component>
CustomLookup
============
COMPONENT
----------
<aura:component controller="CustomLookupControllerCC" >
<!-- Attributes that can be set while component calling-->
<aura:attribute name="objectName" type="string" default="" required="true" />
<aura:attribute name="fieldName" type="string" default="" required="true" />
<aura:attribute name="value" type="String" default="" description="To pre-populate a value" />
<aura:attribute name="recordCount" type="string" default="5" description="Records visible at once"/>
<aura:attribute name="iconName" type="string" default="standard:drafts" description="complete icon name eg. standard:account" />
<aura:attribute name="label" type="string" default="" description="Label will be displayed above input Box" />
<aura:attribute name="placeholder" type="string" default="Search..." description="placeholder for input Box" />
<aura:attribute name="parentFieldAPIName" type="string" default="" description="Parent Field API Name" />
<aura:attribute name="relatedRecordParentId" type="string" default="" description="Parent ID to get list of related Child Id" />
<!-- Internal Use Attributes -->
<aura:handler name="init" value="{!this}" action="{!c.doInit}"/>
<aura:attribute name="searchString" type="string" access="private" default="" description="String to search"/>
<aura:attribute name="selectedRecord" type="Object" access="private" default="" description="selected Record Details" />
<aura:attribute name="recordsList" type="List" access="private" description="List of Records having data" />
<aura:attribute name="message" type="String" access="private" default="" />
<!-- Component Markup -->
<div>
<aura:if isTrue="{!!empty(v.label)}">
<label class="slds-form-element__label">{!v.label}</label>
</aura:if>
<div class="slds-combobox_container">
<div class="slds-combobox slds-dropdown-trigger slds-dropdown-trigger_click slds-is-open" aura:id="resultsDiv" aria-expanded="true" aria-haspopup="listbox" role="combobox">
<div class="slds-combobox__form-element slds-input-has-icon slds-input-has-icon_right" role="none">
<div aura:id="lookup-pill" class="{! 'slds-pill-container ' + if(v.selectedRecord == '','slds-hide','') }" >
<!-- Selected Value Pill -->
<lightning:pill class="fullWidth" label="{!v.selectedRecord.label}" name="{!v.selectedRecord.value}" onremove="{! c.removeItem }">
<aura:set attribute="media">
<lightning:icon iconName="{!v.iconName}" size="x-small" alternativeText="icon"/>
</aura:set>
</lightning:pill>
</div>
<div aura:id="lookupField" class="{! if(v.selectedRecord == '','slds-show','slds-hide') }">
<!-- Icon, Search Bar, Search Icon -->
<lightning:icon class="slds-combobox__input-entity-icon" iconName="{!v.iconName}" size="large" alternativeText="icon"/>
<lightning:input aura:id="inputLookup" class="inputBox" placeholder="{!v.placeholder}" onblur="{!c.blurEvent}" onclick="{!c.showRecords}" onkeyup="{!c.searchRecords}" value="{!v.searchString}" autoComplete="off" variant="label-hidden" id="combobox-id-1" />
<lightning:spinner class="slds-hide" aura:id="Spinner" alternativeText="Loading" size="small" variant="brand"/>
<lightning:icon class="slds-input__icon" iconName="utility:search" size="x-small" alternativeText="search"/>
</div>
</div>
<!-- Dropdown List -->
<div id="listbox-id-1" class="slds-dropdown slds-dropdown_length-5 slds-dropdown_fluid" style="{! 'max-height:' + (8 + (v.recordCount * 40)) + 'px' }">
<ul class="slds-listbox slds-listbox_vertical recordListBox" role="presentation" onkeydown="{!c.selectItem}">
<aura:if isTrue="{!empty(v.message)}" >
<!-- To display Drop down List -->
<aura:iteration items="{!v.recordsList}" var="rec" >
<li id="{!rec.value}" class="slds-listbox__item eachItem" onmousedown="{!c.selectItem}">
<div class="slds-media slds-listbox__option_entity">
<lightning:icon iconName="{!v.iconName}" size="medium" alternativeText="icon" />
<span class="verticalAlign slds-truncate">{!rec.label}</span>
</div>
</li>
</aura:iteration>
<!-- To display Error Message -->
<aura:set attribute="else">
<li class="slds-listbox__item">
<span class="slds-media slds-listbox__option_entity">{!v.message}</span>
</li>
</aura:set>
</aura:if>
</ul>
</div>
</div>
</div>
</div>
</aura:component>
CONTROLLER
----------
({
// To prepopulate the seleted value pill if value attribute is filled
doInit : function( component, event, helper ) {
$A.util.toggleClass(component.find('resultsDiv'),'slds-is-open');
if( !$A.util.isEmpty(component.get('v.value')) ) {
helper.searchRecordsHelper( component, event, helper, component.get('v.value'));
}
},
// When a keyword is entered in search box
searchRecords : function( component, event, helper ) {
if( !$A.util.isEmpty(component.get('v.searchString')) ) {
helper.searchRecordsHelper( component, event, helper, '' );
} else {
$A.util.removeClass(component.find('resultsDiv'),'slds-is-open');
}
},
// When an item is selected
selectItem : function( component, event, helper ) {
if(!$A.util.isEmpty(event.currentTarget.id)) {
var recordsList = component.get('v.recordsList');
var index = recordsList.findIndex(x => x.value === event.currentTarget.id)
if(index != -1) {
var selectedRecord = recordsList[index];
}
component.set('v.selectedRecord',selectedRecord);
component.set('v.value',selectedRecord.value);
$A.util.removeClass(component.find('resultsDiv'),'slds-is-open');
/*if(component.get("v.relatedRecordsOnly")){
}*/
}
},
showRecords : function( component, event, helper ) {
if(!$A.util.isEmpty(component.get('v.recordsList')) && !$A.util.isEmpty(component.get('v.searchString'))) {
$A.util.addClass(component.find('resultsDiv'),'slds-is-open');
}
if( component.get('v.searchString') == '' ) {
helper.searchRecordsHelper( component, event, helper, '' );
}
},
// To remove the selected item.
removeItem : function( component, event, helper ){
component.set('v.selectedRecord','');
component.set('v.value','');
component.set('v.searchString','');
setTimeout( function() {
component.find( 'inputLookup' ).focus();
}, 250);
},
// To close the dropdown if clicked outside the dropdown.
blurEvent : function( component, event, helper ){
$A.util.removeClass(component.find('resultsDiv'),'slds-is-open');
},
})
HELPER
------
({
searchRecordsHelper : function(component, event, helper, value) {
$A.util.removeClass(component.find("Spinner"), "slds-hide");
var searchString = component.get('v.searchString');
var relatedRecordParent = component.get('v.relatedRecordParentId');
var parentFieldAPINm = component.get('v.parentFieldAPIName');
component.set('v.message', '');
component.set('v.recordsList', []);
// Calling Apex Method
var action = component.get('c.fetchRecords');
action.setParams({
'objectName' : component.get('v.objectName'),
'filterField' : component.get('v.fieldName'),
'searchString' : searchString,
'value' : value,
'relatedRecordParentId' : relatedRecordParent,
'parentFieldAPIName' : parentFieldAPINm
});
action.setCallback(this,function(response){
var result = response.getReturnValue();
if(response.getState() === 'SUCCESS') {
if(result.length > 0) {
//alert('Success > 0 length');
// To check if value attribute is prepopulated or not
if( $A.util.isEmpty(value) ) {
component.set('v.recordsList',result);
} else {
component.set('v.selectedRecord', result[0]);
}
} else {
//alert('Success < 0 length');
component.set('v.message', "No Records Found for '" + searchString + "'");
}
} else {
//alert('First Select an Account then try..');
// If server throws any error
var errors = response.getError();
if (errors && errors[0] && errors[0].message) {
component.set('v.message', errors[0].message);
}
}
// To open the drop down list of records
if( $A.util.isEmpty(value) )
$A.util.addClass(component.find('resultsDiv'),'slds-is-open');
$A.util.addClass(component.find("Spinner"), "slds-hide");
});
$A.enqueueAction(action);
}
})
STYLE
-----
.THIS .verticalAlign {
cursor: pointer;
margin: 6px 0px 0px 6px!important;
}
.THIS .fullWidth {
width: 100% !important;
}
.THIS .recordListBox {
margin-top:0px !important;
overflow-y: scroll;
}
.THIS .inputBox input {
padding-left: 30px;
}
.THIS .eachItem:hover {
background-color: #EEE;
cursor: pointer;
}
.THIS .inputIcon {
z-index: 99;
padding: 2px 0;
}
/* For Scrolling */
.THIS ::-webkit-scrollbar {
width: 7px;
height: 7px;
}
.THIS ::-webkit-scrollbar-track {
display: none !important;
}
.THIS ::-webkit-scrollbar-thumb {
border-radius: 10px;
background: rgba(0,0,0,0.4);
}

Comments