1. Introduction
Lightning Data Service (LDS) is a powerful feature in the Salesforce Lightning framework that streamlines the process of working with Salesforce data in your Lightning components. It handles data caching, sharing, and data synchronization between components, allowing developers to focus on building rich user interfaces without worrying about server-side interactions.
This in-depth tutorial will guide you through the ins and outs of Lightning Data Service, providing you with the knowledge and skills to leverage it effectively in your Salesforce applications.
2. What is Lightning Data Service?
Lightning Data Service is a standardized way to create, read, update, and delete (CRUD) records in Lightning components without the need for Apex controllers. It provides a centralized data caching layer that improves performance and ensures data consistency across components.
Key Points:
- Eliminates the need for Apex controllers for basic CRUD operations.
- Ensures data consistency across components using the same record data.
- Improves performance through efficient data caching.
- Simplifies code by handling data operations declaratively.
3. Why Use Lightning Data Service?
Lightning Data Service offers several advantages over traditional data handling methods in Salesforce:
- Simplified Development: Reduce code complexity by eliminating the need for Apex controllers and SOQL queries for basic data operations.
- Improved Performance: Data is cached on the client-side, reducing server round-trips and improving component load times.
- Data Consistency: Multiple components accessing the same record automatically stay in sync.
- Error Handling: Built-in mechanisms for handling errors and data validation.
4. Key Features of Lightning Data Service
- Record Caching: Efficiently caches records to minimize server calls.
- Declarative Data Access: Access data using simple tags or decorators.
- Automatic Data Refresh: Updates all components when a record changes.
- Optimistic UI: Provides immediate feedback to users by updating the UI before the server confirms changes.
5. Understanding the Data Lifecycle
Before diving into code, it’s essential to understand how Lightning Data Service manages the data lifecycle:
- Data Fetching: When a component requests a record, LDS first checks if the data is available in the cache.
- Cache Management: If the data is in the cache and valid, it’s returned immediately. Otherwise, LDS fetches it from the server.
- Data Sharing: If multiple components request the same record, LDS ensures they share the same cached data.
- Data Updating: When a record is updated, LDS synchronizes changes across all components using that record.
- Data Deletion: LDS handles the deletion of records and updates the cache accordingly.
6. Using Lightning Data Service in Aura Components
6.1. The <force:recordData>
Component
In Aura components, the <force:recordData>
component is used to interact with Lightning Data Service. It provides attributes and events to perform CRUD operations.
Key Attributes:
recordId
: The ID of the record to load, create, or update.targetFields
: The attribute that holds the record’s field values.targetError
: Holds any errors encountered during operations.fields
: Specifies the fields to retrieve.mode
: The operation mode (VIEW
,EDIT
, orCREATE
).layoutType
: Determines the layout of the data (FULL
,COMPACT
).
Key Events:
recordUpdated
: Fired when the record is loaded, updated, or deleted.error
: Fired when an error occurs during an operation.
6.2. Basic Usage Example
Let’s create a simple Aura component that displays and updates an account record.
Step 1: Create the Component
AccountComponent.cmp
<aura:component implements="flexipage:availableForAllPageTypes" access="global" >
<!-- Attributes -->
<aura:attribute name="recordId" type="Id" />
<aura:attribute name="account" type="Object" />
<aura:attribute name="errorMessage" type="String" />
<!-- force:recordData -->
<force:recordData
aura:id="accountRecord"
recordId="{!v.recordId}"
targetFields="{!v.account}"
targetError="{!v.errorMessage}"
fields="Name,Industry,Phone"
mode="EDIT"
recordUpdated="{!c.handleRecordUpdated}" />
<!-- Display Fields -->
<lightning:input label="Name" value="{!v.account.Name}" />
<lightning:input label="Industry" value="{!v.account.Industry}" />
<lightning:input label="Phone" value="{!v.account.Phone}" />
<!-- Save Button -->
<lightning:button label="Save" onclick="{!c.handleSave}" />
</aura:component>
Step 2: Add Controller Logic
AccountComponentController.js
({
handleRecordUpdated: function(component, event, helper) {
// Handle the recordUpdated event
var eventParams = event.getParams();
if(eventParams.changeType === "LOADED") {
// Record is loaded
} else if(eventParams.changeType === "ERROR") {
// Handle errors
console.error('Error loading record: ' + component.get("v.errorMessage"));
}
},
handleSave: function(component, event, helper) {
component.find("accountRecord").saveRecord($A.getCallback(function(saveResult) {
if (saveResult.state === "SUCCESS" || saveResult.state === "DRAFT") {
// Record is saved successfully
console.log('Record saved successfully');
} else if (saveResult.state === "ERROR") {
// Handle save errors
console.error('Error saving record: ' + JSON.stringify(saveResult.error));
}
}));
}
})
Explanation:
- force:recordData: Handles data retrieval and updates.
- targetFields: Binds the record fields to the
v.account
attribute. - handleRecordUpdated: Manages the record’s loading state and errors.
- handleSave: Invokes the
saveRecord
method to persist changes.
7. Using Lightning Data Service in Lightning Web Components
In Lightning Web Components, you interact with Lightning Data Service using wire adapters and functions provided by the lightning/ui*Api
modules.
7.1. @wire
Adapter Methods
7.1.1. Importing Wire Adapters
- Record Data:
getRecord
: Retrieves a record.getRecordNotifyChange
: Notifies LDS of record changes.- Record Creation:
createRecord
: Creates a new record.
7.1.2. Basic Usage Example
ExampleComponent.js
import { LightningElement, api, wire } from 'lwc';
import { getRecord, updateRecord } from 'lightning/uiRecordApi';
// Specify fields
const fields = ['Account.Name', 'Account.Industry', 'Account.Phone'];
export default class ExampleComponent extends LightningElement {
@api recordId;
account;
@wire(getRecord, { recordId: '$recordId', fields })
wiredRecord({ error, data }) {
if (data) {
this.account = data;
} else if (error) {
console.error('Error fetching record:', error);
}
}
handleInputChange(event) {
const field = event.target.name;
this.account = { ...this.account, [field]: event.target.value };
}
handleSave() {
const fields = {
Id: this.recordId,
Name: this.account.fields.Name.value,
Industry: this.account.fields.Industry.value,
Phone: this.account.fields.Phone.value,
};
const recordInput = { fields };
updateRecord(recordInput)
.then(() => {
console.log('Record updated successfully');
})
.catch(error => {
console.error('Error updating record:', error);
});
}
}
ExampleComponent.html
<template>
<lightning-input
label="Name"
name="Name"
value={account.fields.Name.value}
onchange={handleInputChange}
></lightning-input>
<lightning-input
label="Industry"
name="Industry"
value={account.fields.Industry.value}
onchange={handleInputChange}
></lightning-input>
<lightning-input
label="Phone"
name="Phone"
value={account.fields.Phone.value}
onchange={handleInputChange}
></lightning-input>
<lightning-button label="Save" onclick={handleSave}></lightning-button>
</template>
7.2. Imperative Apex Calls
While LDS covers most CRUD operations, sometimes you need to call Apex methods directly.
Example
import getAccounts from '@salesforce/apex/AccountController.getAccounts';
export default class AccountList extends LightningElement {
accounts;
error;
connectedCallback() {
getAccounts()
.then((result) => {
this.accounts = result;
})
.catch((error) => {
this.error = error;
});
}
}
Data Caching and Consistency
Lightning Data Service automatically handles data caching:
- Shared Cache: Components share the same cache, ensuring data consistency.
- Automatic Updates: When a record changes, LDS notifies all components using that record.
- Cache Invalidation: LDS invalidates the cache when data becomes stale.
Tips:
- Avoid Redundant Calls: Use LDS to prevent unnecessary server requests.
- Leverage Caching: Trust LDS to manage data freshness and validity.
Error Handling
Proper error handling ensures a smooth user experience.
Common Error Scenarios:
- Validation Errors: Occur when data doesn’t meet Salesforce validation rules.
- Server Errors: Issues with the server or network.
Handling Errors in LWC
updateRecord(recordInput)
.then(() => {
// Success logic
})
.catch(error => {
// Error handling
if (Array.isArray(error.body)) {
console.error('Validation errors:', error.body);
} else {
console.error('Server error:', error.body.message);
}
});
Handling Errors in Aura
saveRecordResult = component.find("accountRecord").saveRecord();
if (saveRecordResult.state === "ERROR") {
var errors = saveRecordResult.error;
// Process errors
}
Best Practices
- Use LDS When Possible: Prefer LDS over Apex for standard CRUD operations.
- Minimize Apex Calls: Only use Apex when necessary, such as complex business logic.
- Handle Errors Gracefully: Provide user-friendly error messages.
- Avoid DML in Loops: Be cautious of governor limits; LDS helps mitigate this.
- Security Considerations: Ensure field-level security and sharing rules are respected.
Limitations and Considerations
- Custom Business Logic: LDS doesn’t replace the need for Apex when custom logic is required.
- Unsupported Objects: Some standard and custom objects may not be fully supported.
- Complex Queries: For SOQL queries with complex conditions, Apex is still necessary.
- Record-Level Security: LDS respects user permissions; ensure users have the necessary access.
Lightning Data Service – Advanced Topics
Data Service Events
LDS provides events to react to data changes:
- Data Refresh: Use
getRecordNotifyChange(records)
to refresh data across components.
Example
import { getRecordNotifyChange } from 'lightning/uiRecordApi';
getRecordNotifyChange([{ recordId: this.recordId }]);
Custom Record Forms
Use lightning:recordForm
and lightning-record-form
for rapid form development.
In Aura Components
<lightning:recordForm
recordId="{!v.recordId}"
objectApiName="Account"
layoutType="Full"
mode="Edit"/>
In Lightning Web Components
<lightning-record-form
record-id={recordId}
object-api-name="Account"
layout-type="Full"
mode="edit">
</lightning-record-form>
Conclusion
Lightning Data Service is an invaluable tool for Salesforce developers, simplifying data operations and improving application performance. By leveraging LDS, you can build more efficient, responsive, and maintainable Lightning applications.
Recap:
- LDS handles CRUD operations without Apex.
- Provides automatic caching and data consistency.
- Simplifies error handling and optimizes performance.
- Best used for standard data operations; use Apex for complex logic.
Additional Resources
- Salesforce Documentation:
- Lightning Data Service Basics
- LWC Lightning Data Service
- Trailhead Modules:
- Use Lightning Data Service
- Apex Hours Videos:
- Lightning Data Service Deep Dive