I managed to get everything wired up leveraging lightning:empApi
and a Platform Event while moving away from the force:recordData
concept.
Below is a working example that anyone can leverage with some subtle changes.
Step 1 - Create Platform Event
- From Setup, search for
Platform Events
under Integrations
. For me the API name is Service_Appointment_Resource_Update__e
.
- Create a new Platform Event and any custom fields you may need.
Step 2 - Publish New Platform Event
This can be done from apex anonymous for testing purposes, but the following is an example in a trigger:
Map<Id, ServiceAppointment> serviceAppointmentMap = new Map<Id, ServiceAppointment>();
Map<Id, Service_Appointment_Resource_Update__e> notificationsToPublish = new Map<Id, Service_Appointment_Resource_Update__e>();
List<AssignedResource> assignedResources = Trigger.isInsert
? Trigger.New
: Trigger.Old;
for (AssignedResource res : assignedResources) {
serviceAppointmentMap.put(res.ServiceAppointmentId, null);
}
serviceAppointmentMap.remove(null);
for (ServiceAppointment sa : [SELECT Id, Has_Resource_Assigned__c FROM ServiceAppointment WHERE Id IN :serviceAppointmentMap.keySet()]) {
sa.Has_Resource_Assigned__c = Trigger.isInsert;//note: if this trigger becomes expanded upon this will require being more flushed out
notificationsToPublish.put(sa.Id, new Service_Appointment_Resource_Update__e(Message__c = 'Service Resource Changed', Is_Resource_Assigned__c = sa.Has_Resource_Assigned__c, Service_Appointment_Id__c = sa.Id));
serviceAppointmentMap.put(sa.Id, sa);
}
if (!serviceAppointmentMap.isEmpty()) {
update serviceAppointmentMap.values();
EventBus.publish(notificationsToPublish.values());//publish all events
}
Step 3 - Create Lightning Component to Subscribe to Platform Event
Component.cmp
<aura:component implements="force:hasRecordId,flexipage:availableForAllPageTypes" access="global">
<aura:attribute name="channel" type="String" default="/event/Service_Appointment_Resource_Update__e"/>
<aura:attribute name="subscription" type="Map"/>
<aura:attribute name="serviceAppointments" type="ServiceAppointment[]" />
<lightning:empApi aura:id="empApi"/>
<aura:handler name="init" action="{!c.doInit}" value="{!this}" />
<aura:component/>
ComponentController.js
doInit : function(component, event, helper) {
component.set('v.subscription', null);
// Get empApi component.
const empApi = component.find('empApi');
// Define an error handler function that prints the error to the console.
const errorHandler = function (message) {
console.error('Received error ', JSON.stringify(message));
};
// Register empApi error listener and pass in the error handler function.
empApi.onError($A.getCallback(errorHandler));
helper.subscribe(component, event, helper);
helper.handleInit(component, event, helper);
}
ComponentHelper.js
({
// Client-side function that invokes the subscribe method on the
// empApi component.
subscribe: function (component, event, helper) {
// Get the empApi component.
const empApi = component.find('empApi');
// Get the channel from the attribute.
const channel = component.get('v.channel');
// Subscription option to get only new events.
const replayId = -1;
// Callback function to be passed in the subscribe call.
// After an event is received, this callback prints the event
// payload to the console. A helper method displays the message
// in the console app.
const callback = function (message) {
console.log('Event Received : ' + JSON.stringify(message));
helper.onReceiveNotification(component, message);
};
// Subscribe to the channel and save the returned subscription object.
empApi.subscribe(channel, replayId, $A.getCallback(callback)).then($A.getCallback(function (newSubscription) {
console.log('Subscribed to channel ' + channel);
component.set('v.subscription', newSubscription);
}));
},
// Client-side function that invokes the unsubscribe method on the
// empApi component.
unsubscribe: function (component, event, helper) {
// Get the empApi component.
const empApi = component.find('empApi');
// Get the channel from the component attribute.
const channel = component.get('v.subscription').channel;
// Callback function to be passed in the unsubscribe call.
const callback = function (message) {
console.log('Unsubscribed from channel ' + message.channel);
};
// Unsubscribe from the channel using the subscription object.
empApi.unsubscribe(component.get('v.subscription'), $A.getCallback(callback));
},
// Client-side function that displays the platform event message
// in the console app and displays a toast if not muted.
onReceiveNotification: function (component, message) {
// Extract notification from platform event
const newNotification = {
time: $A.localizationService.formatDateTime(
message.data.payload.CreatedDate, 'HH:mm'),
message: message.data.payload.Message__c,
serviceAppointmentId: message.data.payload.Service_Appointment_Id__c,
isResourceAssigned: message.data.payload.Is_Resource_Assigned__c
};
let serviceAppointments = component.get("v.serviceAppointments");
serviceAppointments.forEach((sa) => {
if (newNotification.serviceAppointmentId == sa.Id) {
sa.Has_Resource_Assigned__c = newNotification.isResourceAssigned;
}
});
component.set("v.serviceAppointments", serviceAppointments);
}
})
This Trailhead was the walkthrough that finally got me on the right path. The Change Capture Data
methodology is not necessary. So everyone knows, the wait time is almost nonexistent and is far more lightweight and reliable than cometd
appeared to be.