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 usingforce: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 updatetarget
* 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(); } } })
Lightning Icons in Visualforce Page
Salesforce provides 5 different kind of Icons (Standart, Custom, Utility, Doctype, Action) which can be used in Lightning component and Visualforce page. Find SLDS Icons here.
Example:
Instead of uploading the Lightning Design System as a static resource, we can include apex:slds
inside the head tag of Visualforce page to use Lightning Design System stylesheets in the page.
Visualforce Page:
<apex:page showHeader="false" standardStylesheets="false" sidebar="false" docType="html-5.0" > <head> <apex:slds/> </head> <body class="slds-scope"> <div class="slds-m-around--xx-large"> <article class="slds-card"> <div class="slds-card__body"> <table class="slds-table slds-table_bordered"> <thead> <tr class="slds-text-heading--label"> <th scope="col">NAME</th> <th scope="col">CATEGORIES</th> <th scope="col">ICON</th> </tr> </thead> <tbody> <tr> <td scope="row">Account</td> <td scope="row">Standard</td> <td scope="row"> <span class="slds-icon_container slds-icon-standard-account" > <svg aria-hidden="true" class="slds-icon slds-icon--small"> <use xmlns:xlink="http://www.w3.org/2000/xlink" xlink:href="/apexpages/slds/latest/assets/icons/standard-sprite/svg/symbols.svg#account"> </use> </svg> <span class="slds-assistive-text">Account</span> </span> </td> </tr> <tr> <td scope="row">Custom1</td> <td scope="row">Custom</td> <td scope="row"> <span class="slds-icon_container slds-icon-custom-custom1" > <svg aria-hidden="true" class="slds-icon slds-icon--small"> <use xmlns:xlink="http://www.w3.org/2000/xlink" xlink:href="/apexpages/slds/latest/assets/icons/custom-sprite/svg/symbols.svg#custom1"> </use> </svg> <span class="slds-assistive-text">Custom1</span> </span> </td> </tr> <tr> <td scope="row">Email</td> <td scope="row">Utility</td> <td scope="row"> <span class="slds-icon_container slds-icon-utility-email" > <svg aria-hidden="true" class="slds-icon slds-icon-text-default"> <use xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="/apexpages/slds/latest/assets/icons/utility-sprite/svg/symbols.svg#email"> </use> </svg> <span class="slds-assistive-text">Email</span> </span> </td> </tr> <tr> <td scope="row">Priority</td> <td scope="row">Action</td> <td scope="row"> <span class="slds-icon_container slds-icon-action-priority" > <svg aria-hidden="true" class="slds-icon slds-icon--x-small"> <use xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="/apexpages/slds/latest/assets/icons/action-sprite/svg/symbols.svg#priority"> </use> </svg> <span class="slds-assistive-text">Priority</span> </span> </td> </tr> <tr> <td scope="row">CSV</td> <td scope="row">Doctype</td> <td scope="row"> <span class="slds-icon_container slds-icon-doctype-csv" > <svg aria-hidden="true" class="slds-icon "> <use xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="/apexpages/slds/latest/assets/icons/doctype-sprite/svg/symbols.svg#csv"> </use> </svg> <span class="slds-assistive-text">CSV</span> </span> </td> </tr> </tbody> </table> </div> </article> </div> </body> </apex:page>
Invoke Batch Apex From Another Batch Apex
Only in batch class finish
method, We can call another batch class. If you will call another batch class from batch class execute
and start
method, then Salesforce will throw below runtime error.
System.AsyncException: Database.executeBatch cannot be called from a batch start, batch execute, or future method.
Here in below example there are two batch classes “Batch1” and “Batch2“.
I want to execute “Batch2” after finish the “Batch1“.
So, I’m calling “Batch2” class in “Batch1” finish method.
Batch1:
global class Batch1 implements Database.Batchable<Sobject>{ //Method to get the data to be proceesed global database.Querylocator Start(Database.BatchableContext bc){ String query = 'Select Id, Name From Account Limit 1000'; return Database.getQueryLocator(query); } //Method to execute the batch global void execute(Database.BatchableContext bc, Sobject[] scope){ for(Sobject s : scope){ Account a = (Account)s; // TO DO // add your logic } } //Method to be called after the excute global void finish(Database.BatchableContext bc){ //Add your start code for the other batch job here Database.executeBatch(new Batch2()); } }
Batch2:
global class Batch2 implements Database.Batchable<Sobject>{ //Method to get the data to be proceesed global database.Querylocator start(Database.BatchableContext bc){ string query = 'Select Id, Name From Contact Limit 1000'; return Database.getQueryLocator(query); } //Method to execute the batch global void execute(Database.BatchableContext bc, Sobject[] scope){ for(Sobject s : scope){ Contact c = (Contact)s; // TO DO // add your logic } } //Method to be called after the excute global void finish(Database.BatchableContext bc){ } }