To control when the method invocation occurs (for example, in response to clicking a button), call the method imperatively. When you call a method imperatively, you receive only a single response. Compare this behavior with @wire, which delegates control to the framework and results in a stream of values being provisioned.
In the following scenarios, you must call an Apex method imperatively as opposed to using @wire.
To call a method that isn’t annotated with cacheable=true, which includes any method that inserts, updates, or deletes data.
To call a method from an ES6 module that doesn’t extend LightningElement
If an Apex method is marked 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. However, Lightning Data Service doesn’t manage data provisioned by Apex. Therefore, to refresh stale data, invoke the Apex method and then call notifyRecordUpdateAvailable(recordIds) to update the Lightning Data Service cache.
Call an Apex Method
Let’s look at the apexImperativeMethod component from the lwc-recipes repo that uses the same getContactList class as our previous examples. Instead of wiring it, when a user clicks a button, the component calls getContactList().
The imported function returns a promise. This code provides a one-time resolution given a set of parameters, whereas @wire(apexMethod) provides a stream of values and supports dynamic parameters.
1public with sharing class ContactController{2 @AuraEnabled(cacheable=true)3 public static List<Contact>getContactList(){4 return[5 SELECT Id, Name, Title, Phone, Email, Picture__c6 FROM Contact7 WHERE Picture__c != NULL8 WITH USER_MODE9 LIMIT 1010];11}12}
The template uses lwc:if to render the list of contacts. It also uses for:each to iterate over the contacts.
Pass parameters values to an Apex method in 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.
1public with sharing class ContactController{2 @AuraEnabled(cacheable=true)3 public static List<Contact>findContacts(String searchKey){4 String key = '%' + searchKey + '%';5 return[6 SELECT Id, Name, Title, Phone, Email, Picture__c7 FROM Contact8 WHERE Name LIKE :key AND Picture__c != NULL9 WITH USER_MODE10 LIMIT 1011];12}13}
When you pass values such as record data from LWC to Apex, use JavaScript objects or arrays. Map values are not serialized when passed to Apex methods.
Using maps isn’t supported for both imperative and wired Apex calls. The improper use of maps, such as with map[key] = val, allowed the data to be passed with Lightning Web Security (LWS) disabled. However, this usage no longer works when LWS is enabled. Furthermore, map.set(key, val) isn’t supported for passing values to Apex.
1//Don’t do this2Map map = new Map();3map.set(key, val);4apexMethod({map});
The Apex method calls the then or catch block depending if there’s an error. Calling the then and catch blocks happens asynchronously, so you can’t put the whole getContactList() method within a try-catch block.
In this case, the catch block handle errors from both the Apex method and the then block. If the getContactList() Apex method throws an exception, you can handle it in the catch block. If the Apex method runs successfully, your code in the then runs next. And if there’s an error in the then block, you can also handle it in the catch block. See Handle Errors from Apex.