Wire Decorators with Custom Methods: Talking to Salesforce "Through the Wire"

Introduction

In the Lightning Web Component framework, Salesforce introduced the concept of being able to obtain data from Salesforce through “Lightning Data Services”.  Among some prewired elements being provided, such as lightning-record-form, Salesforce introduced the concept of obtaining data from the platform through methods and properties in the javascript controller by decorating them with the @wire decorator. Salesforce littered their documentation with examples using some pre-formatted methods available from the ‘lightning/ui*Api’ modules to show us how this worked.  What many didn’t realize at the time, was that they would write their own methods to pass to the @wire decorator to run, and return their own sets of information. Today we are going to discuss why and how you can do this in your own Lightning Web Components.

Why Would I Want To Do This?

The reasons you would want to do this are numerous once you understand what you can do.  Currently, the methods that salesforce provides gives us object information, record data, and soon will give us list information and data back as well.  but what if you needed to, on page load for example, obtain something more complicated?  How about returning information in a custom, class-based object structure? Or perhaps you need to obtain multiple pieces of data in some sort of chain, with the latter information being dependent on the data returning from the first? This is where creating your own methods to call using the @wire decorator comes in handy.

How Can I Do This?

It all starts with the Apex Class. The Apex Class must be public or global, and the Method you wish to call from the Lightning Component must be public or global, static and annotated with the AuraEnabled(cacheable=true) annotation. We will not go into the details of the cacheable=true modifier, but please understand that by adding that modifier, your method should only retrieve and deliver information, not perform any DML statements to modify data. At this point, you can return whatever you would like in your method – lists, Sets, Maps, Custom SObjects, even instantiated objects from custom classes.  If you decide on instantiated objects from Custom Defined classes, please note that the properties that you wish to work with in the Object on the Javascript side should be exposed by annotating them with the @AuraEnabled annotation as well. As an example, we will return an instantiated object from a Custom Class.

Configuration Item (Will be returned to our Lightning Component)

public class ConfigItem {
	@AuraEnabled
	public String fooSetting { get; set; }
	@AuraEnabled
	public String barSetting { get; set; }
	public ConfigItem(CustomMetadataRecord__mdt recordPassedIn){
		this.fooSetting = recordPassedIn.Foo_Setting__c;
		this.barSetting = recordPassedIn.Bar_Setting__c;
	}
}

CustomLightningService (class that provides data for the Lightning Web Component To Use)

public class CustomLightningService {
	@AuraEnabled(cacheable=true)
	public static ConfigItem obtainCustomSettingsForComponent(String settingName) {
		//get the custom metadata
		CustomMetadataRecord__mdt rec = [SELECT Foo_Setting__c, Bar_Setting__c FROM
		CustomMetadataRecord__mdt WHERE MasterLabel = :settingName];
		//create the item we want to return
		ConfigItem itemToReturn = new ConfigItem(rec);
 
		return itemToReturn;
	}
}

MyLightningComponent –  Lightning Component where it is used

import { LightningElement, api, wire } from 'lwc';
import obtainCustomSetting from '@salesforce/apex/CustomLightningService.obtainCustomSettingsForComponent';
 
export default class MyLightningComponent Extends LightningElement{
	@api settingName;
	//the setting property is where our instantiated object returned from the
	//CustomLightningService.obtainCustomSettingsForComponent method lives.
	//specifically in the 'data' property.
	@wire(obtainCustomSetting,{'settingName':'$settingName'})
	setting;
 
	//we want to use the Foo_Setting__c data
	get foo(){
		return this.setting.data.fields.Foo_Setting__c.value;
	}
}

Its as simple as that!  Now the ‘Config’ item has been delivered to the Lightning component in the ‘setting.data’ property.

What if you needed to chain multiple calls?  How can you do this?  You could wire up another method from the CustomLightningService class that accepts the expected parameter, and when you use this method in your Lightning Component, and expect to pass in the value for the parameter from the other wire call, the second wire method you use will not be executed until the parameter for that method is populated.  The following shows this:

public class CustomLightningService {
@AuraEnabled(cacheable=true)
public static ConfigItem obtainCustomSettingsForComponent(String settingName) {
	//get the custom metadata
	CustomMetadataRecord__mdt rec = [SELECT Foo_Setting__c, Bar_Setting__c FROM
	CustomMetadataRecord__mdt WHERE MasterLabel = :settingName];
	//create the item we want to return
	ConfigItem itemToReturn = new ConfigItem(rec);
 
	return itemToReturn;
}
//new method that will return other information based on the Foo_Setting__c from the previous from the first @wire call
@AuraEnabled(cacheable=true)
public static List<Account> obtainAccountsBasedOnFoo(String fooSettingPassedIn){
	return [SELECT Id FROM Account WHERE Foo__c = :fooSettingPassedIn];
}
}

Update Lightning Component:

import { LightningElement, api, wire } from 'lwc';
import obtainCustomSetting from '@salesforce/apex/CustomLightningService.obtainCustomSettingsForComponent';
 
export default class MyLightningComponent Extends LightningElement{
	@api settingName;
	//the setting property is where our instantiated object returned from the
	//CustomLightningService.obtainCustomSettingsForComponent method lives.
	//specifically in the 'data' property.
	@wire(obtainCustomSetting,{'settingName':'$settingName'})
	setting;
	//this will not be called until setting property is populated from previous wire.
	@wire(obtainAccountsBasedOnFoo,{'fooSettingPassedIn':'$setting.data.Foo_Setting__c.value'})
	accounts;
 
	//we want to use the Foo_Setting__c data
	get foo(){
		return this.setting.data.fields.Foo_Setting__c.value;
	}
}

Wrapping Up – Things to Note

Just a couple of things to keep in mind with using @wire in your Lightning component:

  • What comes back from the data service is immutable.  This means that you can read it, but you cannot edit it.
  • If you absolutely need to modify the values of the object that comes back, you need to make a clone of it.
  • Editing the clone does not automatically update the data you have retrieved from Salesforce.
  • If you do update the data, this does not automatically update the object from the wire service, nor will it automatically “recall” the wire service.
  • The wire service information is “cached” and only updated based on the rules in the Lightning Framework.
  • If you know that the data in the object from the wire service is stale – you can request updated data using the refreshApex() method and passing in the property you wired up or the parameter to the function you wired up.

I hope this helps firm up your understanding of the @wire decorator and some potential uses.  May the @force be with you!