Showing posts with label LDS. Show all posts
Showing posts with label LDS. Show all posts

Sunday, February 4, 2018

Important facts which needs to be considered while designing any solution which includes Large data volumes in Salesforce


  • Sharing calculation is even performed for records which are in recycle bin which may impact your performance. So if you don't need records then perform hard delete on records after archiving it.

  • Only Bulk API supports hard delete functionality.

  • Adding custom indexes on records may improve query performance but can degrade the performance of database insert and update. That is reason that Salesforce doesn't allow developers to add custom indexes by themselves. You have to raise support ticket and Salesforce will evaluate your use case before adding custom indexes. So always evaluate the pros and cons before adding any new custom indexes.

  • As a best practice, don't use formula fields in where clause in SOQL. It degrade the SOQL performance if it doesn't return deterministic values (formula returns pre-defined set of values which are repeatable).

  • Now Salesforce allow you to add custom index to formula fields only if it gives deterministic results

  • Use readonly attribute on VF page if you want to query more than 50 millions records and display the result on UI by processing those records.



Saturday, September 30, 2017

Lightning Data Services : Hands on Training Guide

In this blog, we are going to create a sample app which will list 10 recents accounts from Salesforce and will view and edit account using Lightning Data Services.

So lets start.

Create a “LDSAccountListViewApp” Lightning Application

  • In the Developer Console, choose File > New > Lightning Application.
  • Specify “LDSAccountListViewApp” as the app name and click submit.
  • Specify below code in lightning app.

<aura:application extends="force:slds"> 
    <div class="slds-text-heading_large slds-text-align_center"> 
Lightning Data Service Demo App 
    </div> 
</aura:application>

  • Save the file.
  • Click  preview

Create a LDSAccountEdit lightning Component
  • In the Developer Console, choose File > New > Lightning Component.
  • Specify “LDSAccountEdit” as the component name and click submit. Paste below code and Save the file.
<aura:component > 
<aura:attribute name="recordId" type="String" required="true"/> 
<aura:attribute name="recordInfo" type="Object"/> 
<aura:attribute name="fieldsInfo" type="Object"/> 
<aura:attribute name="recordError" type="String"/> 
<aura:attribute name="currView" type="String" />
<force:recordData aura:id="recordLoader" 
recordId="{!v.recordId}" 
mode="EDIT" 
targetRecord="{!v.recordInfo}" 
targetFields="{!v.fieldsInfo}" fields="Name,Owner.Name,AnnualRevenue,AccountNumber" 
targetError="{!v.recordError}" 
recordUpdated="{!c.handleRecordChanges}" /> 
<div class="maincontainer"> 
</div> 
<!-- Display Lightning Data Service errors, if any --> 
<aura:if isTrue="{!not(empty(v.recordError))}"> 
<div class="recordError"> 
    <ui:message title="Error" severity="error" closable="true"> {!v.recordError}     </ui:message> 
</div> 
</aura:if> 
</aura:component>

  • Click CONTROLLER in the right side bar of the code editor and replace code with below code and save the file.
({ 
    handleRecordChanges: function(component, event, helper) {
  var eventParams = event.getParams(); 
if(eventParams.changeType === "LOADED") { 
// record is loaded 
var fieldsDetails= component.get("v.fieldsInfo"); 
console.log("fieldsInfo is loaded successfully. TargetField"+ JSON.stringify(fieldsDetails)); 
var recordDetails= component.get("v.recordInfo"); 
console.log("recordInfo -Target Record"+ JSON.stringify(recordDetails)); 
console.log('Record loaded successfully'); 
     } 
})
  • Update the “LDSAccountListViewApp” code with below code and save file.
<aura:application extends="force:slds"> 
    <div class="slds-text-heading_large slds-text-align_center"> 
Lightning Data Service Demo App 
    </div> 
    <c:LDSAccountEdit recordId=“0017F000004R9C3QAK”/>
</aura:application>

Lets see what we have done!!!

  • In order to display more account fields, replace the code inside div with class “maincontainer” with below code.


<div class="maincontainer"> 
      <div class="slds-col--padded slds-size--1-of-1 slds-medium--1-of-1 slds-large-size--1-of-1"> 
           <div class="slds-form-element__control" > 
                  <lightning:input label="Account Name" name="accname" value="{!v.fieldsInfo.Name}" /> 
           </div> 
       </div>
       <div class="slds-col--padded slds-size--1-of-1 slds-medium--1-of-1 slds-large-size--1-of-1"> 
            <div class="slds-form-element__control" > 
               <lightning:input type="number" label="Annual Revenue" name="atype" value="{!v.fieldsInfo.AnnualRevenue}"  formatter="currency"/>
             </div> 
        </div> 
        <div class="slds-col--padded slds-size--1-of-1 slds-medium--1-of-1 slds-large-size--1-of-1"> 
            <div class="slds-form-element__control" > 
                  <lightning:input label="Account Number" name="accnum" value="{!v.fieldsInfo.AccountNumber}" /> 
            </div>
         </div>
          <div class="slds-col--padded slds-size--1-of-1 slds-medium--1-of-1 slds-large-size--1-of-1"> 
                <div class="slds-form-element__control" > 
                       <lightning:button variant="brand" label="Save" onclick="{!c.saveRecordHandler}"/> 
                        <lightning:button variant="brand" label="Back" /> 
               </div> 
               </div>
 </div>


  • Click CONTROLLER in the right side bar of the code editor and add saveRecordHandler function and save the file.
saveRecordHandler: function(component, event, helper) {       component.find("recordLoader").saveRecord($A.getCallback(function(saveResult) { 
   if (saveResult.state === "SUCCESS" || saveResult.state === "DRAFT") {         console.log('Record updated successfully'); 
   }else if (saveResult.state === "ERROR") { 
      console.log('Problem error: ' + JSON.stringify(saveResult.error)); 
   } else { 
        console.log('Unknown problem, state: ' + saveResult.state + ', error: ' +        JSON.stringify(saveResult.error)); 
   } 
})); 
}
  • Update the “LDSAccountListViewApp” code with below code and save file.
<aura:application extends="force:slds"> 
    <div class="slds-text-heading_large slds-text-align_center"> 
Lightning Data Service Demo App 
    </div> 
    <c:LDSAccountEdit recordId=“0017F000004R9C3QAK”/>
</aura:application>

Lets see what we have done!!!


Create a LDSAccountView lightning Component
  • In the Developer Console, choose File > New > Lightning Component.
  • Specify “LDSAccountView” as the component name and click submit.
  • Copy the code from “LDSAccountEdit” component and paste it here.
  • Add a attribute disabled="true" in all lightning:input tag
<lightning:input label="Account Name" name="accname" value="{!v.fieldsInfo.Name}" dsabled="true"/>
  • Remove "handleRecordChanges" attribute from <force:recorddata> tag.
  • Remove Lightning:button with label as "Save".
Complete code for LDSAccountView Component is :

<aura:component >
    <aura:attribute name="recordId" type="String" required="true"/>
    <aura:attribute name="recordInfo" type="Object"/>
    <aura:attribute name="fieldsInfo" type="Object"/>
    <aura:attribute name="currView" type="String" />

    <aura:attribute name="recordError" type="String"/>
    <force:recordData aura:id="recordLoader"  
                      recordId="{!v.recordId}"  
                      mode="VIEW"
                      targetRecord="{!v.recordInfo}"
                      targetFields="{!v.fieldsInfo}" 
                      fields="Name,Owner.Name,AnnualRevenue,AccountNumber"
                      targetError="{!v.recordError}"
                      />
    <div class="maincontainer"> 
        <div class="slds-col--padded slds-size--1-of-1 slds-medium--1-of-1 slds-large-size--1-of-1"> 
            <div class="slds-form-element__control" > 
                <lightning:input label="Account Name" name="accname" value="{!v.fieldsInfo.Name}" disabled="true"/> 
            </div> 
        </div>
        <div class="slds-col--padded slds-size--1-of-1 slds-medium--1-of-1 slds-large-size--1-of-1"> 
            <div class="slds-form-element__control" > 
                <lightning:input type="number" label="Annual Revenue" name="atype" value="{!v.fieldsInfo.AnnualRevenue}"  formatter="currency" disabled="true"/>
            </div> 
        </div> 
        <div class="slds-col--padded slds-size--1-of-1 slds-medium--1-of-1 slds-large-size--1-of-1"> 
            <div class="slds-form-element__control" > 
                <lightning:input label="Account Number" name="accnum" value="{!v.fieldsInfo.AccountNumber}" disabled="true"/> 
            </div>
        </div>
        <div class="slds-col--padded slds-size--1-of-1 slds-medium--1-of-1 slds-large-size--1-of-1"> 
            <div class="slds-form-element__control" > 
                <!--<lightning:button variant="brand" label="Save" onclick="{!c.saveRecordHandler}"/> -->
                <lightning:button variant="brand" label="Back" onclick="{!c.goBackToListView}"/> 
            </div> 
        </div>
    </div>
    
    <!-- Display Lightning Data Service errors, if any -->
    <aura:if isTrue="{!not(empty(v.recordError))}">
        <div class="recordError">
            <ui:message title="Error" severity="error" closable="true">
                {!v.recordError}
            </ui:message>
        </div>
    </aura:if>    
</aura:component>

  • Update the “LDSAccountListViewApp” code with below code and save file.
<aura:application extends="force:slds"> 
    <div class="slds-text-heading_large slds-text-align_center"> 
Lightning Data Service Demo App 
    </div> 
    <c:LDSAccountView recordId=“0017F000004R9C3QAK”/>
</aura:application>

Lets see what we have done!!


Create a Apex class to fetch Account records
  • Create Apex Class “LDSAccountListViewController” to fetch latest 10 Account records from your org.
public with Sharing class LDSAccountListViewController { 
@AuraEnabled public static List<Account> findAccounts(){ 
List<Account> accList = new List<Account>(); 
accList=[select id, name,owner.name,type,AccountNumber,AnnualRevenue,Phone from Account order by lastModifiedDate DESC Limit 10]; 
return accList; 
}

Create a LDSAccountListView lightning Component
  • In the Developer Console, choose File > New > Lightning Component.
  • Specify “LDSAccountListView” as the component name and click submit.
<aura:component controller="LDSAccountListViewController" > 
<aura:attribute name="accList" type="List" /> 
<aura:attribute name="menu" type="List" default="View,Edit"/>
<aura:attribute name="currentView" type="String" default="ListView"/>
<aura:attribute name="selectedRecord" type="String" /> 
<aura:handler name="init" value="{!this}" action="{!c.doInit}"/> 
<!--Section for Account List View starts--> 
<aura:if isTrue="{!v.currentView =='ListView'}"> 
<div class="slds-card"> 
  <div class="slds-card__header slds-grid"> 
   <header class="slds-media slds-media_center slds-has-flexi-truncate"> 
         <div class="slds-media__figure"> 
      <lightning:Icon iconName="standard:account" size="large" variant="inverse" /> 
     </div> 
           <div class="slds-media__body"> 
     <h2> <span class="slds-text-heading_small">Accounts({!v.accList.length})</span> </h2>      
     </div> 
   </header> 
   <div class="slds-no-flex"> <lightning:button variant="brand" label="New Account" />   </div> 
    </div> 
    <div class="slds-card__body slds-card__body_inner"> 
        <aura:if isTrue="{!v.accList.length > 0}"> <!--display all accounts--> </aura:if> 
                                </div> 
          <footer class="slds-card__footer">@sunil02kumar</footer> </div> 
</aura:if>
<!--Section for Account List View ends--> 
</aura:component>

  • Click CONTROLLER in the right side bar of the code editor and add doInit function and save the file
({ 
    doInit : function(component, event, helper){ 
var action = component.get("c.findAccounts"); 
action.setCallback(this, function(response) { 
var state = response.getState(); 
if (state === "SUCCESS") { 
var apexResponse = response.getReturnValue();
//console.log('***apexResponse:'+ JSON.stringify(apexResponse));
component.set("v.accList", apexResponse);
console.log('********Accounts list view loaded successfully'); 
}else if(state === "ERROR"){ 
alert('Problem with connection. Please try again.'); 
}}); 
$A.enqueueAction(action); 
    } 
})
  • Update the “LDSAccountListViewApp” code with below code and save file.
<aura:application extends="force:slds"> 
    <div class="slds-text-heading_large slds-text-align_center"> 
Lightning Data Service Demo App 
    </div> 
    <c:LDSAccountListView />
</aura:application>

  • Update LDSAccountListView lightning Component. Replace <!--display all accounts-->  section with below code and save file.
<table class="slds-table slds-table_fixed-layout slds-table_bordered slds-table_cell-buffer">
<thead> 
      <tr class="slds-text-title--caps"> 
<th scope="col">Actions</th> 
<th scope="col">Name</th> 
<th scope="col">Account Number</th> 
</tr> 
</thead> 
<tbody> 
     <aura:iteration items="{!v.accList}" var="item"> 
     <tr class="slds-hint-parent"> 
  <td scope="row"> 
<lightning:buttonMenu iconName="utility:threedots" > 
  <aura:iteration items="{!v.menu}" var="menuItem"> 
                             <lightning:menuItem label="{!menuItem}" value="{!item.Id + '---' + menuItem}" onactive="{!c.onSelectMenuItem}"/> 
  </aura:iteration> 
</lightning:buttonMenu> 
</td> 
<td > {!item.Name}</td> 
<td > {!item.AccountNumber}</td> 
</tr> 
     </aura:iteration> 
</tbody> 
</table>
  • Click CONTROLLER in the right side bar of the code editor and add onSelectMenuItem function and save the file.
onSelectMenuItem : function(component, event, helper) { 
var selectedOption = event.getSource().get('v.value'); 
var selectedId = selectedOption.split('---')[0];
console.log('*************selectedId:'+selectedId);
component.set("v.selectedRecord",selectedId);
console.log('*************selectedOption:'+selectedOption); 
if (selectedOption.endsWith("View")) {
component.set("v.currentView","RecordView"); 
}else if(selectedOption.endsWith("Edit")){
component.set("v.currentView","RecordEdit"); 
}
  • Update LDSAccountListView lightning Component. Now we are going to display LDSAccountView and LDSAccountEdit components based selection made by user on menu item. Add below markup code in LDSAccountListView component after the section for account list views and save file.
<!--Section for Account record View starts--> 
<aura:if isTrue="{!v.currentView =='RecordView'}"> 
<c:LDSAccountView recordId="{!v.selectedRecord}" currView="{!v.currentView}"/> </aura:if> 
<!--Section for Account record View ends--> 

<!--Section for Account record edit starts--> 
<aura:if isTrue="{!v.currentView =='RecordEdit'}"> 
<c:LDSAccountEdit recordId="{!v.selectedRecord}" currView="{!v.currentView}" /> </aura:if> 
<!--Section for Account record edit ends-->

Update LDSAccountView & LDSAccountView lightning Component
  • In LDSAccountEdit component, add action to lightning:button with label as "Back".
<lightning:button variant="brand" label="Back" onclick="{!c.goBackToListView}"/>
  • Click CONTROLLER in the right side bar of the code editor and add goBackToListViewfunction and save the file.
goBackToListView : function(component,event,helper){
component.set("v.currView","ListView"); 
}
  • Same way in LDSAccountView, update lightning:button with label as "Back" and add "goBackToListView" JavaScript function to controller.
Let see what we have done so far!!!



So now we are loading account view and edit page using Lightning Data Services. I think this will help you to understand basic concept of Lightning data services.

Below are list of items which can be implemented to for better User experience and for learning.
  • You can add deletion of records using LDS.
  • When you click on Save in account edit page, it should refresh the list view with latest changes. In this hands on training you have to refresh the browser to see latest values of account record.
  • You can use "New Account" to create new account record using LDS.

Hope this will help in basic understanding of Lightning Data Services!!!.


Wednesday, April 12, 2017

Lightning Data Services : Way to perform operation on records without using server-side Apex class

Today I am going to walk through a new feature introduced by Salesforce - Lightning Data Services.
This is not yet generally available. It is available as developer preview.

What is lightning data services?


Lightning data services allows you to view, create, update and delete a record without using server-side Apex controllers. You can compare this with Standard Controller on VF page which allows you to read, create, update or delete records by providing in built functions like Save, Edit,Delete etc.

Other benefit of using Lightning data services is that all records are cached and shared across all components. This improves the performance because record is loaded only once.
If any component modifies the record, then other component using this records get notified and refresh automatically.

In order to use Lightning data services, you need to use force:recordPreview tag and need to specify recordId while performing any operation.

Now we will cover how to use Lightning data services for different operations.

  • Load Record (View Record)
In order to Load record information on lightning component, you need to pass recordId. It is similar to pass id parameter in URL in VF page while using standard controller.

<force:recordData aura:id="recordLoader"
  recordId="xxxxxxxxxxxxx"
  layoutType="FULL"
  targetRecord="{!v.record}"
    targetFields="{!v.recordInfo}"
  targetError="{!v.recordError}"
  />

Before Summer'17 below was syntax which is now depricated.

<force:recordPreview aura:id="recordLoader"
 recordId="xxxxxxxxxxxxx"
 layoutType="FULL"
 targetRecord="{!v.record}"
 targetError="{!v.recordError}"
 />



recordId: It is 15 or 18 digit recordId.
targetRecord:  This contains complete info about record.
targetFields : A simplified view of the fields in targetRecord, to reference record fields in component markup.
targetError : This specify any error if lightning data services is not successfull in geting record details based on recordId provided.


  • Edit Record
For editing records, you need use same  force:recordPreview tag and you can use additional attribute mode to specify you are editing record.

<force:recordData aura:id="recordHandler"
 recordId="xxxxxxxxxxxx"
 layoutType="FULL"
 targetRecord="{!v.record}"
     targetFields="{!v.recordInfo}"
 targetError="{!v.recordError}"
 mode="EDIT"
 />

Once you get access to record returned by Lightning Data Services in target record, then you can refer it in component to edit its field values. In order to Save the changes performed on record returned by Lightning data services, you can call javascript function to update records in database. In javascript function, you do not need to call apex class methods to update record but use functions provided by Lightning data services framework to perform this operation.

Below is javascript function code :

({
    SaveRecord: function(component, event, helper) {
        component.find("recordHandler").saveRecord($A.getCallback(function(saveResult) {
            if (saveResult.state === "SUCCESS" || saveResult.state === "DRAFT") {
//record got updated in salesforce
                // Reload the view so that all components are refreshed after update
                $A.get("e.force:refreshView").fire();
            }
            else {
var errMsg = 'Unknown problem, state: ' + saveResult.state + ', error: ' +         JSON.stringify(saveResult.error);
                console.log(errMsg);
alert(errMsg);
            }
        }));
    },
})

Remember recordHandler is aura:id of  force:recordData tag


  • Create Record
For creating a new record, you need to use same force:recordPreview tag but no need to specify the recordId attribute.

<force:recordData aura:id="accountRecordCreator"
        layoutType="FULL"
        targetRecord="{!v.newAccount}"
       targetFields="{!v.newAccountInfo}"
        targetError="{!v.newAccountError}"
        />

In VF page, if you do not pass id parameter in URL and using standard controller, Save action creates new record in salesforce. Same way in Lightning data services, you do not need recordId for creating record. 
You need to create a template of record so that it can be used to create new record. It is similar to initializing the sobject to avoid null pointer exception in apex controllers. 

So in doInit function, first create template. Below is an example to create template for account:

doInit: function(component, event, helper) {
// Prepare a new record from template
component.find("accountRecordCreator").getNewRecord(
"Account", // sObject type (entity API name)
null,           // record type (null if no recordtype exist
false,         // skip cache?
$A.getCallback(function() {
var rec = component.get("v.newAccount");  //targetRecord attribute
var error = component.get("v.newAccountError");
if(error || (rec === null)) {
console.log("Error initializing record template: " + error);
}
else {
console.log("Record template initialized: " + rec.sobjectType);
}
})
);
},

In above code "newAccount" and "newAccountError" are component attribute which store information related to Lightning data services.

Now you have created a template and can use newAccount component attribute to specify field values on component. Once user specifies the field values, you can call javascript function on button click to create record. Below is sample code:

createContact: function(component, event, helper) {
component.find("accountRecordCreator").saveRecord(function(saveResult) {
if (saveResult.state === "SUCCESS" || saveResult.state === "DRAFT") {
//record got created in salesforce
//show toast on UI with message
// Reload the view so that all components are refreshed after update
$A.get("e.force:refreshView").fire();
}
else {
var errMsg = 'Unknown problem, state: ' + saveResult.state + ', error: ' + JSON.stringify(saveResult.error);
console.log(errMsg);
alert(errMsg);
}
});
},

If you want to modify any field value in javascript, then set the value in component attribute which hold object information. In our case attribute is "newAccount". So you can set value for "Type" field in Account (as shown below )then call saveRecord method.

component.set("v.newAccount.Type", 'Direct');


  • Delete Record 
For delete also, you need to use force:recordPreview tag and need to specify the recordId and in fields specify Id.

<force:recordData aura:id="recordDeleteHandler"
      recordId="{!v.recordId}"
      fields="Id"
      targetError="{!v.recordError}"
      />

In order to delete record on button click, call below mentioned controller function:

DeleteRecord: function(component, event, helper) {
        component.find("recordDeleteHandler").deleteRecord($A.getCallback(function(saveResult) {
            if (saveResult.state === "SUCCESS" || saveResult.state === "DRAFT") {
//record got deleted from salesforce
                // Reload the view so that all components are refreshed after update
                $A.get("e.force:refreshView").fire();
            }
            else {
 var errMsg = 'Unknown problem, state: ' + saveResult.state + ', error: ' + JSON.stringify(saveResult.error);
                console.log(errMsg);
alert(errMsg);
            }
        }));
    }



I have created a component which will will display list of Account by using Apex controller and then I am creating, editing, viewing and deleting records using Lightning Data Services. For all DMl operation on this component, I am not using server-side apex controller and performing these operations using code snippet shown above.

I have created different components as mentioned below:
  • AccountListView : This will display 10 Accounts and will use controller method to get list of accounts.
  • AccountView : To view account details. We will just pass account recordId to this component.
  • AccountEdit  : This will be used to edit or create new record. If we pass recordId to component, then it will update record. If we pass null in recordId then it will create new record.
After saving below code in your org, create App Page from Lightning App Builder section and add "AccountListView" component to it and activate it. After this open this app page and test the functionality.

If your org have recordtypes defined for Account object, then while creating new Account, you will get recordType selection page. No validation have been applied on fields as this is created for demo purpose only.

You can download complete code from below link:
Sample Code utilizing Lightning Data Services

Below is complete Code:


Looking forward for everyone's comments and feedback!!!!!

Changes to Lightning Data Services (LDS) in Summer'17 Release


More Blogs>>: 
DYNAMICALLY CREATING AND DESTROYING LIGHTNING COMPONENTS    
RAISING AND HANDLING CUSTOM EVENTS IN sALESFORCE lIGHTNING    
WHY TO USE DESIGN RESOURCE AND HOW TO ADD DYNAMIC OPTION TO DATASOURCE    
PASSING INNER WRAPPER CLASS TO LIGHTNING COMPONENT    
LIGHTNING COMPONENT FOR RECORDTYPE SELECTION FOR ANY SOBJECT