Using Lightning Promises
June 14, 2018

Using Lightning Promises

Build Simple Employee List application with Datatable

Introduction

Before Lightning:Datatable was introduced by Salesforce, we had a project that required table data display in Lightning. There are often Visualforce legacy pages where display data tables need to be translated into Lightning. In such cases, Lightning:Datatable from the Lightning library can be used. Also, Lightning Promises might be used to produce a simple synchronous code style for asynchronous Apex controller action calls.

However, the datatable component in the current Spring 2018 release still does not support custom checkboxes or custom radio buttons. This required us to implement some custom solutions. In this article, we’ll describe a case where the datatable component might be appropriate for use, since it does not require features which datatable does not support.

Lightning Promises

Javascript Promises is one of the latest key features of Front-End development. Since Winter 17  ES6 Promises can be used in Lightning Controllers and Helpers. This helps to chain asynchronous server side requests and process them in ES6 synchronous style coding.

It is now possible to use Promises in Lightning. Salesforce documentation does not reveal much about it, but rather gives some basic examples. When implementing Promises in Lightning there are two important points:

  1. Wrap anonymous callback function in $A.getCallback() method.
  2. Do not use storable actions in Promises.

Lightning datatable

Lightning datatable may be helpful to display table data in Lightning in case you do not need custom checkbox columns or customizable hints. Datatable has been available since the Winter ’18 Salesforce release. It helps to display tabular data which has been formatted according to type. For example, emails or phones are displayed as links which open a mail or phone client if the type is configured correctly. The standard component inherits styles from the Lightning Design System.

Other cool features are sorting and infinite scrolling, but to implement them you need some custom code. Therefore, it makes sense to build some simple custom component on top of the standard one. This would display all the cool features and simultaneously be flexible enough to adapt to different cases without much code modification. For the sake of simplicity in this article, we will consider only sorting and not focus on infinite scrolling.

Use case data model

Let’s build a custom employee list application in Lightning using Datatable. This table view will display First Name, Last Name, Date of Birth, Hire Date, Branch/Department, Position, Email, Employee’s phone number.

Let’s use the Contact standard object as a source of data, but also create a separate Employee record type to distinguish employee data from other contacts types in Salesforce organization.

Setting mock data using generation framework

To demonstrate, we need some mock data. It is obvious that Salesforce itself generates mock data in the new developer edition organization registration. Sometimes, ETL tools or automation scripts are used to populate mock data. However, instead of using some additional software to prepare demonstration data, it is possible to write some simple native Apex code for it.

For the following screenshot, custom Apex generation framework was used with the following generation string to create mock data.

Contact: RecordTypeId=r.Employee, FirstName=rvs.firstNames, LastName=rvs.lastNames, BirthDate=random.date, HireDate__c=random.date, Branch__c=random.picklist, Position__c=random.picklist, Phone=random.ukrainian.cell.phone, Email=builder.FirstName[0]+LastName+@gmail.com

The generation framework creates random Contact data with Employee record type. It randomly takes FirstName and LastName from a predefined bank of data, and sets Birth Date and Hire Date to random date values. Also, it sets Branch and Position to random values found in a picklist, and sets phones to random cell numbers, which follows local cell phone number patterns. To populate email values, the first letter from FirstName is taken concatenated to LastName and string literal.

Using Lightning Promises

Simple Data Table Apex Controller Code Snippet

In order to implement this app, we need first to implement Simple Data Table component and its Apex Controller. Let’s start from Apex Controller.

/**
 * Created by Bohdan Dovhan on 1/26/2018.
 */
public with sharing class SimpleDataTableController {
    
    @AuraEnabled
    public static Map<String, Object> getColumnsAndData(
            String sObjectName, List<String> sObjectFieldsNames, String whereClause
    ) {
        try{
            Map<String, Schema.SObjectField> m = Schema.describeSObjects(sObjectName.split(','))[0].fields.getMap();
            List<Object> columns = new List<Object>();
            for ( String fieldName: sObjectFieldsNames ) {
                Schema.DescribeFieldResult dfr = m.get( fieldName ).getDescribe();
                Map<String, Object> column = new Map<String, Object>{
                    'label' => dfr.getLabel(),
                    'fieldName' => dfr.getName(),
                    'type' => String.valueOf( dfr.getType() ).toLowerCase(),
                    'sortable'=>true
                };
                columns.add( column );
            }
            String query = 'SELECT ' + String.join( sObjectFieldsNames, ', ') + ' FROM ' + sObjectName;
            if (!String.isBlank(whereClause)) {
                query += ' WHERE ' + whereClause;
            }
            
            List<SObject> records = Database.query( query );
            return new Map<String, Object>{
                'columns' => columns,
                'data' => records
            };
        } catch(Exception e) {
            throw AuraUtils.buildAuraHandledException(e);
        }
    }
}

This class has one static aura enabled by the getColumnsAndData method, which returns columns and data as values of two properties of a common map. It takes three parameters. SObject name will be Contact for our use case. Fields names list will contain fields which we mentioned before in the data model section. Clause parameter would restrict results only to data having Employee record type.

The code automatically prepares data and columns parameters required for the standard Lightning DataTable component. Also if any exception happens, it is converted to AuraHandledException which can be gracefully displayed by Lightning framework.

Simple Data Table Lightning Component Code Snippet

Now let’s build the SimpleDataTable Lightning component.

<aura:component controller="SimpleDataTableController">
	<aura:attribute name="sObjectName" type="String" description="Input attribute to define which sObject to use" required="true"/>
    <aura:attribute name="sObjectFieldsNames" type="List" description="Input attribute to define which sObject fields to use" required="true"/>
	<aura:attribute name="whereClause" type="String" description="Input attribute to define where clause" required="false"/>
    <aura:attribute name="data" type="Object"/>
    <aura:attribute name="columns" type="List"/>
    <aura:handler name="init" value="{! this }" action="{! c.init }"/>
    <lightning:datatable data="{! v.data }" columns="{! v.columns }" keyField="id" hideCheckboxColumn="true" onsort="{!c.updateColumnSorting}"/>                
</aura:component>

This component is based on the standard DataTable component, but it prepares required data so that the developer does not need to pass the data or columns here, but rather specifies only the name of SObject, fields list and the clause where the required data can be found. Sorting is also enabled here.

Let’s build Lightning Javascript controller for this component.

 ({
	init : function(component, event, helper) {
	    helper.callActionAsPromise(
	        component,
	        helper,
	        'c.getColumnsAndData',
	        {
	            'sObjectName': component.get('v.sObjectName'),
	            'sObjectFieldsNames': component.get('v.sObjectFieldsNames'),
	            'whereClause': component.get('v.whereClause')
	        }
	    ).then(function(r) {
	        component.set('v.columns', r.r.columns);
	        component.set('v.data', r.r.data);
	    })
	},
    
    // Client-side controller called by the onsort event handler
    updateColumnSorting: function (cmp, event, helper) {
        var fieldName = event.getParam('fieldName');
        var sortDirection = event.getParam('sortDirection');
        // assign the latest attribute with the sorted column fieldName and sorted direction
        event.getSource().set("v.sortedBy", fieldName);
        
        event.getSource().set("v.sortedDirection", sortDirection);
        helper.sortData(cmp, fieldName, sortDirection);
    }
})

On init apex controller, getColumnsAndData is called as a promise. This allows the code to look synchronous by calling then method. updateColumnSorting calls another helper method to perform sorting on client side.

Let’s discuss the Lightning Javascript Helper code next.

({
	callActionAsPromise : function(component, helper, actionName, params) {
        return new Promise($A.getCallback(function(resolve, reject) {
            let action = component.get(actionName);
            action.setParams(params);
            action.setCallback(helper, function(actionResult) {
                if (actionResult.getState() === 'SUCCESS') {
                    resolve({'c':component, 'h':helper, 'r':actionResult.getReturnValue()});
                } else {
                    let errors = actionResult.getError();
                    reject(new Error(errors && Array.isArray(errors) && errors.length === 1 ? errors[0].message : JSON.stringify(errors)));
                }
            });
            $A.enqueueAction(action);
        }));
    },
    
    sortData: function (cmp, fieldName, sortDirection) {
        var data = cmp.get("v.data");
        var reverse = sortDirection !== 'asc';
        //sorts the rows based on the column header that's clicked
        data.sort(this.sortBy(fieldName, reverse))
        cmp.set("v.data", data);
    },
    sortBy: function (field, reverse, primer) {
        var key = primer ?
            function(x) {return primer(x[field])} :
            function(x) {return x[field]};
        //checks if the two rows should switch places
        reverse = !reverse ? 1 : -1;
        return function (a, b) {
            return a = key(a), b = key(b), reverse * ((a > b) - (b > a));
        }
    }
})

The first method, callActionAsPromise, can be moved to some basic abstract component for a complex project and reused to call any apex server side action as a promise. It checks the action result state and resolves if state is success and rejects otherwise.

The sorting methods sortData and sortBy completely coincide with documentation sample methods. Only the javascript controller method for sorting was changed. One should note that samples from Salesforce documentation do not always work out of the box, and sometimes require slight changes to make them work properly.

Employee list component and app Code Snippet

Now we can move on to the final component code snippet. It looks very slim.

<aura:component >
	<c:SimpleDataTable sObjectName="Contact"
                 sObjectFieldsNames="FirstName,LastName,BirthDate,HireDate__c,Branch__c,Position__c,Email,Phone"
                 whereClause="RecordType.Name = 'Employee'"
    />
</aura:component>

and so does the application code

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

The Lightning SimpleEmployeeListApp standalone application consists of one SimpleEmployeeList component, which just refers the SimpleDataTable component setting sObjectName to Contact, fields to a predefined list of fields, and populating where clause restricts data results to only employee data. Now we can click and sort by the first name column.

Using Lightning Promises

Notice that data is sorted by FirstName in ascending order. Also, we can click email links to launch the default email application as well as clicking phone links to prompt for phone link application launch. By clicking again on the FirstName column, we will get the data sorted in descending order.

Using Lightning Promises

In the considered use case, we required neither a custom checkbox column nor custom radio button column. We also did not depend on other requirements which may not be supported by standard Lightning DataTable component. For complicated use cases, more custom code might be needed, or alternative open source components might be used, which will support requirements for such use cases.

Tags

CoreValueDatatableLightningSalesforce

Share


Recent Articles

CoreValue Among Top 15 Big Data Analytics Companies 2018

December 14, 2018

CoreValue is proud to be included in the Top 15 Big Data Analytics Companies List according to TopDevelopers.co. TopDevelopers.co is a leading directory for mobile app, web and software, and digital marketing service agencies. They analyse businesses and firms to expose the actuality of current business needs and the trends that are prolific in offering […]

CoreValue Among 500 Best Software Companies Worldwide

December 12, 2018

This year CoreValue joins the 2018 Software 500 list along with IBM, Microsoft, Dell, Apple, Oracle, SAP. The list honors a success of the best software companies in the world and demonstrates top service providers. The inclusion in the Software 500 list is a great honour for CoreValue as it gives national and global recognition […]