Salesforce1 Lightning

Multiple Actions Batching in Lightning Framework

Posted on Updated on

Hi All,

Today I am going to tell you about multiple actions batching when you are working with multiple Lightning Components in an Application.

Most component based technologies allow independent components to maintain a communication channel with their back-end services unfortunately this can result in redundant messaging specially when it constrain by narrow band width on mobile devices.

Lightning Component Framework is design to optimize calls to Salesforce back-end. There could be many separate actions fired from multiple components in any application but all the actions are processed in a single batch this allow us for very efficient processing.

batchingevents

As actions are instantiated and configured they are enqued in a batch.The framework execute each action in the queue after batching up related request.

Now rather then sending separate request for each individual action a single call is made to the server with batch of asynchronous actions each containing the information they need and fire multiple apex processes.

As it is possible for certain actions to be fired more than once the framework also can ensure that only the most recent instance of any action will be batched for processing.

The related action methods are invoked and the framework than gathers and sends back a batch of results again in a single call invoking all related callback functions.

Hope this will be helpful 🙂

How do Lightning Components interact with Salesforce?

Posted on

Hi All,

Today one of my blog visitor asked me, how do Lightning Components interact with Salesforce? How does Lightning component get data from server? I thought to write an article on this, it might help you also to understand the architecture of Lightning Component Framework.

Below image will help you in understanding this.

salesforce

 

  1. Whenever a Lightning Component or an App runs on browser of Mobile device, it first download all component resources(i.e. Styles, Scripts, Markup) on mobile device. They are contained in browser’s memory to support display and processing that is why we call it client-side.
  2. Now if you want to call a apex method from client side, you will fire an event on client side. This event will call a JavaScript method on client-side. This client-side method will make an HTTP requests to Salesforce to call apex method on server-side. Like you do in VisualForce using action function or other JavaScript.
  3. This HTTP request to call apex method will be asynchronous. Asynchronous means you will make the request and forget about it and the controller returns to the client. The processing will be done on server-side.
  4. In the meantime Salesforce processes the logic by executing some apex.
  5. Finally when it is done with processing and gets some resulted data or not, it makes a callback to the browser/client-side. The client response by invoking an associated JavaScript callback function which extract data passed back by apex methods. This data can be assigned to component attributes. Now display will be automatically refreshed.

Hope this will be helpful 🙂

VanillaJs For Better Performance of Lightning Components

Posted on Updated on

Hi All,

Lightning is all about components. You can build applications by assembling components created by you and other developers. A component can contain other components, as well as HTML, CSS, JavaScript, or any other Web-enabled code.

If you have multiple Lightning Components on a Lightning Page/App with 3rd party libraries like jQuery,Bootstrap etc, you may face performance issues like slow loading, code conflicts, many others you don’t know.

Problems you may face while working with 3rd party libraries :

If you have 2 Lightning Components with different version of Libraries, they may conflict with each other.
If you are using same version, they may put unnecessary weight on Components which in turn load your component slowly.

So what could be the solution? The solution is to ensure that you really need to load 3rd party libraries or not.

Basically you should always avoid use of jQuery for very small things like getting element Id, Class, DOM manipulation and many others. To do these small things you have to import jQuery file in you component. Which will put unnecessary weight on your component.

Instead of this you can use getter setter methods provided by Framework or you can use VanillaJs.

You might be thinking what is “VanillaJs”?
Vanilla JavaScript is a fancy name developers have given to the core JavaScript and DOM API combined, and it is used to distinguish it from libraries such as jQuery.

VanillaJs is very easy to use, it is a fast, lightweight, cross-platform framework for building incredible, powerful JavaScript applications. You can read more about here.

VanillaJs is faster than jQuery see below comparison.

vanilla

I know you may have to write few more lines of code in comparison to jQuery, but it is better than putting unnecessary weight on components by importing library.

Comparison of jQuery and Vanilla JS for basic DOM manipulation.

Class selectors

// jQuery
$('.myClass');

// VanillaJs 
document.getElementsByClassName('myClass');

ID selectors

// jQuery
$('#myID');

// VanillaJs
document.getElementById('myID');

Tags

// jQuery
$('div');

// VanillaJs
document.getElementsByTagName('div');

Add and Remove Class

// jQuery
$('div').addClass('myClass');

// VanillaJs
var div = document.querySelector('div');
div.classList.add('myClass');

// jQuery
$('div').removeClass('myClass');

// VanillaJs
var div = document.querySelector('div');
div.classList.remove('myClass');

Add and Remove Attribute

// jQuery
$('.myClass').attr('disabled', true);

// VanillaJs
document.querySelector('.myClass').setAttribute('disabled', true);

// jQuery
$('.myClass').removeAttr('disabled');

// VanillaJs
document.querySelector('.myClass').removeAttribute('disabled');

Hope you will find VanillaJs better than jQuery with Lightning Components.

Thanks 🙂

DropBox Lightning Component

Posted on Updated on

Hi All,

Today i will be showing you DropBox Lightning Component created by me and my colleague Sushil Kumar.

Dropbox is a file hosting service operated by Dropbox, Inc., headquartered in San Francisco, California,
that offers cloud storage, file synchronization, personal cloud, and client software. There are limits in storage of files in Salesforce. If you need a secure and free storage space for your files, you can use DropBox Lightning Component to store files directly in your DropBox app from Salesforce.

Features of this Lightning Component are :

  1. You can use this component on any sObject.
  2. You can store files for a specific record.
  3. You can upload multiple files.
  4. You can delete files directly in DropBox.
  5. You can view you files.

Click here to install Manage Package in your org.

After installing package register for My Domain.

Make sure you have DropBox Account. If you don’t have you can sign up and create DropBox App as below.

Sign up Dropbox account from DropBox website.

After the Sign up login into your dropbox account and then create a Dropbox App using following steps:

  1. Go to on dropbox developer edition i.e. https://www.dropbox.com/developer
  2. Click on My Apps > Create App.
  3. Then fill all information about app and then click on create. See below screen shot.

dropbox

Manage Dropbox Key from Custom setting

Insert a record in custom setting using following steps:

  1. Navigate to Setup > Develop > Custom Settings, click on Manage of “Dropbox Key”.
  2. Click on New, than insert following value:

App Key : from DropBox App

App Secret : from DropBox App

Redirect URI : from salesforce <your domain>/apex/DropBoxOAuth or copy url from preview of DropBoxOAuth page.

isAuthentication : false

Click on Save.

Add Redirect URI in Dropbox App

To add Redirect URI, Open your Dropbox App and you must set auth2 Redirect URIs same as above Redirect URL.

That’s It.

Your final result will look like this.

Thanks. 🙂

Understanding Server Side Errors In Lightning

Posted on Updated on

Hi All,

Every Salesforce developer might have faced server-side/apex errors or exceptions while running VF pages or Lightning Components. I have also faced errors while running VF pages and Lightning Components. It is easy to understand and handle errors on VF page but there is difference when you are working with Lightning Components.

If there is any error on VF page, it will show you user friendly or appropriate error message with line number, when you are not handling it. And if you are handling this exception, you can easily show them using or .

When you are running Lightning Components and if there is any server side error occur it will not show you error on browser. I don’t know why, maybe Lightning is still improving that’s why.

Run below code in your org.

//Apex Controller
public class DemoExceptionController{

    @AuraEnabled
    public static String getServerErrorMessage(){
		//DML Exception
        contact con = new contact();
        insert con;
		
		return 'anything';
	}
}
//ExceptionDemo.cmp
<aura:component controller="DemoExceptionController">
    
    <aura:attribute name="message" type="String"/>
	
    <ui:button label="Show Error" press="{!c.showError}"/>
    <br/>
	Error Message :<ui:outputText value="{!v.message}"/>
	
</aura:component>
//ExceptionDemoController.js
({
	showError : function(component, event, helper) {
		var action = component.get("c.getServerErrorMessage");
		
        action.setCallback(this, function(a) {
            component.set("v.message", a.getReturnValue());
		});
		$A.enqueueAction(action);
	}
})

When you click on “Show Error” button a DML exception will occur on server. This error should be shown on browser by system but it is not shown. I modified ExceptionDemoController.js to manually show server side errors.

({
	showError : function(component, event, helper) {
		var action = component.get("c.getServerErrorMessage");
		
        action.setCallback(this, function(a) {
            component.set("v.message", a.getReturnValue());
            
			if (a.getState() === "SUCCESS") {
				component.set("v.message", a.getReturnValue());
			} else if (a.getState() === "ERROR"){
                console.log(a.getError());
                var errors = a.getError();
				if(errors[0] && errors[0].pageErrors)
					component.set("v.message", errors[0].pageErrors[0].message);    
			}
			
        });
		$A.enqueueAction(action);
	}
})

Now when you click on “Show Error” button it will show you user friendly error message.

Now modify Apex Controller and ExceptionDemoController.js to show different type of error.

//Apex Controller
public class DemoExceptionController{

    @AuraEnabled
    public static String getServerErrorMessage(){
		//List Exception        
        List<Integer> li = new List<Integer>();
        li.add(15);
        Integer i2 = li[1]; // Causes a ListException
	}
}

//ExceptionDemoController.js
({
	showError : function(component, event, helper) {
		var action = component.get("c.getServerErrorMessage");
		
        action.setCallback(this, function(a) {
            component.set("v.message", a.getReturnValue());
            
			if (a.getState() === "SUCCESS") {
				component.set("v.message", a.getReturnValue());
			} else if (a.getState() === "ERROR"){
                console.log(a.getError());
                var errors = a.getError();
                if(errors[0] && errors[0].message)// To show other type of exceptions
                    component.set("v.message", errors[0].message);
                if(errors[0] && errors[0].pageErrors) // To show DML exceptions
            		component.set("v.message", errors[0].pageErrors[0].message);    
			}
        });
		$A.enqueueAction(action);
	}
})

Now when you click on “Show Error” button, this time it will not show you correct user friendly message instead of this “An internal server error has occurred” message will be shown. I tried with other type of exceptions to see the cases in which “An internal server error has occurred” message will be shown and in which correct user friendly message will be shown. In every case “An internal server error has occurred” message is shown except DML exception.

If you want to show appropriate error message you can handle exception to return appropriate error message.

//Apex Controller
public class DemoExceptionController{

    @AuraEnabled
    public static String getServerErrorMessage(){
		try{
            List<Integer> li = new List<Integer>();
            li.add(15);
            Integer i2 = li[1]; // Causes a ListException
        }catch(Exception e){
            return e.getMessage();
            
        }
	}
}

I know this is not a right way to show error message but I am just giving you an idea to show appropriate error message. You can write your own logic according to your requirement.

Thanks 🙂

Understanding Expressions in Lightning

Posted on Updated on

Hi All,

Yesterday I stuck with Expressions in Lightning. While solving this I came to know about different ways of using Expressions and different types of these.

An expression is any set of literal values, variables, sub-expressions, or operators that can be resolved to a single value. You can use expressions for dynamic output or passing values into components by assigning them to attributes. Expressions are also used to provide action methods for user interface events(i.e. OnClick,OnHover).

Bound Expressions

Bound expression syntax is: {!expression}. Curly braces with “!” bang operator is used for Bound expressions.

If you are making any change using Bound expression it will immediately reflected on your component. Bound expressions are used to reflect changes on your Lightning Component. Below are examples of Bound expressions in component.

<aura:component>
	
	<!-- Accessing Fields and Related Objects -->
	<p>{!v.accounts.Name}</p>
	
	<!-- Concatinate value with String -->
	<p>{!'Hello ' + v.msg}</p>
	
	<!-- Ternary Operator -->
	<ui:button aura:id="likeBtn" label="{!(v.likeId == null) ? 'Like It' : 'Unlike It'}" />
	
	<!-- Conditional Markup -->
	<aura:attribute name="edit" type="Boolean" default="true"/>
	<aura:if isTrue="{!v.edit}">
		<ui:button label="Edit"/>
	<aura:set attribute="else">
		You can’t edit this.
	</aura:set>
	</aura:if>
	
	<!-- Adding two items -->
	{!(v.item1) + (v.item2)}
	
	<!-- Using Global Variables -->
	{!$Browser.isPhone}
	{!$Locale.timezone}
	
	<!-- Action Method -->
	<ui:button aura:id="likeBtn" label="Press Me" press="{!c.update}"/>
	
	<!-- Array Length -->
	{!v.myArray && v.myArray.length > 0}
	
</component>

Unbound Expressions

Unbound expression syntax is: {#expression}. Curly braces with “#” hash operator is used for Unbound expressions.

If you are making any change using unbound expression, it will not immediately reflect on your component. If you don’t want to reflect changes, you can use unbound expressions.

Note : Unbound expression changes are not reflected on component but they can be seen by javascript.

Below example show use of unbound expression in component.

//Component1.cmp
<aura:component>
    <aura:attribute name="truthy" type="Boolean"></aura:attribute>
     
    <aura:if isTrue="{#v.truthy}">
        True
    <aura:set attribute="else">
        False
    </aura:set>
    </aura:if>
    <ui:button aura:id="button" label="Check" press="{!c.update}"/>
</aura:component>
 
//Component1Controller.js
({
    update : function(component, event, helper) {
        component.set("v.truthy","false");
		console.log('----> '+component.get("v.truthy"));
    }
})
 
//Component1Renderer.js
({
    rerender: function(cmp, helper) {
        console.log('rerender'); 
        return this.superRerender()
    },
})

In above code when you click on “Check” button it will udpate unbound expression {!#v.truthy} in javascript controller. You will be able to see changes in console but they are not reflected on component.

Hope this will be helpful. Happy coding 🙂

Lightning Component Best Practices PART 2

Posted on

Hi All,

Few days before I wrote an article on best practices of Lightning Components. In that article I told you about best practices of Lightning Component Bundle(Controller, Helper, Renderer) and Events. Today I will tell you about best practices to create secure Lightning Components.

In Winter 16 release Salesforce introduced an important change to Lightning Component, In order to run Lightning Components in your org, you will be required to use the My Domain feature.

Why My Domain? At Salesforce, Trust is their number one value. The security team that reviews all changes to the Force.com platform have determined that requiring of My Domain will allow Salesforce to enact even better security around Lightning Components. And better security is good for everyone.

Because of this new feature insecurity is not neccessorily coming from Lightning Components but can come from JavaScript(Client Side) or Apex(Server Side). All the insecurities may come from JavaScript rather than from Lightning Component. Reason for this is the JavaScript from same domain can actually access,modify and read everything. You should follow below best practices to avoid insecurities from Client Side and Server Side. I tried to explore them.

Client Side

  • When modifying the DOM, avoid the use of HTML rendering functions such as innerHTML or $().append(). Rather use component getters and setters whenever possible.
  • //Component1.cmp
    <aura:component>
    	<aura:attribute name="msg" type="String"></aura:attribute>
    	<div><b>{!v.msg}</b></div>
    </aura:component>
    
    //Component1Helper.js
    ({
    	changeValue : function(component) {
    		component.set("v.msg","Changed value before Render"); // Modifying DOM using setter
    	},
    })
    
    //Component1Renderer.js
    ({
    	render: function(cmp, helper) {
    	   console.log('render');
    	   helper.changeValue(cmp); // Modifying DOM in renderer
    	   return this.superRender()
    	},
    })
    

    In above code I am modifying DOM using setter.

    The DOM may only be modified in a renderer. Outside a renderer, the DOM is read-only. Suppose if you are trying to modify DOM in a controller than you are playing out side of the cycle of aura. If you do modification in controller the renderer will wipe all the DOM modification and you will end up with no result.

    In above code I am modifying DOM in renderer.

    Note : JS code within a component can only modify DOM elements belonging to the component. For example, it cannot modify document head or directly access the DOM element belonging to another component. In order to modify a parent or sibling component, an event should be passed to the target component. Child components should only be accessed via their attributes, and not by accessing the DOM of the child component directly.

  • To change styles dynamically, use $A.util.toggleClass() or $A.util.addClass(),$A.util.removeClass().
    Use $A.util.toggleClass() for component instead of a DOM element.
  • //toggleCss.cmp
    <aura:component>
    	<div aura:id="changeIt">Change Me!</div><br />
    	<ui:button press="{!c.applyCSS}" label="Add Style" />
    	<ui:button press="{!c.removeCSS}" label="Remove Style" />
    </aura:component>
    //toggleCssController.js
    ({
    	applyCSS: function(cmp, event) {
    		var cmpTarget = cmp.find('changeIt');
    		$A.util.addClass(cmpTarget, 'changeMe');
    	},
    	removeCSS: function(cmp, event) {
    		var cmpTarget = cmp.find('changeIt');
    		$A.util.removeClass(cmpTarget, 'changeMe');
    	}
    })
    //toggleCss.css
    .THIS.changeMe {
    	background-color:yellow;
    	width:200px;
    }
    
  • Use $A.getCallback() to wrap any code that accesses a component outside the normal rerendering lifecycle, such as in a setTimeout() or setInterval() call or in an ES6 Promise. $A.getCallback() preserves the current execution context and grants the correct access level to the asynchronous code. Otherwise, the framework loses context and only allows access to global resources.
  • window.setTimeout(
    	$A.getCallback(function() {
    		if (cmp.isValid()) {
    			cmp.set("v.visible", true);
    		}
    	}), 5000
    )
    

    This sample sets the visible attribute on a component to true after a five-second delay.

  • Avoid the use of inline javascript except when referencing JS controller methods.
  • <div onmouseover="myfunction" >foo</div> //bad
    <div onmouseover="c.myControllerFunction" >foo</div> //OK
    
  • Do not overwrite window or document functions.
  • window.onload = function () {
       //some code
    }
    document.write = function() {
       //some code
    }
    
  • Don’t use Script or Link tag to include JavaScript or CSS file. Instead of this use use the [ltng:require] aura component.
  • <link type='text/css' rel='stylesheet' href='YOUR_CSS_FILE.css' /> //bad
    <script src="YOUR_JS_FILE.js"></script> //bad
    
    <ltng:require styles="CSS FILE 1,CSS FILE 2,..." scripts="JS FILE 1,JS FILE 2,.."></ltng:require> //OK
    
  • Avoid using absolute URL use relative URL with ‘/’ and encodeURL.
  • Events may only be fired within a controller or component file, but not in a renderer. If you fire an event in renderer the event will go and call the controller again and controller will instantiate again,setup data again and call renderer again and cycle will repeat. This will end up with an infinite loop.
  • Avoid using component. This component outputs value as unescaped HTML, which introduces the possibility of security vulnerabilities in your code. You must sanitize user input before rendering it unescaped, or you will create a cross-site scripting (XSS) vulnerability. Only use with trusted or sanitized sources of data.

Server Side

  • All controller classes must have the with sharing keyword. There are no exceptions. In some cases, your code will need to elevate privileges beyond those of the logged in user.
  • public class DemoController() {  //Unsafe
        
        @AuraEnabled
        public static String getSummary() {
            //code
        }
    }
    
    public with sharing class DemoController() {  //safe
        
        @AuraEnabled
        public static String getSummary() {
            //code
        }
    }
    
  • CRUD/FLS permissions are not automatically enforced in lightning components or controllers, nor can you rely on lightning components to enforce security (as the client is under the control of the attacker, so all security checks must always be performed server-side). You must explicitly check for for isAccessible(), isUpdateable(), isCreateable(), isDeletable() prior to performing these operations on sObjects.
public with sharing class DemoController() {
    
    @AuraEnabled
    public static Contact getContact(String fieldName) { //safe
        if(Schema.SObjectType.Contact.fields.getMap().get('Name').getDescribe().isAccessible()){
			//code
		}
    }
}

Above best practices will help your Lightning Components in passing through security review of AppExchange.

Hope this will be helpful cheers!! 🙂

Different Rendering Behavior Of Lightning Component

Posted on Updated on

Hi All,

In this article I will tell you how and when you can use/customize different Renderer.js functions of a Lightning Component. There are different types of renderer functions which are called automatically when a Component is Render, Rerender or Unrender. You can customize these functions to change default rendering behaviour of Component.

Let’s explore these functions.

Render

The framework calls render() function when component is being initialized/rendered. The render() function typically returns a DOM node, an array of DOM nodes, or nothing. The base HTML component expects DOM nodes when it renders a component. Use render function to modify DOM or to modify component markup.

//Component1.cmp
<aura:component>
	<aura:attribute name="msg" type="String"></aura:attribute>
	<div><b>{!v.msg}</b></div>
</aura:component>

//Component1Helper.js
({
	changeValue : function(component) {
		component.set("v.msg","Changed value before Render");
	},
})

//Component1Renderer.js
({
	render: function(cmp, helper) {
	   console.log('render');
	   helper.changeValue(cmp);
	   return this.superRender()
	},
})

In render() function I am calling helper method to modify DOM when component renders.

AfterRender

The afterRender function will be called by framework after render() function.AfterRender function allows us to further change an already rendered component. The afterRender() function enables you to interact with the DOM tree after the framework’s rendering service has inserted DOM elements.

Note :

  1. Both the functions will be called only once, you can keep your business logic that needs to run only once in these functions.
  2. Both functions can be used to modify DOM or markup.
  3. Don’t perform DML in render function. You can do in afterRender.

Rerender

Rerender function will be called when a component’s value changes due to user action like a button click or some other event like component/application event. Rerender function updates component upon rerendering of the component.

//Component1.cmp
<aura:component>
	<aura:attribute name="truthy" type="Boolean"></aura:attribute>
	
	<aura:if isTrue="{!v.truthy}">
		True
	<aura:set attribute="else">
		False
	</aura:set>
	</aura:if>
	<ui:button aura:id="button" label="Check" press="{!c.update}"/>
</aura:component>

//Component1Controller.js
({
	update : function(component, event, helper) {
		component.set("v.truthy","false")
	}
})

//Component1Renderer.js
({
	rerender: function(cmp, helper) {
		console.log('rerender'); 
		return this.superRerender()
	},
})

In above component when you click on “Check” button,update method will update component which in turn call rerender.

Unrender

Framework fires an unrender event when a component is deleted.The base unrender() function deletes all the DOM nodes rendered by a component’s render() function. It is called by the framework when a component is being destroyed. Customize this behavior by overriding unrender() in your component’s renderer. This can be useful when you are working with third-party libraries that are not native to the framework.

//Component1.cmp
<aura:component >

	<aura:attribute name="truthy" type="Boolean"></aura:attribute>
	
	<aura:if isTrue="{!v.truthy}">
		<c:Component2 />
	<aura:set attribute="else">
		Second Component is deleted
	</aura:set>
	</aura:if>
	
	<ui:button aura:id="button" label="Check" press="{!c.update}"/>
</aura:component>

//Component1Controller.js
({
	update : function(component, event, helper) {
		component.set("v.truthy","false")
	}
})

//Component1Renderer.js
({
	rerender: function(cmp, helper) {
		console.log('rerender'); 
		return this.superRerender()
	},
})

//Component2.cmp
<aura:component >
	I am in second component
</aura:component>

//Component2Renderer.js
({
	unrender : function (cmp, helper) {
		console.log('Component 2 unrender '); 
		return this.superUnrender();
	},
})

In above code Component2.cmp is deleted when you click on “Check” button on Component1.cmp. When Component2.cmp is deleted framework calls unrender method.

To know more about Best Practices of Lightning Component Rendering click here
Hope this will be helpful 🙂

Who Viewed Record Last Lightning Component

Posted on

Hi All,

In this article I will tell you how to create Lightning Component which shows list of users who viewed record.You can use this Lightning Component for any object whether it is a custom object or standard object.

This can be useful when trying to trace who last Viewed a record? There’s no way to do this in salesforce as of now.It would be great if you could see on a record if anyone has recently viewed it, so I thought to create this Component.

To implement this you have to do following things.
1) On any object you want to use this Lightning Component, create a new custom textarea(255) field called “Last Visited By“.
2) Set History Tracking on for this field.

When complete it will look like this.

Let’s walk through code.

LastVisitedController.apxc :

public with sharing class LastVisitedController{
    
    @AuraEnabled
    public static sObject getViewersList(String recordId, String objectName){
        System.debug('objectName = '+objectName +' recordId = '+recordId);
        
        String query = 'SELECT Id, Name, '+
                       '(Select OldValue, NewValue, Field From Histories Where Field = \'Last_Visited_By__c\' ORDER BY CreatedDate DESC) '+
                       'FROM '+objectName +' Where Id = \''+ recordId + '\' LIMIT 1' ;
        
        sObject obj = Database.Query(query);
        
        return obj;
    }
    
    @AuraEnabled
    public static void updateRecord(String recordId, String objectName){
        Datetime sdate = System.now();        //Format the datetime value to your locale        
        String sysTime = sdate.format('dd/MM/yyyy HH:mm');
    
        String query = 'SELECT Id, Name, Last_Visited_By__c '+
                        'FROM '+objectName +' Where Id = \''+ recordId + '\' LIMIT 1' ;
        
        sObject obj = Database.Query(query);
        obj.put('Last_Visited_By__c' , System.Userinfo.getFirstName() + ' '+ System.Userinfo.getLastName() +' '+ sysTime);
        update obj;
    }
    
}

The getViewersList method is used to return list of users who visited record recently. Method updateRecord is used to update “Last Visited By” field when a user visited record.

LastVisitedCmp.cmp :

<aura:component controller="LastVisitedController" implements="force:appHostable,force:hasRecordId,force:hasSObjectName,flexipage:availableForAllPageTypes">
    <ltng:require styles="/resource/SLDS/assets/styles/salesforce-lightning-design-system-vf.css"/>
    <!-- Atrribute Defination for Object Name -->
	<aura:attribute name="sObjectName" type="String"></aura:attribute>
    <!-- Atrribute Defination for Record Id -->
	<aura:attribute name="recordId" type="String"></aura:attribute>
    <!-- Atrribute Defination for Who visited -->
	<aura:attribute name="visitorsList" type="sObject"></aura:attribute>
    <!-- Event Handler for init Event-->
    <aura:handler name="init" value="{!this}" action="{!c.doInit}"></aura:handler>
    <!--page header-->
    <div class="slds">
        <div class="slds-page-header">
            <div class="slds-grid">
                <div class="slds-col slds-has-flexi-truncate">
                    <div class="slds-media">
                        <div class="slds-media__body">
                            <div class="slds-grid">
                                <h1 class="slds-text-heading--medium slds-m-right--small slds-truncate slds-align-middle">Recently Visited By</h1>
                            </div>
                        </div>
                    </div>
                </div>
            </div>   
        </div>
    </div>
    <!--Container-->
    <div class="container" style="overflow-y:scroll;height:300px">
        <ul class="slds-list--vertical slds-has-cards">
            <aura:iteration items="{!v.visitorsList.Histories}" var="h">
                <li class="slds-list__item">
                    <div class="slds-tile slds-tile--board">
                        <div class="slds-tile__detail">
                            <p class="slds-text-heading--medium">{!h.NewValue}</p>
                        </div>
                    </div>
                </li>
            </aura:iteration>	
        </ul>
    </div>
</aura:component>

The force:appHostable interface indicates the component can be hosted in the Salesforce1 Mobile app.
The flexipage:availableForAllPageTypes interface indicates the component can be used in App Builder.
The force:hasRecordId interface indicates the current record Id should be injected in the component’s recordId attribute.
The force:hasSObjectName interface indicates the current sObjectName should be injected in the component’s sObjectName attribute.

When the component is instantiated on the page, the current record Id and sObject are automatically injected in the recordId and sObjectName attribute. You can then make calls to the server passing the recordId and sObjectName as a parameter to retrieve or save data for that specific record.

LastVisitedCmpController.js :

({
	doInit : function(component, event, helper) {
		helper.getViewerList(component);
	},
})

LastVisitedCmpHelper.js :

({
	getViewerList : function(component) {
		var action = component.get("c.getViewersList");
        action.setParams({
            "recordId" : component.get("v.recordId"),
            "objectName" : component.get("v.sObjectName")
        });
        action.setCallback(this, function(action) {
        	component.set("v.visitorsList",action.getReturnValue());
        });
        
        $A.enqueueAction(action);
	},
    updateRecord : function(component) {
		var action = component.get("c.updateRecord");
        action.setParams({
            "recordId" : component.get("v.recordId"),
            "objectName" : component.get("v.sObjectName")
        });
        $A.enqueueAction(action);
	},
})

LastVisitedCmpRenderer.js :

({
    afterRender : function(component,helper){
        this.superAfterRender();
        helper.updateRecord(component);
    }	
})

The afterRender will be called when Component render completly. It will call helper function which updates “Last Viewed By” field of current record in the background.

🙂

Lightning Component Best Practices

Posted on Updated on

Hi All,

In this article, I will tell you the best practices for Lightning Components, Lightning component bundle and Events.

Lightning Component

Lightning is all about components. You can build applications by assembling components created by you and other developers. A component can contain other components, as well as HTML, CSS, JavaScript, or any other Web-enabled code. This enables you to build apps with sophisticated UIs. We should always keep in mind Lightning Component’s modular approach while creating a Lightning App. It is always a best practice to use component based developement approach. Following are the benifits of component based approach.

  1. Increases
    a. Developer Productivity
    b. Feature availablity
    c. Application Scaliblity
  2. Decreases
    a. Application Complexity
    b. Time to Delivery
    c. Overall Cost

Lightning Component Bundle

Each Lightning Component is made up of a markup, JavaScript controller, a Helper, a Renderer and more(Component Bundle).

final00001

Controller

  1. Use Controllers to listen to user events and other events like Component Event, Appliction Event.
  2. Delegate your business logic to helper methods.
  3. Do not trigger DML operation on component initializaton. If you are doing DML in init(), you are creating a CSRF(Cross-Site Request Forgery).
  4. Do not modify DOM in Controller. If you modify DOM in Controller it will call renderer method which will end in no result.

Helper

Always write your business logic in helper functions because

  1. Helper functions may be called from any other javascript in the component bundle.
  2. Whenever a component runs Lightning Framework creates an instance of the Controller, an instance of the Renderer for each component but creates only one copy of the Helper and passes the reference of the Helper into every Controller instance and every Renderer instance. Below picture will make you understand this well.

final00002

Since Helper is shared across everything, it allows us to share and keep logic across of Controllers and Renderers in one place. It also helps us keep logic within Controllers and Renderers lean. Anytime you need to call one controller function from another controller function, move that logic to Helper.

Renderer

  1. Use Renderer whenever you want to customize default rendering, rerendering, afterrendering and unrendering behaviour for a component.
  2. Do not fire an event in renderer, firing an event in a renderer can cause an infinite rendering loop.
  3. If you need to directly manipulate DOM elements in your component, you should do it in the component’s renderer.

Events

  1. Always use events to implement communication between components.
  2. Always try to use a component event instead of an application event, if possible. Component events can only be handled by components above them in the containment hierarchy so their usage is more localized to the components that need to know about them. Below picture will make you understand better about component event.final00003
  3. Application events are best used for something that should be handled at the application level, such as          navigating to a specific record.appevent
  4. It’s a good practice to handle low-level events, such as a click, in your event handler and refire them as higher-level events, such as an approvalChange event or whatever is appropriate for your business logic.
  5. If you have a large number of handler component instances listening for an event, it may be better to identify a dispatcher component to listen for the event. The dispatcher component can perform some logic to decide which component instances should receive further information and fire another component or application event targeted at those component instances.
  6. Do not use onclick and ontouchend events in a component. The framework translates touch-tap events into clicks and activates any onclick handlers that are present.

That’s it for now I will come with more details in upcoming posts. 🙂