Salesforce Generic sObject Lightning Lookup Component

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:

Simple Calculator Using Salesforce Lightning Components

Calculator Component:

<!--Calculator-->
<aura:component >
    <aura:attribute name="num1" type="Integer"/>
    <aura:attribute name="num2" type="Integer"/>
    <aura:attribute name="num3" type="Integer"/><br/>
    <ui:inputNumber label="Number 1 " value="{!v.num1}"/><br/>
    <ui:inputNumber label="Number 2 " value="{!v.num2}"/><br/>
    <aura:attribute name="isAdd" type="Boolean" default="false"/>
    <aura:attribute name="isSub" type="Boolean" default="false"/>
    <aura:attribute name="isMul" type="Boolean" default="false"/>
    <aura:attribute name="isDiv" type="Boolean" default="false"/>
    <aura:attribute name="isRefresh" type="Boolean" default="false"/>
    
    <aura:if isTrue="{!v.isAdd}">
        Addition of Two Numbers is :  {!num3}
        <ui:outputNumber value="{!v.num3}"/>
    </aura:if>
    
    <aura:if isTrue="{!v.isSub}">
        Substraction of Two Numbers is :  {!num3} 
        <ui:outputNumber value="{!v.num3}"/>
    </aura:if>
    
    <aura:if isTrue="{!v.isMul}">
        multiplication of Two Numbers is :  {!num3}
        <ui:outputNumber value="{!v.num3}"/>
    </aura:if>
    
    <aura:if isTrue="{!v.isDiv}">
        Division of Two Numbers is :  {!num3}
        <ui:outputNumber value="{!v.num3}"/>
    </aura:if>
    <br/>
    <br/>
    <ui:button press="{!c.addAction}" label="Addition" />
    <aura:if isTrue="{!v.isRefresh}">
        <ui:button press="{!c.refreshAction}" label="Refresh" />
    </aura:if>
    <ui:button press="{!c.substractionAction}" label="Substraction" />    
    <ui:button press="{!c.multiplicationAction}" label="Multiplication" />    
    <ui:button press="{!c.divisionAction}" label="Division" />
    
</aura:component>

Calculator JavaScript Controller:

({
    addAction : function(component, event, helper) {
        var num1=component.get("v.num1");
        var num2=component.get("v.num2");
        var num3=num1+num2;
        component.set("v.num3", num3);
        component.set("v.isAdd", true);
        component.set("v.isRefresh", true);
        component.set("v.isMul", false);
        component.set("v.isSub", false);
        component.set("v.isDiv", false);
    },
    multiplicationAction : function(component, event, helper) {
        var num1=component.get("v.num1");
        var num2=component.get("v.num2");
        var num3=num1*num2;
        component.set("v.num3", num3);
        component.set("v.isMul", true);
        component.set("v.isRefresh", true);
        component.set("v.isDiv", false);
        component.set("v.isSub", false);
        component.set("v.isAdd", false);
    },
    substractionAction : function(component, event, helper) {
        var num1=component.get("v.num1");
        var num2=component.get("v.num2");
        var num3=num1-num2;
        component.set("v.num3", num3);
        component.set("v.isSub", true);
        component.set("v.isRefresh", true);
        component.set("v.isMul", false);
        component.set("v.isDiv", false);
        component.set("v.isAdd", false);
    },
    divisionAction : function(component, event, helper) {
        var num1=component.get("v.num1");
        var num2=component.get("v.num2");
        var num3=num1/num2;
        component.set("v.num3", num3);
        component.set("v.isDiv", true);
        component.set("v.isRefresh", true);
        component.set("v.isMul", false);
        component.set("v.isSub", false);
        component.set("v.isAdd", false);
    },
    
    refreshAction : function(component, event, helper) {
        component.set("v.num1", 0);
        component.set("v.num2", 0);
        component.set("v.isDiv", false);
        component.set("v.isMul", false);
        component.set("v.isSub", false);
        component.set("v.isAdd", false);
        component.set("v.isRefresh", false);
    }
})

Lightning App:

<!--TestApp-->
<aura:application extends="force:slds">
    <c:Calculator />
</aura:application>

Output:

Lightning Tab With Next And Back Buttons In Lightning Component

Component:

<!--TabComponent-->
<aura:component>
    <aura:attribute name="selTabId" type="String" default="tab1" />
    <div aura:id="data-entry">
        <lightning:tabset variant="default" selectedTabId="{!v.selTabId}" >
            <lightning:tab label="Mobile" aura:id="tab1" tabindex="1" id="tab1" title="Mobile">
                <p>Apple</p>
                <p>Samsung</p>
                <p>Lenovo</p>
            </lightning:tab>
            
            <lightning:tab label="Laptop" aura:id="tab2" tabindex="2" id="tab2" title="Laptop">
                <p>Apple</p>
                <p>Lenovo</p>
                <p>Dell</p>
            </lightning:tab>  
            
            <lightning:tab label="Tablet" aura:id="tab3" tabindex="3" id="tab3" title="Tablet">
                <p>Apple</p>
                <p>Lenovo</p>
                <p>Dell</p>
            </lightning:tab> 
        </lightning:tabset>
    </div>
    
    <div class="slds-clearfix">
        <div class="slds-float_left">
            <!--disabled the back button on first Tab-->    
            <lightning:button disabled="{!v.selTabId == 'tab1'}" variant="neutral" label="Back" onclick="{!c.back}" />
        </div>
        <div class="slds-float_right">
            <!--disabled the back button on last Tab--> 
            <lightning:button variant="brand" disabled="{!v.selTabId == 'tab3'}" label="Next" onclick="{!c.next}" />
        </div>
    </div>
</aura:component>

Component Controller:

({
    next : function(component, event, helper) {
        //Get the current selected tab value
        var currentTab = component.get("v.selTabId");
        
        if(currentTab == 'tab1'){
            component.set("v.selTabId" , 'tab2');   
        }else if(currentTab == 'tab2'){
            component.set("v.selTabId" , 'tab3');     
        }
    },
    
    back : function(component, event, helper) {
        //Get the current selected tab value  
        var currentTab = component.get("v.selTabId");
        
        if(currentTab == 'tab2'){
            component.set("v.selTabId" , 'tab1');     
        } else if(currentTab == 'tab3'){
            component.set("v.selTabId" , 'tab2');     
        }
    }
})

Output:

Get Selected Tab of Lightning Tabset

Component:

<!--TabComponent-->
<aura:component>
    <aura:attribute name="selTabId" type="String" default="tab1" />
    <div aura:id="data-entry">
        <lightning:tabset onselect="{!c.tabSelected}" variant="default" selectedTabId="{!v.selTabId}" >
            <lightning:tab label="Mobile" aura:id="tab1" tabindex="1" id="tab1" title="Mobile">
                <p>Apple</p>
                <p>Samsung</p>
                <p>Lenovo</p>
            </lightning:tab>
            
            <lightning:tab label="Laptop" aura:id="tab2" tabindex="2" id="tab2" title="Laptop">
                <p>Apple</p>
                <p>Lenovo</p>
                <p>Dell</p>
            </lightning:tab>      
        </lightning:tabset>
    </div>
</aura:component>

Component Controller:

({
    tabSelected: function(component,event,helper) {
        alert(component.get("v.selTabId"));
    }
})

Output:

Create Lightning Tabset in Component

Component:

<!--TabComponent-->
<aura:component>
    <div aura:id="data-entry">
        <lightning:tabset variant="default" selectedTabId="tab1" >
            <lightning:tab label="Mobile" aura:id="tab1" tabindex="1" id="tab1" title="Mobile">
                <p>Apple</p>
                <p>Samsung</p>
                <p>Lenovo</p>
            </lightning:tab>
            
            <lightning:tab label="Laptop" aura:id="tab2" tabindex="2" id="tab2" title="Laptop">
                <p>Apple</p>
                <p>Lenovo</p>
                <p>Dell</p>
            </lightning:tab>      
        </lightning:tabset>
    </div>
</aura:component>

Output: