Lightning · Salesforce

Implementing Lodash in Salesforce

Recently in my project the requirement needed use of large number of arrays. Initially the thought was to iterate over the array elements using for loop, resulting use of large number of for loops and therefore making the overall process slow. So in search of an alternative found a better approach using the JavaScript library called Lodash. Lodash is simple, effective and in few lines we can iterate over all the array elements. In the beginning Lodash was used for removing duplicates, later on for sorting array elements, finding a particular object in an array of objects, etc. This article is to show how to implement Lodash in Salesforce Lightning (same approach can be implemented in Visualforce pages). Before that let’s discuss:

What is Lodash ?

Lodash is a low-level utility library that offers consistency, customization and performance. It makes JavaScript easier by taking the hassle out of working with arrays, numbers, objects, strings, etc. Lodash’s modular methods are great for:

  • Iterating arrays, objects, & strings.
  • Manipulating & testing values.
  • Creating composite functions.

Using Lodash in Sorting Datatables with individual columns:

Below data table displays list of Account details with fields Name, City, Phone and Revenue. Lightning Design System is used for designing the table and Lodash full build library which is uploaded as static resource used for sorting the items. The sorting arrow icons are SVG components, you can follow the trailhead tutorial to get the icons.

screen-shot-2016-09-07-at-11-30-00-am

Following is the controller used to query the list of account details.

AccountClass.apxc:


public class AccountClass {

@AuraEnabled
public static List<Account> getAccountItems() {

List<Account> accountDetails = [Select Id,Name,BillingCity,Phone,AnnualRevenue from Account];

return accountDetails;
}

}

 

Lightning component is created to display the queried list of Accounts.

sortingCmp.cmp:


<aura:component access="global" controller="AccountClass" implements="force:appHostable,flexipage:availableForAllPageTypes">

<ltng:require styles="/resource/slds0122/assets/styles/salesforce-lightning-design-system.min.css" />
<ltng:require scripts="/resource/lodash/lodash/lodash.js" />

<aura:attribute name="accountItems" type="Account[]" />
<aura:attribute name="BItems" type="Account[]" />
<aura:attribute name="sort1" type="String" default="down" />
<aura:attribute name="sort2" type="String" default="down" />
<aura:attribute name="sort3" type="String" default="down" />
<aura:attribute name="sort4" type="String" default="down" />

<aura:handler name="init" value="{!this}" action="{!c.doInit}" />
<div class="slds">
<div class="container slds-p-top--medium MyContent">
<form class="slds-form--stacked">
<div id="contentTable" class="slds-scrollable--x">
<table class="slds-table slds-table--bordered slds-table--cell-buffer">
<thead>
<tr class="slds-text-heading--label" style="background-color: #27ae60">
<th class="slds-size--3-of-12" scope="col" style="font-weight: 500;color: white;">
<div class="slds-grid slds-wrap">
<span class="slds-truncate" style="padding-top:3px">Name</span>
<div class="slds-icon_container" title="Sort Column">
<ui:button label="" class="slds-button slds-button--icon-bare iconButton" press="{!c.sortByName}">
<aura:if isTrue="{!v.sort1 == 'down'}">
<c:svg ariaHidden="true" class="slds-button__icon iconSVGButton"
xlinkHref="/resource/slds0122/assets/icons/utility-sprite/svg/symbols.svg#arrowdown">
</c:svg>
<aura:set attribute="else">
<c:svg ariaHidden="true" class="slds-button__icon iconSVGButton"
xlinkHref="/resource/slds0122/assets/icons/utility-sprite/svg/symbols.svg#arrowup">
</c:svg>
</aura:set>
</aura:if>
</ui:button>
</div>
</div></th>
<th class="slds-size--3-of-12" scope="col" style="font-weight: 500;color: white;">
<div class="slds-grid slds-wrap">
<span class="slds-truncate" style="padding-top:3px">Billing City</span>
<div class="slds-icon_container" title="Sort Column">
<ui:button label="" class="slds-button slds-button--icon-bare iconButton" press="{!c.sortByCity}">
<aura:if isTrue="{!v.sort2 == 'down'}">
<c:svg ariaHidden="true" class="slds-button__icon iconSVGButton"
xlinkHref="/resource/slds0122/assets/icons/utility-sprite/svg/symbols.svg#arrowdown">
</c:svg>
<aura:set attribute="else">
<c:svg ariaHidden="true" class="slds-button__icon iconSVGButton"
xlinkHref="/resource/slds0122/assets/icons/utility-sprite/svg/symbols.svg#arrowup">
</c:svg>
</aura:set>
</aura:if>
</ui:button>
</div>
</div></th>
<th class="slds-size--3-of-12" scope="col" style="font-weight: 500;color: white;">
<div class="slds-grid slds-wrap">
<span class="slds-truncate" style="padding-top:3px">Phone</span>
<div class="slds-icon_container" title="Sort Column">
<ui:button label="" class="slds-button slds-button--icon-bare iconButton" press="{!c.sortByPhone}">
<aura:if isTrue="{!v.sort3 == 'down'}">
<c:svg ariaHidden="true" class="slds-button__icon iconSVGButton"
xlinkHref="/resource/slds0122/assets/icons/utility-sprite/svg/symbols.svg#arrowdown">
</c:svg>
<aura:set attribute="else">
<c:svg ariaHidden="true" class="slds-button__icon iconSVGButton"
xlinkHref="/resource/slds0122/assets/icons/utility-sprite/svg/symbols.svg#arrowup">
</c:svg>
</aura:set>
</aura:if>
</ui:button>
</div>
</div></th>
<th class="slds-size--3-of-12" scope="col" style="font-weight: 500;color: white;">
<div class="slds-grid slds-wrap">
<span class="slds-truncate" style="padding-top:3px">Annual Revenue</span>
<div class="slds-icon_container" title="Sort Column">
<ui:button label="" class="slds-button slds-button--icon-bare iconButton" press="{!c.sortByRevenue}">
<aura:if isTrue="{!v.sort4 == 'down'}">
<c:svg ariaHidden="true" class="slds-button__icon iconSVGButton"
xlinkHref="/resource/slds0122/assets/icons/utility-sprite/svg/symbols.svg#arrowdown">
</c:svg>
<aura:set attribute="else">
<c:svg ariaHidden="true" class="slds-button__icon iconSVGButton"
xlinkHref="/resource/slds0122/assets/icons/utility-sprite/svg/symbols.svg#arrowup">
</c:svg>
</aura:set>
</aura:if>
</ui:button>
</div>
</div></th>
</tr>
</thead>
<tbody>
<aura:iteration items="{!v.accountItems}" var="Item" indexVar="index">
<tr>
<td class="slds-size--3-of-12">{!Item.Name}</td>
<td class="slds-size--3-of-12">{!Item.BillingCity}</td>
<td class="slds-size--3-of-12">{!Item.Phone}</td>
<td class="slds-size--3-of-12">$ {!Item.AnnualRevenue}</td>
</tr>
</aura:iteration></tbody>
</table>
</div>
</form>
</div>
</div>
</aura:component>

Clicking on the arrow icons triggers javascript functions in the client side controller, Lodash sorting methods are implemented here.

sortingCmpController.js:


({
 doInit : function(component) {
 var action = component.get("c.getAccountItems");
 action.setCallback(this, function(a) {
 if (a.getState() === "SUCCESS") {
 var uniqueItems = _.orderBy(a.getReturnValue(), function(x){
 return x.Name;
 },['asc']);
 component.set("v.accountItems", uniqueItems);
 } else if (a.getState() === "ERROR") {
 $A.log("Errors", a.getError());
 }
 });
 $A.enqueueAction(action);
 },

 sortByName : function(component) {
 var items = component.get("v.accountItems");
 if(component.get("v.sort1") === "down"){
 var uniqueItems = _.orderBy(items, function(x){
 return x.Name;
 },['desc']);
 component.set("v.accountItems", uniqueItems);
 component.set("v.sort1", "up");
 } else {
 var uniqueItems = _.orderBy(items, function(x){
 return x.Name;
 },['asc']);
 component.set("v.accountItems", uniqueItems);
 component.set("v.sort1", "down");
 }
 },

 sortByCity : function(component) {
 var items = component.get("v.accountItems");
 if(component.get("v.sort2") === "down"){
 var uniqueItems = _.orderBy(items, function(x){
 return x.BillingCity;
 },['desc']);
 component.set("v.accountItems", uniqueItems);
 component.set("v.sort2", "up");
 } else {
 var uniqueItems = _.orderBy(items, function(x){
 return x.BillingCity;
 },['asc']);
 component.set("v.accountItems", uniqueItems);
 component.set("v.sort2", "down");
 }
 },

 sortByPhone : function(component) {
 var items = component.get("v.accountItems");
 if(component.get("v.sort3") === "down"){
 var uniqueItems = _.orderBy(items, function(x){
 return x.Phone;
 },['desc']);
 component.set("v.accountItems", uniqueItems);
 component.set("v.sort3", "up");
 } else {
 var uniqueItems = _.orderBy(items, function(x){
 return x.Phone;
 },['asc']);
 component.set("v.accountItems", uniqueItems);
 component.set("v.sort3", "down");
 }
 },

 sortByRevenue : function(component) {
 var items = component.get("v.accountItems");
 if(component.get("v.sort4") === "down"){
 var uniqueItems = _.orderBy(items, function(x){
 return x.AnnualRevenue;
 },['desc']);
 component.set("v.accountItems", uniqueItems);
 component.set("v.sort4", "up");
 } else {
 var uniqueItems = _.orderBy(items, function(x){
 return x.AnnualRevenue;
 },['asc']);
 component.set("v.accountItems", uniqueItems);
 component.set("v.sort4", "down");
 }
 },
})

Next step is to add CSS.

sortingCmp.css

.THIS .MyContent {
 padding-left:20px;
 padding-top:75px;
 width: 90%;
}

.THIS .iconButton{
 box-shadow: none;
 font-weight: normal;
}

.THIS .iconSVGButton{
 fill: white;
}

In the last step component is called from the Lightning application.

sortingApp.app


<aura:application >
<c:sortingCmp />
</aura:application>

In the Preview click on the column buttons to get the sorted lists.

screen-shot-2016-09-07-at-11-30-44-am

To remove duplicates:

Scenario 1: Let us take the same example as above displaying the list of objects. Now say it has duplicates as shown in the below image and we want to remove this duplicates. Instead of iterating with for loops and if conditions we can use Lodash to get the expected results.

screen-shot-2016-09-07-at-8-30-39-pm

Use the below code in the JavaScript controller to remove the duplicates.

removeDuplicates.js

removeDuplicates: function(component) {
var items = component.get("v.accountItems");
var uniqueItems = _.uniqWith(items, _.isEqual);
component.set("v.accountItems", uniqueItems);
},

Scenario 2: Suppose we have objects with one particular field being same, say name field and we want this field to be unique.

screen-shot-2016-09-07-at-8-20-52-pm

To remove the object with duplicate names use the below code.

removeDupsByField.js

removeDupsByField: function(component) {
var items = component.get("v.accountItems");
var uniqueItems = _.uniqBy(items, function(temp) {
return temp.Name;
});
component.set("v.accountItems", uniqueItems);
},

The method which we used for removing duplicates keeps the first occurrence of the element and removes the other. In this example we will have object with city Austin kept and object with city Burlington removed. If you want the opposite to happen i.e. Burlington kept and Austin removed, first we have to reverse the array and use the remove duplicate method. Following is the code for reversing an array.

reverseElements.js

reverseElements: function(component) {
var items = component.get("v.accountItems");
var reverseItems = _.reverse(items);
component.set("v.accountItems", reverseItems);
}

Finding particular object in an array of objects:

Scenario 1: We have B which is a single object and A which is list of objects. To find at which index B is located in list of objects A we can use Lodash.

screen-shot-2016-09-08-at-6-58-54-pm

findOneObject.js

findOneObject: function(component) {
var listA = component.get("v.accountItems");
var objB = _.find(listA, function(temp) {
if (temp.Name === "Express Logistics and Transport") {
return temp;
}
});
if (objB !== undefined) {
var index = _.indexOf(listA, objB);
console.log("Index of element: "+index);
}
},

Scenario 2: We have two list of objects A, B and say B is subset of A. I want to find where the elements of B are located in A. We can find the indexes of elements of B located in A using below code

screen-shot-2016-09-08-at-6-54-26-pm

findObjectsIndex.js

findObjects: function(component) {
var listA = component.get("v.accountItems");
var listB = component.get("v.BItems");
for (var j = 0; j < listB.length; j++) {
var obj = _.find(listA, function(temp) {
if (temp.Name === (listB[j]).Name) {
return temp;
}
});
if (obj !== undefined) {
var index = _.indexOf(listA, obj);
console.log("Index of element: "+index);
}
}
},

Similarly we have many other functions in Lodash which can be useful. Other important thing is Lodash works even when Salesforce Locker Service is enabled. This article shows only few methods of Lodash and according to our requirement we can use other methods, implementing them is as similar as the methods shown here.

Related links:

Lodash Documentation

View complete code on Sorting in GitHub

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s