We can use Lightning Data Service to load, create, edit, or delete a record in the Aura and Lightning Web Components without using Apex code. Lightning Data Service handles sharing rules and all other security stuff for us. As we no longer need apex for basic operations so it improves the overall performance of the system.
So its a type of Standard controller for Lightning. For read-only access we don’t need any code. While for the code that modifies data, JavaScript controllers are only required. It’s built on highly efficient local storage that’s shared across all components that use it. So records loaded in Lightning Data Service are cached and shared across components.

So 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.
Lightning Data Service in Lightning Web Components
While the core functionality remains same in Aura and LWC for Lightning Data Service. We have few difference in how we get and send data in LWC. In LWC, Lightning Data Service is built on top of User Interface API. So UI API is a public Salesforce API that Salesforce uses to build Lightning Experience and Salesforce for iOS and Android. UI API responses also respect CRUD access, field-level security settings, and sharing settings. So this means that the framework displays only records and fields for which users have CRUD access and FLS visibility. We get all these features by using the Lightning Data Service wire adapters (lightning/ui*Api
) and the components built on it.



Components that Use Lightning Data Service
We have several standard components available in Aura and Lightning web components. We also have the form components. So one advantage of using the form-based components is that you can achieve many of your record display needs entirely in markup without JavaScript. Another powerful feature of the form-based components is automatic field mapping with field-level validation.
So below is the list of components which we have in Lightning and Lightning Web Components. While we have seperate post for most of them remaining we will discuss here.
- lightning:recordForm
- lightning:recordViewForm
- lightning:recordEditForm
- force:recordData
- lightning-record-edit-form
- lightning-record-form
- lightning-record-view-form
Loading a Record
Loading a record is the simplest operation in Lightning Data Service. We can accomplish it entirely in markup. To load a record using Lightning Data Service, in Lightning we can use lightning:recordForm, lightning:recordViewForm or force:recordData. force:recordData must specify the following.
- The ID of the record to load
- Which component attribute to assign the loaded record
- A list of fields to load
Sample Code
<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"/>
<force:recordData aura:id="recordLoader"
recordId="{!v.recordId}"
targetFields="{!v.simpleRecord}"
targetError="{!v.recordError}"
recordUpdated="{!c.handleRecordUpdated}"
/>
<!-- Display a lightning card with details about the record -->
<div class="Record Details">
<lightning:card iconName="standard:account" title="{!v.simpleRecord.Name}" >
<div class="slds-p-horizontal--small">
<p class="slds-text-heading--small">
<lightning:formattedText title="Billing City" value="{!v.simpleRecord.BillingCity}" /></p>
<p class="slds-text-heading--small">
<lightning:formattedText title="Billing State" value="{!v.simpleRecord.BillingState}" /></p>
</div>
</lightning:card>
</div>
<!-- Display Lightning Data Service errors, if any -->
<aura:if isTrue="{!not(empty(v.recordError))}">
<div class="recordError">
{!v.recordError}</div>
</aura:if>
</aura:component>
When the record loads or updates, to access the record fields in the JavaScript controller, use the component.get(“v.simpleRecord.fieldName”) syntax. force:recordData loads data asynchronously by design since it may go to the server to retrieve data. To track when the record is loaded or changed we use the recordUpdated event
({
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.");
console.log("You loaded a record in " +
component.get("v.simpleRecord.Industry"));
} 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
}
}
})
Display a Record with a Custom Layout Using lightning:recordViewForm or lightning-record-view-form
For example, If we want to create our own layout and want the SLDS support as well. Then we can use the lightning:recordViewForm or lightning-record-view-form to display data. So it will give us read only access of data.
Sample Code
<aura:component>
<lightning:recordViewForm recordId="001XXXXXXXXXXXXXXX"
objectApiName="Account">
<div class="slds-grid">
<div class="slds-col slds-size_2-of-3">
<lightning:outputField fieldName="Name" />
<lightning:outputField fieldName="Phone" />
</div>
<div class="slds-col slds-size_1-of-3">
<lightning:outputField fieldName="Industry" />
<lightning:outputField fieldName="AnnualRevenue" />
</div>
</div>
</lightning:recordViewForm>
</aura:component>
Now to use same form in LWC
<template>
<lightning-record-view-form record-id={recordId} object-api-name="Account">
<div class="slds-grid">
<div class="slds-col slds-size_1-of-2">
<lightning-output-field field-name="Name"></lightning-output-field>
<lightning-output-field field-name="Phone"></lightning-output-field>
</div>
<div class="slds-col slds-size_1-of-2">
<lightning-output-field field-name="Industry"></lightning-output-field>
<lightning-output-field field-name="AnnualRevenue"></lightning-output-field>
</div>
</div>
</lightning-record-view-form>
</template>
Saving a Record
To save a record using Lightning Data Service, we need to call saveRecord on the force:recordData component. And after the saveoperation complets we invoked a callback function.
The Lightning Data Service save operation is used in two cases.
- To save changes to an existing record
- To create and save a new record
component.find("recordHandler").saveRecord($A.getCallback(function(saveResult) {
// use the recordUpdated event handler to handle generic logic when record is changed
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));
}
}));
While the other lightning components provide OOTB functionalty to Save a record. So this is one of the reason we should use lightning components. As we get many feature support OOTB.
Load a Record in EDIT Mode
So to Load a record in Edit mode, we need to set mode attribute to “Edit”. So we can use Lightning:recordForm, Lightning:RecordEditForm to easily create edit screen. While in LWC we have lightning-record-form and lightning-record-edit-form.
To perform the save operation, we need to call saveRecord on the force:recordData component. saveRecord takes one argument—a callback function to be invoked when the operation completes. So this callback function receives a SaveRecordResult as its only parameter. SaveRecordResult includes a state attribute that indicates success or error, and other details.
Sample Code
<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 lightning card with details about the record -->
<div class="Record Details">
<lightning:card iconName="standard:account" title="{!v.simpleRecord.Name}" >
<div class="slds-p-horizontal--small">
<p class="slds-text-heading--small">
<lightning:formattedText title="Billing State" value="{!v.simpleRecord.BillingState}" /></p>
<p class="slds-text-heading--small">
<lightning:formattedText title="Billing City" value="{!v.simpleRecord.BillingCity}" /></p>
</div>
</lightning:card>
</div>
<!-- Display an editing form -->
<div class="Record Details">
<lightning:card iconName="action:edit" title="Edit Account">
<div class="slds-p-horizontal--small">
<lightning:input label="Account Name" value="{!v.simpleRecord.Name}"/>
<br/>
<lightning:button label="Save Account" variant="brand" onclick="{!c.handleSaveRecord}" />
</div>
</lightning:card>
</div>
<!-- Display Lightning Data Service errors, if any -->
<aura:if isTrue="{!not(empty(v.recordError))}">
<div class="recordError">
{!v.recordError}</div>
</aura:if>
</aura:component>
As we need to save the record so for this we need to call saveRecord in Javascript controller
({
handleSaveRecord: function(component, event, helper) {
component.find("recordHandler").saveRecord($A.getCallback(function(saveResult) {
// use the recordUpdated event handler to handle generic logic when record is changed
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));
}
}));
},
/**
* 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
}
}
})
So in the next post we will cover the remaining points to delete the record and handle record change events. If you want to add anything let me know in the comments section. Happy programming 🙂
Like My Facebook page for more updates.
1 thought on “Lightning Data Service: Loading Data without Apex”