Wire Apex Methods to Lightning Web Components
To read Salesforce data, Lightning web components use a reactive wire service. Use
@wire
in a component’s JavaScript class to
specify an Apex method. You can @wire
a property or
a function to receive the data. To operate on the returned data, @wire
a function.
To use @wire
to call an Apex method, annotate
the Apex method with @AuraEnabled(cacheable=true)
. A client-side Lightning Data Service cache
is checked before issuing the network call to invoke the Apex method on the server. To
refresh stale data, call refreshApex()
, because
Lightning Data Service doesn’t manage data provisioned by Apex.
Use this syntax to import an Apex method and wire it to a component.
import apexMethodName from
'@salesforce/apex/Namespace.Classname.apexMethodReference';
@wire(apexMethodName, { apexMethodParams })
propertyOrFunction;
apexMethodName
—A symbol that identifies the Apex method.apexMethodReference
—The name of the Apex method to import.Classname
—The name of the Apex class.Namespace
—The namespace of the Salesforce organization. Specify a namespace unless the organization uses the default namespace (c
), in which case don’t specify it.apexMethodParams
—An object with properties that match the parameters of theapexMethod
, if needed. If a parameter value isnull
, the method is called. If a parameter value isundefined
, the method isn’t called. If the Apex method is overloaded, the choice of what method to call is non-deterministic (effectively random), and the parameters passed may cause errors now or in the future. Don't overload@AuraEnabled
Apex methods.ImportantapexMethodParams
is an object. To pass parameter values to an Apex method, pass an object whose properties match the parameters of the Apex method. For example, if the Apex method takes a string parameter, don’t pass a string directly. Instead, pass an object that contains a property whose value is a string.propertyOrFunction
—A private property or function that receives the stream of data from the wire service. If a property is decorated with@wire
, the results are returned to the property’sdata
property orerror
property. If a function is decorated with@wire
, the results are returned in an object with adata
property or anerror
property.Note Thedata
property and theerror
property are hardcoded values in the API. You must use these values.
Wire an Apex Method to a Property
Let’s look at the apexWireMethodToProperty component from the lwc-recipes repo. This component prints a list of contacts returned by an Apex method.
To get its data, the component wires an Apex method. The Apex method makes a SOQL
query that returns a list of contacts that each have a picture. (This component
doesn’t render the pictures, but other sample components do.) As we’ve learned, to
return a simple list of contacts, it’s best to use getListUi
. But to use a SOQL query to select certain
records, we must use an Apex method.
Remember that the method must be static
, and
global
or public
. The method must be decorated with @AuraEnabled(cacheable=true)
.
public with sharing class ContactController {
@AuraEnabled(cacheable=true)
public static List<Contact> getContactList() {
return [
SELECT Id, Name, Title, Phone, Email, Picture__c
FROM Contact
WHERE Picture__c != null
WITH SECURITY_ENFORCED
LIMIT 10
];
}
}
The component’s JavaScript code imports the Apex method and invokes it via the wire
service. The wire service either provisions the list of contacts to the contacts.data
property, or returns an error to
the contacts.error
property.
// apexWireMethodToProperty.js
import { LightningElement, wire } from 'lwc';
import getContactList from '@salesforce/apex/ContactController.getContactList';
export default class ApexWireMethodToProperty extends LightningElement {
@wire(getContactList) contacts;
}
The template uses the if:true
directive
to check whether the contacts.data
property
is truthy. If it is, it iterates over it and renders the name of each contact. If
contacts.error
is truthy, the component
renders <c-error-panel>
.
<!-- apexWireMethodToProperty.html -->
<template>
<lightning-card title="ApexWireMethodToProperty" icon-name="custom:custom63">
<div class="slds-m-around_medium">
<template if:true={contacts.data}>
<template for:each={contacts.data} for:item="contact">
<p key={contact.Id}>{contact.Name}</p>
</template>
</template>
<template if:true={contacts.error}>
<c-error-panel errors={contacts.error}></c-error-panel>
</template>
</div>
</lightning-card>
</template>
Wire an Apex Method with a Dynamic Parameter
Now let’s wire an Apex method that takes a parameter. This component is also in the lwc-recipes repo.
The Apex method takes a string parameter called searchKey
, and returns a list of contacts whose name contains the
string.
// ContactController.cls
public with sharing class ContactController {
@AuraEnabled(cacheable=true)
public static List<Contact> findContacts(String searchKey) {
String key = '%' + searchKey + '%';
return [
SELECT Id, Name, Title, Phone, Email, Picture__c
FROM Contact
WHERE Name LIKE :key AND Picture__c != null
WITH SECURITY_ENFORCED
LIMIT 10
];
}
}
The component’s JavaScript prefaces the value of the searchKey
parameter with $
to
indicate that it’s dynamic and reactive. It references a property of the component
instance. If its value changes, the template rerenders.
@wire
the component to the Apex
method, the first parameter is a string, which is the name of the Apex
method—in this case, findContacts
. The second @wire
parameter is an object that contains the parameters to pass
to the Apex method. So, for example, even though findContacts
takes a string, we don’t pass a string as the second
@wire
parameter. Instead, we pass
an object that contains a property whose value is a string: { searchKey: '$searchKey' }
. (If the Apex
method took another parameter, we would add another property to the
object.)// apexWireMethodWithParams.js
import { LightningElement, wire } from 'lwc';
import findContacts from '@salesforce/apex/ContactController.findContacts';
/** The delay used when debouncing event handlers before invoking Apex. */
const DELAY = 300;
export default class ApexWireMethodWithParams extends LightningElement {
searchKey = '';
@wire(findContacts, { searchKey: '$searchKey' })
contacts;
handleKeyChange(event) {
// Debouncing this method: Do not update the reactive property as long as this
function is
// being called within a delay of DELAY. This is to avoid a very large number
of Apex method calls.
window.clearTimeout(this.delayTimeout);
const searchKey = event.target.value;
this.delayTimeout = setTimeout(() => {
this.searchKey = searchKey;
}, DELAY);
}
}
The template uses searchKey
as the value
of the <lightning-input>
field.
<!-- apexWireMethodWithParams.html -->
<template>
<lightning-card title="ApexWireMethodWithParams" icon-name="custom:custom63">
<div class="slds-m-around_medium">
<lightning-input type="search" onchange={handleKeyChange}
class="slds-m-bottom_small" label="Search" value={searchKey}></lightning-input>
<template if:true={contacts.data}>
<template for:each={contacts.data} for:item="contact">
<p key={contact.Id}>{contact.Name}</p>
</template>
</template>
<template if:true={contacts.error}>
<c-error-panel errors={contacts.error}></c-error-panel>
</template>
</div>
</lightning-card>
</template>
Wire an Apex Method with Complex Parameters
This example demonstrates how to call an Apex method that takes an object as a
parameter. The @wire
syntax is the same for
any Apex method, but this example shows a pattern for building and passing an
object.
The component has three input fields that take a string, a number, and a number of
list items that the component uses to generate a list. The Apex method simply
concatenates and returns a string based on the values. When an input value changes,
the @wire
calls the Apex method and
provisions new data.
<!-- apexWireMethodWithComplexParams.html -->
<template>
<lightning-card
title="ApexWireMethodWithComplexParams"
icon-name="custom:custom63"
>
<div class="slds-var-m-around_medium">
<lightning-input
label="String"
type="string"
value={stringValue}
class="string-input"
onchange={handleStringChange}
></lightning-input>
<lightning-input
label="Number"
type="number"
min="0"
max="100"
value={numberValue}
class="number-input"
onchange={handleNumberChange}
></lightning-input>
<lightning-input
label="List items"
type="number"
min="0"
max="10"
value={listItemValue}
class="list-item-input"
onchange={handleListItemChange}
></lightning-input>
<br />
<template if:true={apexResponse.data}>
<p>{apexResponse.data}</p>
</template>
</div>
<template if:true={apexResponse.error}>
<c-error-panel errors={error}></c-error-panel>
</template>
</lightning-card>
</template>
The Apex method takes an object, CustomWrapper
.
public with sharing class ApexTypesController {
@AuraEnabled(cacheable=true)
public static String checkApexTypes(CustomWrapper wrapper) {
// The values are based on the data that is defined in the
// apexWireMethodWithComplexParams Lightning web component.
String response =
'You entered "' +
wrapper.someString +
'" as String, and "' +
wrapper.someInteger +
'" as Integer value. The list contained ' +
wrapper.someList.size() +
' items.';
return response;
}
}
public with sharing class CustomWrapper {
@TestVisible
class InnerWrapper {
@AuraEnabled
public Integer someInnerInteger { get; set; }
@AuraEnabled
public String someInnerString { get; set; }
}
@AuraEnabled
public Integer someInteger { get; set; }
@AuraEnabled
public String someString { get; set; }
@AuraEnabled
public List<InnerWrapper> someList { get; set; }
}
To pass parameter values from a component to an Apex method, use an object. In this
example, the Apex method takes an object, CustomWrapper
, so the component builds a matching object to pass in
the @wire
.
// apexWireMethodWithComplexParams.js
import { LightningElement, wire } from 'lwc';
import checkApexTypes from '@salesforce/apex/ApexTypesController.checkApexTypes';
export default class ApexWireMethodWithComplexParams extends LightningElement {
listItemValue = 0;
numberValue = 50;
stringValue = 'Some string';
parameterObject = {
someString: this.stringValue,
someInteger: this.numberValue,
someList: []
};
@wire(checkApexTypes, { wrapper: '$parameterObject' })
apexResponse;
handleStringChange(event) {
this.parameterObject = {
...this.parameterObject,
someString: (this.stringValue = event.target.value)
};
}
handleNumberChange(event) {
this.parameterObject = {
...this.parameterObject,
someInteger: (this.numberValue = parseInt(event.target.value, 10))
};
}
handleListItemChange(event) {
const someList = [];
for (let i = 0; i < event.target.value; i++) {
someList.push({
someInnerString: this.stringValue,
someInnerInteger: this.numberValue
});
}
this.parameterObject = {
...this.parameterObject,
someList
};
}
}
Wire an Apex Method to a Function
Now let’s look at the apexWireMethodToFunction component from the lwc-recipes repo. This component wires an Apex method call to a function. Because the results are provisioned to a function, the JavaScript can operate on the results. Also, the template accesses the data a bit differently than if results were provisioned to a property.
The rendered component is the same as apexWireMethodToProperty (except for the header).
This component calls the same Apex method as
apexWireMethodToProperty. The method must be static
, and global
or public
. The method
must be decorated with @AuraEnabled(cacheable=true)
.
// ContactController.cls
public with sharing class ContactController {
@AuraEnabled(cacheable=true)
public static List<Contact> getContactList() {
return [
SELECT Id, Name, Title, Phone, Email, Picture__c
FROM Contact
WHERE Picture__c != null
WITH SECURITY_ENFORCED
LIMIT 10
];
}
}
The component’s JavaScript code imports the Apex method and invokes it via the wire
service. The wire service provisions the results to the wiredContacts()
function via an object with
either an error
or data
property. If the wire service provisions
data
, it’s assigned to this.contacts
, which is used in the template.
If it provisions error
, it’s assigned to
this.error
, which is also used in the
template. If the value of these properties change, the template
rerenders.
// apexWireMethodToFunction.js
import { LightningElement, wire } from 'lwc';
import getContactList from '@salesforce/apex/ContactController.getContactList';
export default class ApexWireMethodToFunction extends LightningElement {
contacts;
error;
@wire(getContactList)
wiredContacts({ error, data }) {
if (data) {
this.contacts = data;
this.error = undefined;
} else if (error) {
this.error = error;
this.contacts = undefined;
}
}
}
The template uses the if:true
directive to check
for the JavaScript contacts
property. If it
exists, it iterates over it and renders the name of each contact. If the error
property exists, the component renders
<c-error-panel>
.
<!-- apexWireMethodToFunction.html -->
<template>
<lightning-card title="ApexWireMethodToFunction" icon-name="custom:custom63">
<div class="slds-m-around_medium">
<template if:true={contacts}>
<template for:each={contacts} for:item="contact">
<p key={contact.Id}>{contact.Name}</p>
</template>
</template>
<template if:true={error}>
<c-error-panel errors={error}></c-error-panel>
</template>
</div>
</lightning-card>
</template>
No comments:
Post a Comment