1

Developing a component that displays a visualization based on the value of a field, Opportunity.Magic_Number__c. That field is updated periodically via Salesforce REST API calls outside of our solution - never by humans. If someone has the Opportunity Lightning Record Page open, we want the component to re-render the visualization if Opportunity.Magic_Number__c changes.

Right now, it renders correctly but does not update unless you reload the page (or make some change to the Opportunity).

Here's a simplified example...

<template>
  <div class="amazing-visualization">{magicNumber}</div>
</template>
import { api, LightningElement, wire } from 'lwc';
import { getRecord, getFieldValue } from 'lightning/uiRecordApi';
import MAGIC_NUMBER_FIELD from '@salesforce/schema/Opportunity.Magic_Number__c';

export default class MagicNumber extends LightningElement {
  @api recordId;

  @wire(getRecord, { recordId: '$recordId',[MAGIC_NUMBER_FIELD] }) opportunity;

  get magicNumber() {
    return getFieldValue(this.opportunity.data, MAGIC_NUMBER_FIELD) || 42;
  }
}

Looking at the example above, can anyone make a recommendation regarding implementing CDC in an LWC component like this one so the value on the template is updated immediately if the underlying field is changed?

3 Answers 3

4

CDC is the right way to go. Here is the example above using it...

Required configuration for org:

  1. Go to Setup and type Change Data Capture in Quick Find then select it
  2. Type Opportunity in Type to filter list...
  3. Select Opportunity then click the Add (right-arrow) button
  4. Click Save

Here are the LWC component elements...

<?xml version="1.0" encoding="UTF-8" ?>
<LightningComponentBundle
  xmlns="http://soap.sforce.com/2006/04/metadata"
  fqn="scores"
>
  <apiVersion>56.0</apiVersion>
  <isExposed>true</isExposed>
  <masterLabel>Magic Number</masterLabel>
  <description>Whatever you want</description>
  <targets>
    <target>lightning__RecordPage</target>
  </targets>
  <targetConfigs>
    <targetConfig targets="lightning__RecordPage">
        <objects>
            <object>Opportunity</object>
        </objects>
    </targetConfig>
  </targetConfigs>
</LightningComponentBundle>
<template>
  <div class="amazing-visualization">{magicNumber}</div>
</template>
import { api, LightningElement, wire } from 'lwc';
import { getRecord, getFieldValue } from 'lightning/uiRecordApi';
import { getRecord, getFieldValue, getRecordNotifyChange } from 'lightning/uiRecordApi';
import { subscribe, unsubscribe, onError } from 'lightning/empApi';
import MAGIC_NUMBER_FIELD from '@salesforce/schema/Opportunity.Magic_Number__c';

export default class MagicNumber extends LightningElement {
  @api recordId;

  @wire(getRecord, { recordId: '$recordId',[MAGIC_NUMBER_FIELD] }) opportunity;

  channelName = '/data/OpportunityChangeEvent';
  subscription = {}; // holds subscription, used for unsubscribe

  connectedCallback() {
    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(this.channelName, -1, changeEventCallback).then(subscription => {
      this.subscription = subscription;
    });
  }

  // Called by registerSubscribe()
  processChangeEvent(changeEvent) {
    try {
      const recordIds = changeEvent.data.payload.ChangeEventHeader.recordIds; // avoid deconstruction
      if (recordIds.includes(this.recordId)) getRecordNotifyChange([{ recordId: this.recordId }]); // Refresh all components
    } catch (err) {
      console.error(err);
    }
  }

  get magicNumber() {
    return getFieldValue(this.opportunity.data, MAGIC_NUMBER_FIELD) || 42;
  }
}
0

Yes you have to use either CDC/Platform Events to publish the event when there is an update to the backend Data.

Then you have to subscribe this streaming channel in LWC with the help of lightning/empApi module to listen to the event messages.

Please find the below blog post describing in detail. https://salesforcediaries.com/2023/01/18/change-data-capture-automate-with-lwc/

0

pushtopic will be the best approach for your problem

You just need to simply create a pushtopic channel with your required field in query & subcribe it in lwc component

for your requirement, run this apex code to create pushtopic channel

pushTopic.Name = 'OppMagicNumberChannel';
pushTopic.Query = 'SELECT Id, Magic_Number__c FROM Opportunity'; //fields to track
pushTopic.ApiVersion = 45.0;
pushTopic.NotifyForOperationCreate = true;
pushTopic.NotifyForOperationUpdate = true;
pushTopic.NotifyForOperationUndelete = true;
pushTopic.NotifyForOperationDelete = true;
insert pushTopic;

& use it into lwc component like this

import {api, LightningElement, wire} from 'lwc';
import {subscribe, unsubscribe} from 'lightning/empApi';

export default class MagicNumber extends LightningElement {
  @api recordId;
  MagicNumber;

  //Subscribing to the pushTopic
  channelName = '/topic/OppMagicNumberChannel';
  subscription = {};

  connectedCallback () {
    this.handleSubscribe ();
    this.fetchdata ();
  }

  handleSubscribe () {
    let self = this; // just to avoid context loosing issue

    const messageCallback = function (response) {
      self.fetchdata ();
    };

    subscribe (this.channelName, -1, messageCallback).then (response => {
      this.subscription = response;
    });
  }

  fetchdata(){
    //make your imparative apex call to get field data & update 'MagicNumber'-that can be used in html , this method will be called automatically once your magic_number__c changes
  }
}

Only change you'll have to do is write a apex controller method so that it can be called imparatively in new message callback

hope it'll help you better

1
  • 1
    Thanks for contributing. Was looking at salesforce.stackexchange.com/questions/368001/… and it seems like CDC is Salesforce's next generation and might be sunsetting pushtopic. That said, can the Apex code you provided above be included in a Managed Package's PostInstall? If so, that would have an advantage over CDC because it requires a System Administrator to perform additional steps after installation. Commented Feb 11, 2023 at 14:37

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .