Header Ads Widget

Best Practices for Salesforce Lightning Component. How Lightning Data Service can improve


Best Practices for Salesforce Lightning Component. How Lightning Data Service can improve Lightning Component performance and solve inconsistent data problem without writing single line of Apex code. Demo source code, image and slides included.

If you have worked on Visualforce and started playing with Lightning Component, you must have identified one important missing feature, which is standard Controller.

Lets assume a scenario, you have two Lightning Components on Account page. One Lightning Component is used to show some fields and other component is used to edit same record.

Problem 1 :

To achieve this, both Lightning components would invoke Apex method separately at the cost of performance, by issuing duplicate server calls. Below image depicts problem, where multiple Lightning component requests same content by making separate server calls.


Problem 2 :

If data gets changed because of any component, Other component needs to be informed with the help of Lightning Events. Again, it would cost performance, as every Lightning component may listen that event.

Problem 3 :

Data Inconsistency. It is quite possible that field updated but because of some defect / issue data not reflected everywhere.

Problem 4 :

Developer needs to write Apex code and handler many scenarios like SOQL, Field level security, writing test classes and many more.

To fix all of above problems, Salesforce introduced Lightning Data Service in Winter 17 as a pilot program and in Summer 17 (currently) its in Beta and expected to be GA in Winter 18.

Advantages of Lightning Data Service

No need to write any Apex class

No need to write SOQL

Field level security and record sharing is inbuilt

CRUD operation supported

Shared cache is used by all standard and custom components

Auto notification to all components

Supports offline in Salesforce 1

In summer 17, we can use force:recordData Lightning component to declare Lightning Data Service. Following are important attributes

RecordId – represents record to be loaded, deleted or updated. If its null then it indicates record to be inserted

LayoutType – Layout from which field needs to be loaded. Currently it supports Full or Compact

Fields– If Layout does not contains field, we can use this attribute. Either one of LayoutType or Fields is mandatory.

Target* – attributes starting with target indicates values to be returned from Data Service. It can return record or error.

Mode – Indicates Data Service should be used in View or Edit mode

RecordUpdated – edit mode does not updates record by default, to update target* attributes, use this method handler

Read record using Lightning Data Service

Lets jump to source code, that could be used to read record with the help Lightning Data Service without writing single line of Apex code.

<aura:component implements="flexipage:availableForAllPageTypes,

flexipage:availableForRecordHome,force:hasRecordId"

access="global" >



        <aura:attribute name="account" type="Object"

          description="The record object to be displayed"/>

        <aura:attribute name="accountRecord" type="Object"

          description="A simplified view record object to be displayed"/>

        <aura:attribute name="recordError" type="String"

          description="An error message bound to force:recordData"/>



        <force:recordData aura:id="record"

               layoutType="FULL"

               recordId="{!v.recordId}"

               targetError="{!v.recordError}"

               targetRecord="{!v.account}"

               targetFields="{!v.accountRecord}"

    fields="Name, Company_Email__c"

               mode="VIEW"/>

       

    <div class="slds-box">

        <div class="slds-text-heading_medium">

            Load Account - Data Service

        </div>

    

         <!-- Display a header with details about the record -->

        <div class="slds-form--stacked slds-tile">

            <div class="slds-form-element">

                <label class="slds-form-element__label"  >Name: </label>

                <div class="slds-form-element__control">

                  <ui:outputText class="slds-input"

                    value="{!v.accountRecord.Name}" />

                </div>

            </div>

            <div class="slds-form-element">

                <label class="slds-form-element__label" >Company Email : </label>

                <div class="slds-form-element__control">

                  <ui:outputTextArea class="slds-input" 

                    value="{!v.accountRecord.Company_Email__c}"  />

                </div>

            </div>

        </div>

    </div>

</aura:component>

As we can see, force:recordData component is not rendered on user interface.



Update record using Lightning Data Service

Next source code is for Lightning component, which updates record with the help of Data Service.

<aura:component implements="force:appHostable,
flexipage:availableForRecordHome,force:hasRecordId" 
access="global" >
 
        <aura:attribute name="account" type="Object" 
          description="The record object to be displayed"/>
        <aura:attribute name="accountRecord" type="Object" 
          description="A simplified view record object to be displayed"/>
        <aura:attribute name="recordSaveError" type="String" 
          description="An error message bound to force:recordData"/>
    
    <force:recordData aura:id="accRec" 
        layoutType="FULL" 
               recordId="{!v.recordId}"  
               targetError="{!v.recordSaveError}"
               targetRecord="{!v.account}"
               targetFields="{!v.accountRecord}"
               mode="EDIT"
                recordUpdated="{!c.recordUpdated}"/>
    
     <div class="slds-box"> 
        <div class="slds-text-heading_medium">
            Edit Account - Data Service
        </div> 
        <div class="slds-form--stacked slds-tile"> 
            <div class="slds-form-element">
                <label class="slds-form-element__label"  >Name: </label>
                <div class="slds-form-element__control">
                       <ui:inputText aura:id="recordName"  class="slds-input"  
                                value="{!v.accountRecord.Name}"
                                required="true"/>
                </div>
            </div>
             <div class="slds-form-element">
                 <label class="slds-form-element__label"  >Company Email: </label>
                 <div class="slds-form-element__control">
                       <ui:inputText aura:id="companyEmail"  class="slds-input"  
                                value="{!v.accountRecord.Company_Email__c}"
                                required="true"/>
                 </div>
            </div>
            <div class="slds-form-element">
               <ui:button aura:id="saveAccount" 
                buttonTitle="Save Account" class="button" label="Save Account" press="{!c.saveAccount}"/>
            </div>
        </div>
        
           <aura:if isTrue="{!not(empty(v.recordSaveError))}">
               <br />
            <div class="error slds-box">
                 {!v.recordSaveError}
            </div> 
        </aura:if>
    </div>
</aura:component>





.THIS .error {
    background-color: #c23934;
        border: 1px solid #c23934 ;
        color: white;
}



({
       saveAccount : function(component, event, helper) {
               helper.saveAccount(component, event, helper);
       },
    recordUpdated : function(component, event, helper) {
               helper.recordUpdated(component, event, helper);
       }
})





({
        saveAccount : function(component, event, helper) {
               component.find("accRec").saveRecord($A.getCallback(function(saveResult) {
            if (saveResult.state === "SUCCESS" || saveResult.state === "DRAFT") {
                console.log("Save completed successfully.");
            } else if (saveResult.state === "INCOMPLETE") {
                component.set("v.recordSaveError","User is offline, device doesn't support drafts.");
            } else if (saveResult.state === "ERROR") { 
                var errMsg = "";
                // saveResult.error is an array of errors, 
                // so collect all errors into one message
                for (var i = 0; i < saveResult.error.length; i++) {
                    errMsg += saveResult.error[i].message + "\n";
                }
                component.set("v.recordSaveError", errMsg);
                
            } else {
                component.set("v.recordSaveError",'Unknown problem, state: ' + saveResult.state + ', error: ' + 
                             JSON.stringify(saveResult.error));
            }
        }));
        },
    recordUpdated : function(component, event, helper){
        var changeType = event.getParams().changeType;
               if (changeType === "CHANGED") {
            component.find("accRec").reloadRecord();
        }
    }
})



There are two important observations in above source code.



Saving record in Data Service

We have to call saveRecord method on Data Service Lightning Component. Check code on JavaScript controller.

Refresh record in Data Service

Use method defined by recordupdate attribute on Data Service. We are using reloadRecord method and code can be seen in JavaScript Controller.

Using Lightning app builder page, we can add these two lightning components on record page. Below image shows complete demo of sample application.

imitations of Lightning Data Service




It does not support bulk record operations. (think about StandardSetController functionality in Visualforce)

If record gets updated from outside page, like using Dataloader or some other user, it will not be refreshed. Means, it supports client side caching but not server side caching yet

Post a Comment

0 Comments