Lightning Data service

We can use Lightning Data Service to:

  • load
  • create
  • edit
  • delete

a record in your component without requiring Apex code.

  • Handles sharing rules and field-level security for you.
  • Improves performance and user interface consistency.
  • Similar to Visualforce standard Controller
  • Whenever possible, use Lightning Data Service to read and modify Salesforce data in your components.

  • Read-only access can be entirely declarative in your component’s markup to eliminate Apex code entirely.

  • Your data access code is consolidated into your component, which significantly reduces complexity.

Benefits

  • Built on highly efficient local storage that’s shared across all components that use it.
  • Records loaded in Lightning Data Service are cached and shared across components.
    • Components accessing the same record see significant performance improvements, because a record is loaded only once, no matter how many components are using it.
    • Shared records also improve user interface consistency.
    • When one component updates a record, the other components using it are notified, and in most cases, refresh automatically.

Loading Record force:recordData


<!--

flexipage:availableForRecordHome:
 This component can be added to:
    record home page in the Lightning App Builder
    (Lightning Record Page)

force:lightningQuickActionWithoutHeader:
    as a custom action
    (Lightning Quick Action)

force:hasRecordId:
    The record ID is supplied by the implicit recordId attribute
    added by the force:hasRecordId interface.


<aura:component
implements="flexipage:availableForRecordHome,force:lightningQuickActionWithoutHeader, force:hasRecordId">



    <aura:attribute name="record" type="Object"/>
    <aura:attribute name="simpleRecord" type="Object"/>
    <aura:attribute name="recordError" type="String"/>

    <!--
      layoutType:
      All fields on that layoutType are loaded for the record

      Or  provide fields attribute to specify the list of fields
      to load with the fields attribute

      example:
      fields="Name,BillingCity,BillingState"
      -------------
      recordId:
      The ID of the record to load
      -------------
      targetRecord:
      targetFields:
      Which component attribute to assign the loaded record


    -->
    <force:recordData aura:id="recordLoader"
      recordId="{!v.recordId}"
      layoutType="FULL"



      targetRecord="{!v.record}"
      targetFields="{!v.simpleRecord}"
      targetError="{!v.recordError}"

      recordUpdated="{!c.handleRecordUpdated}"
      />

    <!-- Display a header with details about the record -->
    <div class="slds-page-header" role="banner">
        <p class="slds-text-heading_label">{!v.simpleRecord.Name}</p>
        <h1 class="slds-page-header__title slds-m-right_small
            slds-truncate slds-align-left">
            {!v.simpleRecord.BillingCity}, {!v.simpleRecord.BillingState}
        </h1>
    </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>

Controller

({
    handleRecordUpdated: function(component, event, helper) {
        var eventParams = event.getParams();
        if(eventParams.changeType === "LOADED") {
           // record is loaded (render other component which needs record data value)
            console.log("Record is loaded successfully.");
        } else if(eventParams.changeType === "CHANGED") {
            // record is changed
        } else if(eventParams.changeType === "REMOVED") {
            // record is deleted
        } else if(eventParams.changeType === "ERROR") {
            // there’s an error while loading, saving, or deleting the record
        }
    }
})

How to use this in Lightning App Builder

Component

How it is in Developer Console

Lightning App Builder

How to use this in Lightning App Builder -1

How to use this in Lightning App Builder -2

How to use this in Lightning App Builder -3

How to use this in Lightning App Builder -4

How to use this in Lightning App Builder -5

Saving a Record

Two cases:

  • Save changes to an existing record
    • mode="EDIT" and use saveRecord on force.recordData
  • Create and Save a new record

To save a record using Lightning Data Service:

call saveRecord on the force:recordData component, and pass in a callback function to be invoked after the save operation completes.


  handleSaveRecord: function(component, event, helper) {
         // saveRecord
          component.find("recordHandler").saveRecord($A.getCallback(function(saveResult) {
              // NOTE: If you want a specific behavior(an action or UI behavior) when this action is successful
              // then handle that in a callback (generic logic when record is changed should be handled in recordUpdated event handler)
              if (saveResult.state === "SUCCESS" || saveResult.state === "DRAFT") {
                  // handle component related logic in event handler
              } else if (saveResult.state === "INCOMPLETE") {
                  console.log("User is offline, device doesn't support drafts.");
              } else if (saveResult.state === "ERROR") {
                  console.log('Problem saving record, error: ' + JSON.stringify(saveResult.error));
              } else {
                  console.log('Unknown problem, state: ' + saveResult.state + ', error: ' + JSON.stringify(saveResult.error));
              }
          }));
      }
  <<aura:component implements="flexipage:availableForRecordHome,force:hasRecordId">

    <aura:attribute name="record" type="Object"/>
    <aura:attribute name="simpleRecord" type="Object"/>
    <aura:attribute name="recordError" type="String"/>

    <force:recordData aura:id="recordHandler"
      recordId="{!v.recordId}"
      layoutType="FULL"
      targetRecord="{!v.record}"
      targetFields="{!v.simpleRecord}"
      targetError="{!v.recordError}"

      mode="EDIT"

      recordUpdated="{!c.handleRecordUpdated}"

      />

    <!-- Display a header with details about the record -->
    <div class="slds-page-header" role="banner">
        <p class="slds-text-heading_label">Edit Record</p>
        <h1 class="slds-page-header__title slds-m-right_small
            slds-truncate slds-align-left">{!v.simpleRecord.Name}</h1>
    </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>

    <!-- Display an editing form -->
    <lightning:input aura:id="recordName" name="recordName" label="Name"
                  value="{!v.simpleRecord.Name}" required="true"/>

     <lightning:button label="Save Record" onclick="{!c.handleSaveRecord}"

               variant="brand" class="slds-m-top_medium"/>
</aura:component>
  /**
     * Control the component behavior here when record is changed (via any component)
     */
    handleRecordUpdated: function(component, event, helper) {
        var eventParams = event.getParams();
        if(eventParams.changeType === "CHANGED") {
            // get the fields that changed for this record
            var changedFields = eventParams.changedFields;
            console.log('Fields that are changed: ' + JSON.stringify(changedFields));
            // record is changed, so refresh the component (or other component logic)
            var resultsToast = $A.get("e.force:showToast");
            resultsToast.setParams({
                "title": "Saved",
                "message": "The record was updated."
            });
            resultsToast.fire();

        } else if(eventParams.changeType === "LOADED") {
            // record is loaded in the cache
        } else if(eventParams.changeType === "REMOVED") {
            // record is deleted and removed from the cache
        } else if(eventParams.changeType === "ERROR") {
            // there’s an error while loading, saving or deleting the record
        }
    }

Creating a Record

  • Declare force:recordData without assigning a recordId.

  • Load a record template by calling the getNewRecord function on force:recordData

  • `getNewRecord doesn’t return a result. It simply prepares an empty record and assigns it to the targetRecord attribute, which is used by the contact form in the component’s markup.
  • Apply values to the new record

  • Save the record by calling the saveRecord function on force:recordData.

<aura:component implements="flexipage:availableForRecordHome, force:hasRecordId">

    <aura:attribute name="newContact" type="Object"/>
    <aura:attribute name="simpleNewContact" type="Object"/>
    <aura:attribute name="newContactError" type="String"/>

    <aura:handler name="init" value="{!this}" action="{!c.doInit}"/>

    <!-- note there is no recordId: recordId="{!v.recordId} here -->
    <force:recordData aura:id="contactRecordCreator"
                      layoutType="FULL"
                      targetRecord="{!v.newContact}"
                      targetFields="{!v.simpleNewContact}"
                      targetError="{!v.newContactError}" />

    <div class="slds-page-header" role="banner">
        <p class="slds-text-heading_label">Create Contact</p>
    </div>

    <!-- Display Lightning Data Service errors -->
    <aura:if isTrue="{!not(empty(v.newContactError))}">
        <div class="recordError">
            <ui:message title="Error" severity="error" closable="true">
                {!v.newContactError}
            </ui:message>
        </div>
    </aura:if>

    <!-- Display the new contact form -->
    <div class="slds-form_stacked">
        <lightning:input aura:id="contactField" name="firstName" label="First Name"
                         value="{!v.simpleNewContact.FirstName}" required="true"/>

        <lightning:input aura:id="contactField" name="lastname" label="Last Name"
                      value="{!v.simpleNewContact.LastName}" required="true"/>

        <lightning:input aura:id="contactField" name="title" label="Title"
                      value="{!v.simpleNewContact.Title}" />

        <lightning:button label="Save contact" onclick="{!c.handleSaveContact}"
                   variant="brand" class="slds-m-top_medium"/>
   </div>

</aura:component>

({
    doInit: function(component, event, helper) {
        // Prepare a new record from template
        component.find("contactRecordCreator").getNewRecord(

            "Contact", // sObject type (objectApiName)

            null,      // recordTypeId
            // If not specified, the default record type for the object is used,
            // as defined in the user’s profile.

            false,     // skip cache?
            // Whether to load the record template from the server instead of the client-side Lightning Data Service cache.
            // Defaults to false

            // callback function invoked after the empty record is created. This function receives no arguments.
            /*
             The callback passed to getNewRecord() must be wrapped in $A.getCallback()
              to ensure correct access context when the callback is invoked.
             If the callback is passed in without being wrapped in $A.getCallback(),
              any attempt to access private attributes of your component results in access check failures.
            */

            /*

            Even if you’re not accessing private attributes,
              it’s a best practice to always wrap the callback function for getNewRecord() in $A.getCallback(). Never mix (contexts), never worry.
            */

            $A.getCallback(function() {
                var rec = component.get("v.newContact");
                var error = component.get("v.newContactError");
                if(error || (rec === null)) {
                    console.log("Error initializing record template: " + error);
                    return;
                }
                console.log("Record template initialized: " + rec.sobjectType);
            })
        );
    },

    handleSaveContact: function(component, event, helper) {
        if(helper.validateContactForm(component)) {
            component.set("v.simpleNewContact.AccountId", component.get("v.recordId"));
            component.find("contactRecordCreator").saveRecord(function(saveResult) {
                if (saveResult.state === "SUCCESS" || saveResult.state === "DRAFT") {
                    // record is saved successfully
                    var resultsToast = $A.get("e.force:showToast");
                    resultsToast.setParams({
                        "title": "Saved",
                        "message": "The record was saved."
                    });
                    resultsToast.fire();

                } else if (saveResult.state === "INCOMPLETE") {
                    // handle the incomplete state
                    console.log("User is offline, device doesn't support drafts.");
                } else if (saveResult.state === "ERROR") {
                    // handle the error state
                    console.log('Problem saving contact, error: ' + JSON.stringify(saveResult.error));
                } else {
                    console.log('Unknown problem, state: ' + saveResult.state + ', error: ' + JSON.stringify(saveResult.error));
                }
            });
        }
    }
})

results matching ""

    No results matching ""