Tag Archives: Lightning Framework

Wrapper Class In Lightning Component

Lightning Component:

<!--
Name: ContactList.cmp
--> 
<aura:component controller="ContactController">
    <!--Declare Event Handlers-->  
    <aura:handler name="init" action="{!c.doInit}" value="{!this}"/>
    
    <!--Declare Attributes-->
    <aura:attribute name="contactList" type="ContactController.ContactWrapper[]" />   
    <aura:attribute name="isSelectAll" type="boolean" default="false"/>
    
    <div class="slds-m-around_xx-large">
        <h1 class="slds-text-heading--medium">Contacts</h1>
        <br/>
        <!--Contact List Table-->
        <table class="slds-table slds-table--bordered slds-table--cell-buffer" role="grid">      
            <thead>  
                <tr class="slds-text-title--caps">
                    <th>           
                        <label class="slds-checkbox">
                            <ui:inputCheckbox value="{!v.isSelectAll}" change="{!c.handleSelectAllContacts}" aura:id="selectAll"/>
                            <span class="slds-checkbox--faux" />
                            <span class="slds-form-element__label"></span>
                        </label>
                    </th>
                    <th scope="col">
                        <div class="slds-truncate" title="Name">Name</div>
                    </th>
                    <th scope="col">
                        <div class="slds-truncate" title="Account">Account</div>
                    </th>
                    
                    <th scope="col">
                        <div class="slds-truncate" title="Phone">Phone</div>
                    </th>
                    
                    <th scope="col">
                        <div class="slds-truncate" title="Email">Email</div>
                    </th>
                </tr>
            </thead>
            <tbody>        
                <aura:iteration items="{!v.contactList}" var="con">
                    <tr>
                        <th>
                            <label class="slds-checkbox">
                                <ui:inputCheckbox aura:id="checkContact" value="{!con.isSelected}" text="{!con.Id}"/>
                                <span class="slds-checkbox--faux" />
                                <span class="slds-form-element__label"></span>
                            </label>
                        </th>
                        <th scope="row">
                            <div class="slds-truncate" title="{!con.Name}">{!con.Name}</div>
                        </th>
                        <td>
                            <div class="slds-truncate" title="{!con.Account}">{!con.Account}</div>
                        </td>
                        <th scope="row">
                            <div class="slds-truncate" title="{!con.Phone}">{!con.Phone}</div>
                        </th>
                        <td>
                            <div class="slds-truncate" title="{!con.Email}">{!con.Email}</div>
                        </td>
                    </tr>
                </aura:iteration>
            </tbody>
        </table>
        <div>
            <br/>
            <lightning:button label="Submit" class="slds-button_brand" onclick="{!c.handleSelectedContacts }"  />
        </div>
    </div>
</aura:component>

Lightning JS Controller:

({
    //get Contact List from apex controller
    doInit : function(component, event, helper) {
        var action = component.get("c.getContactList");
        action.setCallback(this, function(result){
            var state = result.getState();
            if (component.isValid() && state === "SUCCESS"){
                component.set("v.contactList",result.getReturnValue());   
            }
        });
        $A.enqueueAction(action);
    },
    
    //Select all contacts
    handleSelectAllContacts: function(component, event, helper) {
        var getID = component.get("v.contactList");
        var checkvalue = component.find("selectAll").get("v.value");        
        var checkContact = component.find("checkContact"); 
        if(checkvalue == true){
            for(var i=0; i<checkContact.length; i++){
                checkContact[i].set("v.value",true);
            }
        }
        else{ 
            for(var i=0; i<checkContact.length; i++){
                checkContact[i].set("v.value",false);
            }
        }
    },
    
    //Process the selected contacts
    handleSelectedContacts: function(component, event, helper) {
        var contactList = component.get("v.contactList");
        var isSelectAll = component.get("v.isSelectAll");
        
        var selectedContacts = [];
        
        if(isSelectAll){
            selectedContacts = contactList;
        }
        else{
            var k = 0;
            for (var i=0; i<contactList.length; i++){
                var c = contactList[i];
                if(c.isSelected) {
                    selectedContacts[k] = c;
                    k++; 
                }     
            }
        }
        
        if(selectedContacts.length > 0){
            var contactRecords = JSON.stringify(selectedContacts);
            var action = component.get("c.processSelectedContacts");
            action.setParams({
                contactRecords : contactRecords
            });
            action.setCallback(this, function(result){
                var state = result.getState();
                if (component.isValid() && state === "SUCCESS"){
                    alert('Success in calling server side action');
                }
                else if(state == "ERROR"){
                    alert('Error in calling server side action');
                }
            });
            $A.enqueueAction(action);
        }
    }
})

Apex Controller:

public class ContactController {
    
    @AuraEnabled
    Public static List<ContactWrapper> getContactList(){
        List<ContactWrapper> contactList = new List<ContactWrapper>(); 
        //get all contact list
        List<Contact> conList = [SELECT Id, Name, Account.Name, Phone, Email FROM Contact LIMIT 10];
        for(Contact con : conList){
            ContactWrapper obj = new ContactWrapper();
            obj.ContactId = con.Id;
            obj.Name = con.Name;
            obj.Account = con.Account.Name;
            obj.Phone = con.Phone;
            obj.Email = con.Email;
            obj.isSelected = false; 
            contactList.add(obj);
        }
        return contactList;
    }
    
    @AuraEnabled
    Public static void processSelectedContacts(string contactRecords){
        system.debug('contactRecords-' + contactRecords);
        List<ContactWrapper> contactList = new  List<ContactWrapper>();
        if(!string.isBlank(contactRecords)){
            contactList = (List<ContactWrapper>)System.JSON.deserialize(contactRecords,List<ContactWrapper>.class);
            system.debug('contactList-' + contactList);
        }
    }
    
    public class ContactWrapper{
        @AuraEnabled
        public String ContactId {get;set;}
        @AuraEnabled
        public String Name {get;set;}
        @AuraEnabled
        public String Account {get;set;}
        @AuraEnabled
        public String Phone {get;set;}
        @AuraEnabled
        public String Email {get;set;}
        @AuraEnabled
        public boolean isSelected {get;set;}
    }
}

Application Events in Salesforce Lightning Framework

Lightning framework is based on event-driven architecture which allows to communicate between different events. Lightning events are fired from JavaScript controller actions that are triggered by a user interacting with the user interface.

The communication between components are handled by events. There are two types of custom events in the Lightning Framework:

  • Component Events
  • Application Events

Application Events are handled by any component have handler defined for event.These events are essentially a traditional publish-subscribe model.

Application Event Example:
In below example by using Application event, I’m passing the values from a child component to a parent component via event.

Sample Application Event:
Create a sample application type event. Use type=”APPLICATION” in the aura:event tag for an application event. The attribute type is the one that will differentiate Application event from Component event.

<!--SampleApplicationEvent.evt-->
<aura:event type="Application" description="Sample Application Event">
    <aura:attribute name="message" type="String" />
</aura:event>

Child Component:

<!--Child.cmp-->
<aura:component>
    <lightning:button label="Click to fire the event" variant="brand" onclick="{!c.childComponentEvent}"/>
</aura:component>

Child Component JS Controller:
Use $A.get(“e.myNamespace:myAppEvent”) in JavaScript to get an instance of the myAppEvent event in the myNamespace namespace.
To set the attribute values of event, call event.setParam() or event.setParams().

({
    childComponentEvent : function(cmp, event,helper) { 
        //Get the event using event name. 
        var appEvent = $A.get("e.c:SampleApplicationEvent"); 
        //Set event attribute value
        appEvent.setParams({"message" : "Welcome "}); 
        appEvent.fire(); 
    }
})

Parent Component:
The application event handled by the Parent Component that fired using aura:handler in the markup.
The action attribute of aura:handler sets the client-side controller action to handle the event.

<!--Parent.cmp-->
<aura:component>
    <aura:attribute name="eventMessage" type="String"/> 
    <aura:handler event="c:SampleApplicationEvent" action="{!c.parentComponentEvent}"/>
    <div class="slds-m-around_xx-large">
        <c:Child /> 
        <p>{!v.eventMessage}</p> 
    </div>
</aura:component>

Parent Component JS Controller:

({
    parentComponentEvent : function(cmp, event) { 
        //Get the event message attribute
        var message = event.getParam("message"); 
        //Set the handler attributes based on event data 
        cmp.set("v.eventMessage", message + 'Biswajeet');         
    }
})

Output:

System Events in Lightning Framework

System events are fired automatically by the Lightning framework such as during component initialization, attribute value change, rendering etc. All Components can register for system events in their HTML markup.

We can handle these events in the Lightning apps or components, and within the Salesforce mobile app. Here are few examples of system events.

Event Name Description
aura:doneRendering Indicates that the initial rendering of the root application has completed.
aura:doneWaiting Indicates that the app is done waiting for a response to a server request.
aura:locationChange Indicates that the hash part of the URL has
changed.
aura:noAccess Indicates that a requested resource is not accessible due to
security constraints on that resource.
aura:systemError Indicates that an error has occurred.
aura:valueChange Indicates that an attribute value has
changed.
aura:valueDestroy Indicates that a component has been
destroyed.
aura:valueInit Indicates that an app or component has been
initialized.
aura:valueRender Indicates that an app or component has been rendered or
rerendered.
aura:waiting Indicates that the app is waiting for a response to a server request.

Display Toast Message in Lighting Component

Using force:showToast we can display a toast notification with a message. A toast displays a message below the header at the top of a view. The message is specified by the message attribute.

Attributes:

  • title Specifies the toast title in bold.
  • message Specifies the message to display. It is a required attribute.
  • messageTemplate Overwrites message string with the specified message. Requires messageTemplateData.
  • messageTemplateData An array of text and actions to be used in messageTemplate.
  • key String Specifies an icon when the toast type is other. Icon keys are available at the Lightning Design System Resources page.
  • duration Toast duration in milliseconds. The default is 5000ms.
  • type The toast type, which can be error, warning, success, or info. The default is other, which is styled like an info toast and doesn’t display an icon, unless specified by the key attribute.
  • mode The toast mode, which controls how users can dismiss the toast. The default is dismissible, which displays the close button.
    dismissible: Remains visible until you press the close button or duration has elapsed, whichever comes first.
    pester: Remains visible until duration has elapsed. No close button is displayed.
    sticky: Remains visible until you press the close buttons.

Example:

Component:

<aura:component implements="force:appHostable,flexipage:availableForAllPageTypes">
    <div>
        <lightning:button label="Information"
                          variant="brand"
                          onclick="{!c.showInfo}"/>
        <lightning:button label="Error"
                          variant="destructive"
                          onclick="{!c.showError}"/>
        <lightning:button label="Warning"
                          variant="neutral"
                          onclick="{!c.showWarning}"/>
        <lightning:button label="Success"
                          variant="success"
                          onclick="{!c.showSuccess}"/>
    </div> 
</aura:component>

Component JS Controller:

({
    showInfo : function(component, event, helper) {
        var toastEvent = $A.get("e.force:showToast");
        toastEvent.setParams({
            title : 'Info',
            message: 'This is an information message.',
            duration:' 5000',
            key: 'info_alt',
            type: 'info',
            mode: 'dismissible'
        });
        toastEvent.fire();
    },
    showSuccess : function(component, event, helper) {
        var toastEvent = $A.get("e.force:showToast");
        toastEvent.setParams({
            title : 'Success',
            message: 'This is a success message',
            duration:' 5000',
            key: 'info_alt',
            type: 'success',
            mode: 'pester'
        });
        toastEvent.fire();
    },
    showError : function(component, event, helper) {
        var toastEvent = $A.get("e.force:showToast");
        toastEvent.setParams({
            title : 'Error',
            message:'This is an error message',
            duration:' 5000',
            key: 'info_alt',
            type: 'error',
            mode: 'pester'
        });
        toastEvent.fire();
    },
    showWarning : function(component, event, helper) {
        var toastEvent = $A.get("e.force:showToast");
        toastEvent.setParams({
            title : 'Warning',
            message: 'This is a warning message.',
            duration:' 5000',
            key: 'info_alt',
            type: 'warning',
            mode: 'sticky'
        });
        toastEvent.fire();
    }
})

Output:

Salesforce Lightning Data Service

Salesforce introduced Lightning Data Service(LDS) in Winter 17, It is to serve as the data layer for Lightning. It is similar like Standard Controllers in Visualforce Page.

Advantages of Lightning Data Service:

  • Lightning Data Service allows to load, create, edit, or delete a record without requiring Apex code and SOQL query.
  • Lightning Data Service handles sharing rules and field-level security.
  • Records loaded in Lightning Data Service are cashed and shared across all components.
  • When one component updates a record, the other component using it are notified.
  • Lightning Data Service supports offline in Salesforce1.

Consideration of Lightning Data Service:

  • Lightning Data Service is only available in Lightning Experience and Salesforce1.
  • Lightning Data Service does not support bulk operations.
  • Lightning Data Service in other containers, such as Lightning Components for Visualforce, Lightning Out, or Communities isn’t supported.
  • Lightning Data Service notifies listeners about data changes only if the changed fields are the same as in the listener’s fields or layout.

We can use force:recordData Lightning component to declare Lightning Data Service. To use Lightning Data Service, we need specify the following attributes:

  • recordId specifies the record to load. Records can’t be loaded without a recordId.
  • mode can be set to either EDIT or VIEW, which determines the behavior of notifications and what operations are available to perform with the record. If you’re using force:recordData to change the record in any way, set the mode to EDIT.
  • layoutType specifies the layout (FULL or COMPACT) that is used to display the record.
  • fields specifies which fields in the record to query. The fields or layoutType attribute (or both) must be provided.
  • target* attributes starting with target indicates values to be returned from Data Service. It can return record or error.
  • recordUpdated edit mode does not updates record by default, to update target* attributes, use this method handler.
  • targetRecord will contain only the fields relevant to the requested layoutType and/or fields atributes.
  • targetFields is populated with the simplified view of the loaded record.
  • targetError is to the localized error message if the record can’t be provided.

Here is an example of Lightning Data Service: In below example I’ve created a Lightning Quick Action to edit Contact Email and Phone using Lightning Data Service force:recordData.

ContactEditDataService.cmp :

<!--ContactEditDataService.cmp-->
<aura:component implements="force:appHostable,force:lightningQuickActionWithoutHeader,flexipage:availableForRecordHome,force:hasRecordId" access="global" >
    <!--Declare Atrribute-->
    <aura:attribute name="contact" type="Object"/>
    <aura:attribute name="contactRecord" type="Object"/>
    <aura:attribute name="recordSaveError" type="String"/>
    
    <force:recordData aura:id="conRec" 
                      layoutType="FULL" 
                      recordId="{!v.recordId}"  
                      targetError="{!v.recordSaveError}"
                      targetRecord="{!v.contact}"
                      targetFields="{!v.contactRecord}"
                      mode="EDIT"
                      recordUpdated="{!c.handaleRecordUpdated}"/>
    
    <div class="slds-box"> 
        <div class="slds-text-heading_medium">
            {!v.contactRecord.Name}
        </div> 
        
        <aura:if isTrue="{!not(empty(v.recordSaveError))}">
            <br />
            <div class="error slds-box">
                {!v.recordSaveError}
            </div> 
        </aura:if>
        
        <div class="slds-form--stacked slds-tile"> 
            <div class="slds-form-element">
                <label class="slds-form-element__label">Phone: </label>
                <div class="slds-form-element__control">
                    <lightning:input type="tel" value="{!v.contactRecord.Phone}" name="phone" required="true"/>
                </div>
            </div>
            <div class="slds-form-element">
                <label class="slds-form-element__label">Email: </label>
                <div class="slds-form-element__control">
                    <lightning:input type="email" aura:id="email" value="{!v.contactRecord.Email}" required="true"/>
                </div>
            </div>
            <div class="slds-form-element">
                <lightning:button variant="brand" title="Save" label="Save" onclick="{!c.handleSaveContact}"/>
                <lightning:button title="Cancel" label="Cancel" onclick="{!c.handaleCancel}"/>
            </div>
        </div>
    </div>
</aura:component>

ContactEditDataService JS Controller :

({
    handleSaveContact : function(component, event, helper) {
        helper.saveContact(component, event, helper);
    },
    
    handaleRecordUpdated : function(component, event, helper) {
        helper.recordUpdated(component, event, helper);
    },
    
    handaleCancel : function(component, event, helper) {
        $A.get("e.force:closeQuickAction").fire();
    }
})

ContactEditDataService Helper :

({
    //Save contact record
    saveContact : function(component, event, helper) {
        component.find("conRec").saveRecord($A.getCallback(function(saveResult) {
            if (saveResult.state === "SUCCESS" || saveResult.state === "DRAFT") {
                //To close the component with success message
                var toastEvent = $A.get("e.force:showToast");
                toastEvent.setParams({
                    "title": "Success!",
                    "message": "The record has been updated successfully."
                });
                toastEvent.fire();
                $A.get("e.force:closeQuickAction").fire();
                $A.get('e.force:refreshView').fire();
            } else if (saveResult.state === "INCOMPLETE") {
                //Show data saved incomplete message
                component.set("v.recordSaveError","Data saved incomplete.");
            } else if (saveResult.state === "ERROR") {
                //Show error message
                var errMsg = "";
                for (var i = 0; i < saveResult.error.length; i++) {
                    errMsg += saveResult.error[i].message + "\n";
                }
                component.set("v.recordSaveError", errMsg);
            } 
        }));
    },
    
    //Refresh record after update
    recordUpdated : function(component, event, helper){
        var changeType = event.getParams().changeType;
        if (changeType === "CHANGED") {
            component.find("conRec").reloadRecord();
        }
    }
})

Output: