thumbnail

Hot Reloading Salesforce Records with Change Data Capture and Lightning Web Components

Marc Swan Marc Swan | 7 min read
3 months ago

Have you ever been working in Salesforce, only to realize that the record you’re looking at has been updated by someone else, but you had no idea until you refreshed the page? Frustrating, right? Wouldn’t it be great if records could just update themselves in real-time, without any manual refreshes? Well, good news — they can! In this post, we’ll explore how to use Salesforce’s Change Data Capture events and Lightning Web Components (LWCs) to create a hot-reloading experience for your records.

Why Hot Reloading Matters

In a dynamic business environment, data changes rapidly. Sales reps update opportunities, support agents close cases, and records evolve continuously. Relying on manual page refreshes to see the latest data isn’t just inconvenient; it can lead to outdated information and misinformed decisions. By implementing real-time data updates, users can stay in sync effortlessly.

Understanding Change Data Capture

Change Data Capture (CDC) is a Salesforce feature that publishes change events, which represent changes to Salesforce records. These events are broadcasted over a messaging channel that subscribers can listen to. When a record is created, updated, deleted, or undeleted, a change event is published. This makes CDC perfect for keeping components in sync with the latest data.

Benefits of Using Change Data Capture

  • Real-Time Updates: Receive notifications instantly when data changes.
  • Efficiency: Reduce the need for manual refreshes or polling for changes.
  • Scalability: Handle a high volume of changes without performance hits.

Setting Up Change Data Capture for Your Object

Before we dive into the code, let’s set up CDC for the object you want to monitor.

Step 1: Enable Change Data Capture

  1. Navigate to Setup: In Salesforce, click the gear icon and select Setup.
  2. Find Change Data Capture: In the Quick Find box, type “Change Data Capture”.
  3. Select Objects: Check the boxes next to the objects you want to monitor. For example, if you’re interested in the Account object, select it.
  4. Save: Click Save to enable CDC for those objects.

That’s it! You’ve now set up a messaging channel for your object. Salesforce will start publishing change events for it.

Object Limitations and Considerations

Not all Salesforce objects support Change Data Capture. It’s important to know which objects are eligible so you don’t run into unexpected issues.

Finding Supported Objects

To find out which objects support Change Data Capture:

  1. Salesforce Documentation: Refer to the Change Data Capture Supported Objects in the Salesforce Developer Guide.
  2. In Setup: When you navigate to Setup > Change Data Capture, Salesforce displays a list of available objects for CDC. If an object isn’t listed, it’s not supported.

Examples of Supported Objects

  • Standard Objects:
  • Account
  • Contact
  • Opportunity
  • Case
  • Lead


  • Custom Objects:
  • Any custom object you have created in your org (e.g., Invoice__c, Project__c)


Examples of Unsupported Objects

  • Activities:
  • Task
  • Event


  • Setup and Configuration Objects:
  • User
  • Profile
  • PermissionSet


  • Some Standard Objects:
  • Attachment
  • ContentDocument


Considerations

  • Limited Support for Certain Objects: Some objects may partially support CDC, meaning only certain fields or events trigger change events.
  • High-Volume Objects: For objects with a high volume of changes, consider the potential performance impact and governor limits.

Building the Lightning Web Component

Now, let’s create an LWC that listens to these change events and updates the record in real-time.

Component Overview

Our component, PageListener, will:

  • Subscribe to the change event channel for the specified object.
  • Listen for events related to the current record.
  • Refresh the record view when a change is detected.

The Code Breakdown

Below is the complete code for the PageListener component.

JavaScript Controller: pageListener.js

import { api, LightningElement, wire, track } from 'lwc';
import { getRecord, getRecordNotifyChange } from 'lightning/uiRecordApi';
import { subscribe, unsubscribe, onError } from 'lightning/empApi';
import { ShowToastEvent } from 'lightning/platformShowToastEvent';

export default class PageListener extends LightningElement {
 @api recordId;
 @api objectApiName;

 @wire(getRecord, { recordId: '$recordId', layoutTypes: 'Full' }) record;

 subscription = {}; // holds subscription, used for unsubscribe

 connectedCallback() {
 console.log('Connecting to :', this.objectApiName + 'ChangeEvent');
 console.log('Connected to change event: ', this.channelName);
    this.registerErrorListener();
    this.registerSubscribe();
  }

  disconnectedCallback() {
    unsubscribe(this.subscription, () => console.log('Unsubscribed to change events.'));
  }

  // Called by connectedCallback()
  registerErrorListener() {
    onError(error => {
      console.error('Salesforce error', JSON.stringify(error));
    });
  }

  // Called by connectedCallback()
  registerSubscribe() {
    const changeEventCallback = changeEvent => {
      this.processChangeEvent(changeEvent);
    };

    // Sets up subscription and callback for change events
    subscribe('/data/' + this.objectApiName + 'ChangeEvent', -1, changeEventCallback).then(subscription => {
      this.subscription = subscription;
    });
  }

  // Called by registerSubscribe()
  processChangeEvent(changeEvent) {
    try {
        const recordIds = changeEvent.data.payload.ChangeEventHeader.recordIds; // Avoid destructuring
        if (recordIds.includes(this.recordId)) {
            // Refresh all components related to this record
            getRecordNotifyChange([{ recordId: this.recordId }]);
  }
 } catch (err) {
   console.error(err);
   // Optionally, dispatch an error toast
   this.dispatchEvent(
    new ShowToastEvent({
     title: 'Error',
     message: 'An error occurred while processing the record update.',
     variant: 'error',
     mode: 'dismissable'
    })
   );
  }
 }
}

HTML Template: pageListener.html

<template>
  <!-- This component doesn't render anything -->
</template>

Meta Configuration: pageListener.js-meta.xml

<?xml version="1.0" encoding="UTF-8" ?>
<LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata">
  <apiVersion>61.0</apiVersion>
  <isExposed>true</isExposed>
  <masterLabel>Page Listener</masterLabel>
  <description>Listens to changes for a record using Change Data Capture</description>
  <targets>
    <target>lightning__RecordPage</target>
  </targets>
  <targetConfigs>
    <targetConfig targets="lightning__RecordPage">
      <property
        name="objectApiName"
        type="String"
        label="Object API Name"
        description="API name of the object to listen for changes"
      />
    </targetConfig>
  </targetConfigs>
</LightningComponentBundle>

Key Parts Explained

  • @api recordId and @api objectApiName: These properties allow the component to be aware of the current record and object.
  • connectedCallback and disconnectedCallback: Lifecycle hooks used to subscribe and unsubscribe from the change event channel.
  • subscribe and unsubscribe: Methods from lightning/empApi used to handle event subscriptions.
  • proccessChangeEvent: The function that processes incoming change events and refreshes the record if necessary.
  • getRecordNotifyChange: This method notifies all Lightning Data Service components to refresh their cache for the specified record.

How Real-Time Updates Work

Let’s walk through how the component ensures that users see the latest data without interfering with their current actions.

Subscribing to Change Events

When the component is initialized (connectedCallback), it subscribes to the /data/ObjectNameChangeEvent channel. This channel broadcasts any changes to records of that object type.

Processing Change Events

When a change event occurs, the handleChangeEvent method checks if the changed record's ID matches the one currently being viewed. If it does, it calls getRecordNotifyChange, which tells all components using Lightning Data Service to refresh their data for that record.

Safe Updates During Edits

One of the concerns with real-time updates is what happens if a user is in the middle of editing a record. The good news is that getRecordNotifyChange is smart. It refreshes the data cache but doesn't overwrite unsaved changes in an edit form. This means:

  • Viewing User: Sees the updated data almost instantly.
  • Editing User: Continues editing without disruption. Once they save, they’ll be working with the latest data.

Ensuring Data Consistency

By using getRecordNotifyChange, we ensure that all components tied to the record are updated. This includes:

  • Record details
  • Related lists
  • Custom components using @wire(getRecord)

Adding the Component to a Record Page

To make this work, you’ll need to add the PageListener component to your record page.

  1. Navigate to a Record Page: Go to any record of the object you set up.
  2. Edit Page: Click the gear icon and select Edit Page.
  3. Drag and Drop the Component: Find your PageListener component in the list and drag it onto the page. Since it doesn't render anything, placement isn't critical.
  4. Set the Object API Name: In the component’s properties pane, set the Object API Name to match your object (e.g., Account).
  5. Save and Activate: Save your changes and activate the page if necessary.

Now, your component is live and listening for changes!

Testing the Real-Time Updates

Let’s make sure everything works as expected.

  1. Open Two Browser Windows: Log into Salesforce in two separate windows or browsers.
  2. View the Same Record: In both windows, navigate to the same record.
  3. Edit and Save in One Window: Make a change to the record in one window and save it.
  4. Watch the Other Window: In the other window, observe that the record details update automatically without a refresh.

Wrapping Up

By leveraging Change Data Capture and Lightning Web Components, we’ve created a seamless, real-time experience for Salesforce users. No more manual refreshes or outdated information — just instant updates as they happen.


Discussions

Login to Post Comments
No Comments Posted