Newer Version Available

This content describes an older version of this product. View Latest

Using Apex to Work with Salesforce Records

Use Apex only if you need to customize your user interface to do more than what Lightning Data Service allows, such as using a SOQL query to select certain records. Apex provisions data that’s not managed and you must handle data refresh on your own.

The term sObject refers to any object that can be stored in Lightning Platform. This could be a standard object, such as Account, or a custom object that you create, such as a Merchandise object.

An sObject variable represents a row of data, also known as a record. To work with an object in Apex, declare it using the SOAP API name of the object. For example:

1Account a = new Account();
2MyCustomObject__c co = new MyCustomObject__c();

For more information on working on records with Apex, see Working with Data in Apex.

This example controller persists an updated Account record. Note that the update method has the @AuraEnabled annotation, which enables it to be called as a server-side controller action.

1public with sharing class AccountController {
2
3    @AuraEnabled
4    public static void updateAnnualRevenue(String accountId, Decimal annualRevenue) {
5        Account acct = [SELECT Id, Name, BillingCity FROM Account WHERE Id = :accountId];
6        acct.AnnualRevenue = annualRevenue;
7
8        // Perform isAccessible() and isUpdateable() checks here 
9        update acct;
10    }
11}

When using Apex controllers, load the data once and pass it to child components as attributes. This approach reduces the number of listeners and minimizes server calls, which improves performance and ensures that your components show consistent data.

Note

Differences Between Lightning Data Service and Apex

The lightning:record*Form and force:recordData components are the easiest way to work with records. They are built on top of Lightning Data Service, which manages field-level security and sharing for you in addition to managing data loading and refresh. You can use these components for objects that are supported by User Interface API

Use Apex only if you’re working with a scenario listed at Using Apex, You can call the Apex method imperatively, such as in response to a button click, as shown in the Loading Record Data from a Standard Object section. Alternatively, to load record data during component initialization, use the init handler, as shown in the Loading Record Data By Criteria section. When using Apex to load or provision data, you must handle data refresh on your own by invoking the Apex method again.

Loading Record Data from an Object

Load records from an object in an Apex controller. The following Apex controller has methods that return a list of tasks. Task is an object that isn’t supported by Lightning Data Service and the User Interface API. Therefore, we recommend using Apex to load task record data.

1public with sharing class TaskController {
2
3    @AuraEnabled(cacheable=true)
4    public static List<Task> getTasks() {
5        return [SELECT Subject, Priority, Status FROM Task];    }
6}
This example component uses the previous Apex controller to display a list of task record data when you press a button. The flexipage:availableForAllPageTypes interface denotes that you can use this example on a Lightning page.
1<!-- apexForTasks.cmp -->
2<aura:component implements="flexipage:availableForAllPageTypes" controller="TaskController">
3    <aura:attribute name="tasks" type="Task[]"/>
4    <lightning:card iconName="standard:task">
5        
6        <lightning:button label="Get Tasks" onclick="{!c.getMyTasks}"/>
7        <aura:iteration var="task" items="{!v.tasks}">
8            <p>{!task.Subject} : {!task.Priority}, {!task.Status}</p>
9        </aura:iteration>
10        
11    </lightning:card>
12</aura:component>
When you press the button, the following client-side controller calls the getTasks() method and sets the tasks attribute on the component. For more information about calling server-side controller methods, see Calling a Server-Side Action.
1// apexForTasksController.js
2({
3    getMyTasks: function(cmp){
4        var action = cmp.get("c.getTasks");
5        action.setCallback(this, function(response){
6            var state = response.getState();
7            if (state === "SUCCESS") {
8                cmp.set("v.tasks", response.getReturnValue());
9            }
10        });
11	 $A.enqueueAction(action);
12    }
13})

Loading Record Data By Criteria

As we’ve learned, to load a simple list of record data, you can use base components or force:recordData, as shown at Loading a Record. But to use a SOQL query to select certain records, use an Apex controller.

Remember that the method must be static, and global or public. The method must be decorated with @AuraEnabled(cacheable=true).

For example, query related cases based on an account Id and limit the result to 10 records.

1public with sharing class CaseController {
2    @AuraEnabled(cacheable=true)
3    public static List<Case> getCases(String accountId) {
4        return [SELECT AccountId, Id, Subject, Status, Priority, CaseNumber
5                FROM Case
6                WHERE AccountId = :accountId LIMIT 10];
7    }
8}

The client-side controller loads related cases using the init handler. The action.setParams() method passes in the record Id of the account record being viewed to the Apex controller,

1// casesForAccountController.js
2({
3    init : function(cmp, evt) {
4        var action = cmp.get("c.getCases");
5        action.setParams({
6            "accountId": cmp.get("v.recordId")
7        });
8        action.setCallback(this, function(response){
9            var state = response.getState();
10            if (state === "SUCCESS") {
11                cmp.set("v.cases", response.getReturnValue());
12            }
13        });
14        $A.enqueueAction(action);
15    }
16})
In your custom component, load a form that enables editing and updating of cases on an account record using lightning:recordEditForm, by performing these steps.
  • Query the relevant cases and set the result to the component attribute v.cases.
  • Iterate over the cases by passing in the case Id to the recordId attribute on lightning:recordEditForm.
The example implements the flexipage:availableForRecordHome and force:hasRecordId interfaces so you can use the example on an account record page.
1<!-- casesForAccount.cmp -->
2<aura:component implements="flexipage:availableForRecordHome,force:hasRecordId" controller="CaseController">
3    <aura:attribute name="cases" type="Case[]"/>
4    <aura:attribute name="recordId" type="Id" />
5    <aura:handler name="init" value="{! this }" action="{! c.init }"/>
6    
7    <aura:iteration items="{!v.cases}" var="case">
8        <lightning:card title="{!case.Id}" iconName="standard:case">
9            <lightning:recordEditForm objectApiName="Case" recordId="{!case.Id}">
10                <lightning:inputField fieldName="Subject"/>
11                <lightning:inputField fieldName="Status"/>
12
13                <!– Read-only field -->
14                <lightning:outputField fieldName="Origin" variant="label-hidden"/>
15
16                <lightning:button label="Update case" type="submit"/>
17            </lightning:recordEditForm>
18        </lightning:card>
19    </aura:iteration>
20</aura:component>

The case data on the account record is managed by Lightning Data Service since it uses lightning:recordEditForm; therefore, the case data that’s referenced (subject, status, and origin) reflects the latest data. However, if a case on the account is deleted or a new case is added to the account, you must invoke the Apex method again to query the new results.

Note

For read-only data, use lightning:outputField. To work with read-only data only, use lightning:recordViewForm or lightning:recordForm. For granular control of your UI, use force:recordData. For more information, see Lightning Data Service.