Showing posts with label Lightning component. Show all posts
Showing posts with label Lightning component. Show all posts

Thursday, 8 April 2021

Salesforce Lightning TreeGrid with pagination

INTRODUCTION/SCENARIO

While working on one of the user stories of Pharma sector for a client based on Chicago, USA; there was a requirement to display Contact records under their parent account record. Also, pagination was needed with a picklist to choose number of records to be displayed on a page.

CHALLENGE

Lightning:treeGrid is useful component for displaying structured data such as hierarchy or forecasting data while dealing with the same object. But there is no functionality available to display records of 2 different objects (In our case, Account and Contact) in the same table while using Lightning:treeGrid.

APPROACH / SOLUTION

To overcome this limitation, I've implemented custom Javascript in the lightning component containing the mechanism to have customized list of Accounts and Contacts with pagination.

Note: This component requires API version 42.0 and later.

treeGridController is a controller class of the lightning components treeGrid utilized to fetch Account object data which has having child Contact records.

treeGridController.apxc
public class treeGridController {
    
    @AuraEnabled
    public static List <Account> getAccountList() {
        return [Select Id, Name,
                    (SELECT Name, Phone, Email FROM Contacts) 
                    From Account
                    Where Id IN (Select AccountId From Contact)
                    ORDER BY Name ASC];
    }
}

Below are Component & JavaScript files for the reference which i've used to meet the requirement..

treeGrid.cmp
<aura:component controller="treeGridController" implements="flexipage:availableForAllPageTypes" >
    <aura:attribute name="resultData" type="Object" access="private"/>
    <aura:attribute name="gridColumns" type="List" />
    <aura:attribute name="gridData" type="Object" />
    <aura:attribute name="gridExpandedRows" type="Object" />
    <aura:attribute name="PageNumber" type="Integer" />
    <aura:attribute name="TotalPages" type="Integer"/>
    <aura:attribute name="currentPage" type="Integer" default="0" />
    <aura:attribute name="limit" type="Integer" default="5" />
    <aura:handler name="init" value="{!this}" action="{!c.doInit}"/>
    <div class="slds-page-header" role="banner">
        <ui:inputSelect aura:id="pageSize" label="Display Records Per Page: " change="{!c.onSelectChange}">
            <ui:inputSelectOption label="5" text="5" value="true"/>
            <ui:inputSelectOption label="10" text="10"/>
            <ui:inputSelectOption label="50" text="50"/>
        </ui:inputSelect>
    </div>
    <lightning:treeGrid aura:id="accTree"
                        columns="{!v.gridColumns}"
                        data="{!v.gridData}"
                        expandedRows="{!v.gridExpandedRows}"
                        keyField="Id"
                        hideCheckboxColumn = "true"
                        />
    <div class="slds-clearfix">
        <div class="slds-page-header" role="banner">
            <div class="slds-float_right">            
                <lightning:button disabled="{!v.PageNumber == 1}" variant="brand" aura:id="prevPage" label="Prev" onclick="{!c.handlePrev}" />            
                <lightning:button disabled="{!v.PageNumber == v.TotalPages}" aura:id="nextPage" variant="brand" label="Next" onclick="{!c.handleNext}"/>
            </div>
            <p class="slds-page-header__title">Page {!v.PageNumber} of {!v.TotalPages}</p>
        </div>
    </div>
</aura:component>

treeGridController.js
({
    doInit : function(component, event, helper) {
        var columns = [
            {
                type: 'url',
                fieldName: 'AccountURL',
                label: 'Account Name',
                typeAttributes: {
                    label: { fieldName: 'accountName' }
                }
            },
            {
                type: 'text',
                fieldName: 'Name',
                label: 'Contact Name'
            },
            {
                type: 'phone',
                fieldName: 'Phone',
                label: 'Phone Number'
            },
            {
                type: 'email',
                fieldName: 'Email',
                label: 'Email'
            }
        ];
        component.set('v.gridColumns', columns);
        var action = component.get("c.getAccountList");
        action.setCallback(this, function(response){
            var state = response.getState();
            if (state === "SUCCESS" ) {
                var resultData = response.getReturnValue();
                component.set('v.resultData', resultData);
                helper.bindTableData(component, event);
            }
        });
        $A.enqueueAction(action);
    },
    handleNext : function(component, event, helper){
        component.set('v.currentPage',component.get('v.currentPage')+1);
        helper.buildTable(component, event);
    },
    handlePrev : function(component, event, helper){
        component.set('v.currentPage',component.get('v.currentPage')-1);
        helper.buildTable(component, event);
    },
    onSelectChange : function(component, event, helper){
        component.set('v.currentPage',0);
        var pageSize = component.find('pageSize').get('v.value');
        component.set('v.limit',pageSize);
        helper.buildTable(component, event);
    }
})

treeGridHelper.js
({
    bindTableData : function(component, event) {
        var resultData = component.get('v.resultData');
        for (var i=0; i<resultData.length; i++ ) {
            resultData[i].accountName = resultData[i]['Name'];
            delete resultData[i]['Name'];
            resultData[i]._children = resultData[i]['Contacts'];
            delete resultData[i].Contacts;
            resultData[i].AccountURL = '/'+resultData[i].Id;                
        }
        component.set('v.resultData',resultData);
        this.buildTable(component, event);
    },
    buildTable : function(component, event){
        var resultData = component.get('v.resultData');
        var limit = component.get('v.limit');
        var currentPage = component.get('v.currentPage');
        var totalPage = Math.ceil(resultData.length / limit);
        var startIndex = currentPage * limit;
        var row = [];
        var expandedRows = [];        
        for (var i = startIndex; i < parseInt(startIndex)+parseInt(limit); i++) {
            if(resultData[i]){
                expandedRows.push(resultData[i].Id);
                row.push(resultData[i]);
            }
        }
        component.set('v.gridData', row);
        component.set('v.PageNumber',currentPage+1);
        component.set('v.TotalPages',totalPage);
        component.set('v.gridExpandedRows', expandedRows);
    }
})

OUTPUT: 


If you have any questions you can reach out our Salesforce Consulting team here.

Thursday, 18 March 2021

Display Picklist values dynamically in lightning component


Requirement:
While working on one of the user stories of Engineering sector project for a client based out of Tampa, FL, USA;  there was a requirement to provide an interface that would display selected Industry (Pick-list field) values for Accounts on the fly while performing Mass Edit.

Challenge: 

Mass Edit feature was implemented using the Custom Lightning component. And, t
here is no out-of-the-box (OOTB) feature available to display selected pick-list values in the in lightning component, while bulk edit is being performed.

Solution:
To overcome this limitation, we have created an Apex class that contains the mechanism to retrieve the list of Industry (pick-list) field values from the Account object.

This Apex class is then used by Custom Lightning Component (BulkEdit.Cmp) which displays the list of Accounts. Using component.get ("v.pickvalues") in DynamicPickController component, we are retrieving selected pick-list values from the Parent Component (BulkEdit.cmp). 

Then Industry pick-list values are being compared with the values that have been retrieved from Parent Component. If field values are matched, it will set the selected Industry field value.

If values are not matched, then it will set the first value from the pick list options.

Once comparison is done,  value is set to the attribute IndustryPick that we have defined in the Custom DynamicPickController.Cmp component.

BulkEdit.cmp
<aura:iteration items="{!v.selectedlst}" var="ac" >
                                    <c:DynamicPickController selectedOptions = '{!v.selectedlst}'
                                                  Name = '{!ac.lstTelLineAccLocUserobject2.Name}'
                                                  Industry = '{!ac.lstTelLineAccLocUserobject2.Industry}'
                                                  pickvalues = '{!v.pickvalues}'
                                                  />
</aura:iteration>

DynamicPickController.Js
doInit : function(component, event, helper) {
        var IndustryPick = [];
        for(var i = 0 ; i < component.get("v.pickvalues").length ; i++)
        {
            if(component.get("v.pickvalues")[i] == component.get("v.Industry"))  
            {
                IndustryPick.push({"label":component.get("v.pickvalues")[i], "value":component.get("v.pickvalues")[i], "selected":true})
            }
            else
            {
                IndustryPick.push({"label":component.get("v.pickvalues")[i], "value":component.get("v.pickvalues")[i]})
            }
        }
        component.set("v.IndustryPick", IndustryPick);
    }

DynamicPickController.Cmp
<aura:attribute name="pickvalues" type="list"/>
<aura:handler name="init" value="{!this}" action="{!c.doInit}" />
<lightning:select name="Industry" label="Select a Industry:" aura:id="Industry" value="{!v.Industry}">
                <aura:iteration items="{!v.IndustryPick}" var="option">
                    <option text="{!option.label}" value="{!option.label}" selected="{!option.selected}"/>
                </aura:iteration>
</lightning:select>

Output:

If you have any questions you can reach out our Salesforce Consulting team here.

Thursday, 11 March 2021

Hyperlink a Record in lightning:datatable

Requirement:

While working on one of the user stories of healthcare sector project for a client based out of Atlanta, GA, USA, there was a need of displaying a list of Contacts in lightning:datatable having 2 columns with hyperlinks. One is contact name and other is account name. And clicking on it, it should redirect to contact record and account record respectively.

Challenge: 

The challenging part was, there is no standard functionality available in the lightning:datatable for a hyperlink field to redirect to record detail page.

Solution:

To overcome this limitation, we'd to make changes in column format for a Contact name and Account name fields with type:'url' and also need to add typeAttributes as display below.

{ label: 'Name', fieldName: 'contacturl', type: 'url', typeAttributes: { label: { fieldName: 'Name' }, target: '_blank' } }, { label: 'Account Name', fieldName: 'accounturl', type: 'url', typeAttributes: { label: { fieldName: 'AccountName' }, target: '_blank' } }




See below code files - ApexController, Component files, and output to have a clear understanding.

ContactController
public class ContactController {
    @AuraEnabled
    public static List < Contact > fetchContacts() {
        return [ SELECT Id, Name, Account.Id,
                Account.Name,Phone,Department,LeadSource FROM Contact];        
    }
}
The contact controller class is referenced by the HyperLinkDatable component to display retrieved contacts as a data table.

HyperLinkDatable.cmp
<aura:component implements="force:appHostable" controller="ContactController">
               
    <aura:attribute type="Contact[]" name="conList"/>
    <aura:attribute name="columns" type="List"/>
    <aura:handler name="init" value="{!this}" action="{!c.doInit}"/>
   
    <lightning:datatable data="{!v.conList}"
                         columns="{!v.columns}"
                         keyField="Id"
                         hideCheckboxColumn="true"/>
   
</aura:component>

HyperLinkDatableController.js
({
    
    doInit : function( component, event, helper ) {        
        component.set('v.columns', [
            { label: 'Name', fieldName: 'contacturl', type: 'url',
             typeAttributes: { label: { fieldName: 'Name' }, target: '_blank' }},
            {label: 'Department',fieldName: 'Department', type: 'Text' },
            {label: 'Lead Source',fieldName: 'LeadSource', type: 'Phone' },
            {label: 'Phone',fieldName: 'Phone', type: 'Phone' },
            { label: 'Account Name', fieldName: 'accounturl', type: 'url',
             typeAttributes: { label: { fieldName: 'AccountName' }, target: '_blank' } }
        ]);
        //getting contact records
        var action = component.get( "c.fetchContacts" );
        action.setCallback(this, function( response ) {            
            var state = response.getState();
            if ( state === "SUCCESS" ) {
                var records = response.getReturnValue();
                //setting value for url
                records.forEach( function( record ) {
                    record.AccountName = record.Account.Name;
                    record.accounturl = '/' + record.Account.Id;
                    record.Name = record.Name;
                    record.contacturl = '/' + record.Id; 
                });
                component.set( "v.conList", records );                
            }            
        });
        $A.enqueueAction( action );        
    }    
})

After this, we need to create a Lightning App to integrate all of the files as a solution.

<aura:application
access="GLOBAL" extends="force:slds"> <c:HyperLinkDatatable/> </aura:application>

Output:


If you have any questions you can reach out our Salesforce Consulting team here.

Wednesday, 10 July 2019

Display "mini page layout" on hover in Lightning Component

Requirement:
There was a requirement, while working with one of the clients, to provide an interface which would display mini page layout on hover of the Lightning component. This layout had to be customized and shown on MouseHover of the Contact object field.

Challenge: 

In Lightning, the mini page layout is only available for the fields which have a lookup or have a master-detail relationship. There is no out-of-the-box (OOTB) functionality available to display linked record field values in the pop-up in lightning component.

Solution:

To overcome this limitation, we developed a custom solution which involved creating an apex class called ContactsController. This class contains the mechanism to retrieve the list of contacts along with related accounts. This class is used by Custom Lightning Component to display the list of contacts with a pop-up functionality.


ContactsController class

public class ContactsController {
    @AuraEnabled
    public static List <contact> getContacts() {
        return [SELECT Id, name,phone, Contact.account.Name, Contact.account.industry, Contact.account.Type,
                Contact.account.Phone  FROM contact ORDER BY createdDate ASC];
    }
}

ContactsController class is referenced by MouseHover component to display retrieved contacts as a data table and use JavaScript Controller to illustrate contacts only from the list.

MouseHover.cmp

<aura:component controller="ContactsController">
    <aura:attribute name="contacts" type="List" />
    <aura:attribute name="conAccLst" type="List" />
    <aura:attribute name="reId" type="Id" />
    <aura:attribute name="mouseHoverData" type="object" />
    <aura:attribute name="togglehover" type="boolean" default="false"/>
    <aura:handler name="init" value="{!this}" action="{!c.doInit}" />
    <aura:attribute name="hoverRow" type="Integer" default="-1" />
    <!-- Use a data table from the Lightning Design System: https://www.lightningdesignsystem.com/components/data-tables/ -->
    <table class="slds-table slds-table_bordered slds-table_striped slds-table_cell-buffer slds-table_fixed-layout">
        <thead>
            <tr class="slds-text-heading_label">
                <th scope="col"><div class="slds-truncate" title="Name" style="text-align: center">Contact Name</div></th>
                <th scope="col"><div class="slds-truncate" title="Phone" style="text-align: center">Phone</div></th>
            </tr>
        </thead>
        <tbody>
            <!-- Use the Apex model and controller to fetch server side data -->
            <aura:iteration items="{!v.contacts}" var="contact" indexVar="index">
                <tr data-selected-Index="{!index}">
                    <td><div class="slds-truncate" title="{!contact.Name}" style="text-align: center">
                        <a id="{!contact.Id}" onmouseenter="{!c.handleMouseHover}" onmouseout="{!c.handleMouseOut}" data-index="{!index}" tabindex="-1">{!contact.Name}</a></div>
                        <aura:if isTrue="{!v.hoverRow==index}">
                            <aura:if isTrue="{!v.togglehover==true}">
                                <div   class="slds-popover slds-nubbin_bottom"
                                     role="tooltip" id="help" style="position: absolute; right: 225px; bottom: 100%; width: 22rem; padding: inherit;">
                                    Account Name: {!v.mouseHoverData.Name}<br/>
                                    Phone:{!v.mouseHoverData.Phone}<br/>
                                    Type:{!v.mouseHoverData.Type}<br/>
                                  
                                </div>
                            </aura:if>
                        </aura:if>
                    </td>
                    <td><div class="slds-truncate" title="{!contact.Phone}" style="text-align: center">{!contact.Phone}</div></td>
                </tr>
            </aura:iteration>
        </tbody>
    </table>
</aura:component>

MouseHoverController.js retrieves all contacts on the load event of MouseHover Component and call MouseHoverHelper javascript file on hover of any contact name to display a pop-up with related account details.

MouseHoverController.js


({
    doInit: function(component, event, helper) {
        // Fetch the Contact list from the Apex controller
        helper.getAccountList(component);
    },
    handleMouseHover: function(component, event, helper) {
        var my = event.srcElement.id;
        component.set("v.reId",my);
        helper.getMiniLayout(component, event, helper)
    },
    handleMouseOut: function(component, event, helper) {
        component.set("v.hoverRow",-1);
        component.set("v.togglehover",false);
    }
})

MouseHoverHelper fetches the related account details as a pop-up on hover of a contact name.

MouseHoverHelper.js 


({
    // Fetch the Contact from the Apex controller
    getAccountList: function(component) {
        var action = component.get('c.getContacts');
        // Set up the callback
        var self = this;
        action.setCallback(this, function(actionResult) {
            var result = actionResult.getReturnValue();
            component.set('v.contacts', result);
            var conAccList = [];
            for(var i=0 ; i<result.length;i++){
                conAccList.push({"Id":result[i].Id, "value":result[i]});
            }
            component.set('v.conAccLst', conAccList);
        });
        $A.enqueueAction(action);
    },
    //Fetch the releted account on mouseHover 
    getMiniLayout:function(component, event, helper){
        
        var getAccount = component.get('v.conAccLst');
        for(var i=0;i<getAccount.length;i++){
            if(getAccount[i].Id == component.get("v.reId")){
                component.set('v.mouseHoverData', getAccount[i].value.Account);
                break;
            }
        }
        component.set("v.hoverRow", parseInt(event.target.dataset.index));
        component.set("v.togglehover",true);
    }
})

After this, we have created a Lightning App to integrate all of the files as a solution

Sample.app 

<aura:application extends="force:slds">
    <c:MouseHover />
</aura:application>


Output :



If you have any questions you can reach out our Salesforce Consulting team here.