Workaround: Refresh Custom LWC on Change of Data in Standard Components or Backend Updates in Salesforce


 If you are looking for a solution to refresh your custom Lightning Web Component on change of data in one of the Standard Component or from Backend. This blog is for you!

To refresh the APEX in LWC we have refreshApex(), to refresh the standard components on change of custom LWC we have refreshViewAPIs

To refresh a Custom LWC whenever data is changing on Standard component or in backend unfortunately there are no OOTB way to achieve it. But we are here to create a workaround of it.

As a workaround we will use Platform Events to notify custom LWC that something has changed in data and it needs to refresh.

Please check steps below: 

Step 1: Create a Platform Event as shown below.



Step 2: You need to publish the platform event using an automation flow or trigger. In my scenario I have used triggers.

I have created OpportunityTrigger and published it on AfterUpdate event. As a best practice you can modify the code accordingly and publish the platform event on change of specific fields only!

OpportunityTrigger.trigger

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
trigger OpportunityTrigger on Opportunity (before insert, before update, before delete, after insert, after update, after delete, after undelete) {
    OpportunityTriggerHandler handler = new OpportunityTriggerHandler(Trigger.isExecuting, Trigger.size);
    switch on Trigger.operationType {
        when BEFORE_INSERT {
            // handler.beforeInsert(Trigger.new);
        } 
        when BEFORE_UPDATE {
            // handler.beforeUpdate(Trigger.old, Trigger.new, Trigger.oldMap, Trigger.newMap);
        }
        when BEFORE_DELETE {
            // handler.beforeDelete(Trigger.old, Trigger.oldMap);
        }
        when AFTER_INSERT {
            // handler.afterInsert(Trigger.new, Trigger.newMap);
        }
        when AFTER_UPDATE {
            handler.afterUpdate(Trigger.old, Trigger.new, Trigger.oldMap, Trigger.newMap);
        }
        when AFTER_DELETE {
            // handler.afterDelete(Trigger.old, Trigger.oldMap);
        } 
        when AFTER_UNDELETE {
            // handler.afterUndelete(Trigger.new, Trigger.newMap);
        }
    }
}


OpportunityTriggerHandler.cls

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
public without sharing class OpportunityTriggerHandler {
    private boolean triggerIsExecuting=false;
    private integer triggerSize=0;
    public OpportunityTriggerHelper helper;
    public OpportunityTriggerHandler(boolean triggerIsExecuting, integer triggerSize) {
        this.triggerIsExecuting = triggerIsExecuting;
        this.triggerSize = triggerSize;
        this.helper = new OpportunityTriggerHelper();
    }
    public void beforeInsert(List<Opportunity> newOpportunities) {
        // helper.doTask1();
        // helper.doTask2();
    }
    public void beforeUpdate(List<Opportunity> oldOpportunities, List<Opportunity> newOpportunities, Map<ID, SObject> oldOpportunitiesMap, Map<ID, SObject> newOpportunitiesMap) {
        // helper.doTask3();
        // helper.doTask4();
    }
    public void beforeDelete(List<Opportunity> oldOpportunities, Map<ID, SObject> oldOpportunityMap) {
        // helper.doTask5();
        // helper.doTask1();
    }
    public void afterInsert(List<Opportunity> newOpportunities, Map<ID, SObject> newOpportunityMap) {
        // helper.doTask2();
        // helper.doTask3();
    }
    public void afterUpdate(List<Opportunity> oldOpportunities, List<Opportunity> newOpportunities, Map<ID, SObject> oldOpportunityMap, Map<ID, SObject> newOpportunityMap) {
        helper.componentRefreshPE();
    }
    public void afterDelete(List<Opportunity> oldOpportunities, Map<ID, SObject> oldOpportunityMap) {
        // helper.doTask3();
        // helper.doTask1();
    }
    public void afterUndelete(List<Opportunity> newOpportunities, Map<ID, SObject> newOpportunityMap) {
        // helper.doTask4();
        // helper.doTask2();
    }
}


OpportunityTriggerHelper.cls

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
public with sharing class OpportunityTriggerHelper {
    public OpportunityTriggerHelper() {
        System.debug('Inside OpportunityTriggerHelper Constructor');
    }
    public void componentRefreshPE() {
        System.debug('componentRefreshPE');
        Refresh_Custom_Components__e refreshEvent = new Refresh_Custom_Components__e();
        EventBus.publish(refreshEvent);
    }
}


Step 3: Now you can subscribe to this Platform Event from your custom LWC and using refreshApex you can refresh the component whenever the event is bubbling up as shown below!

OpportunityOverAmountApex.cls

1
2
3
4
5
6
public with sharing class OpptiesOverAmountApex {
    @AuraEnabled(cacheable=true)
    public static List<Opportunity> getOpptyOverAmount(Id recordId) {
        return [SELECT Id, Name, Amount, StageName, CloseDate FROM Opportunity WHERE AccountId=:recordId];
    }
}


OpportunitiesOverAmount.html

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
<template>
  <!-- Display a list of opportunities -->
<lightning-card title="List of related opportunities" icon-name="utility:user">
    <div class="slds-var-m-around_medium">
    <p>
        <lightning-formatted-number
        value={amount}
        format-style="currency"
        ></lightning-formatted-number>
    </p>
    <template lwc:if={getOppty.data}>
        <template for:each={getOppty.data} for:item="oppty" for:index="idx">
        <div key={oppty.Id} class="slds-m-bottom_medium">
            <p>{idx}. {oppty.Name}</p>
            <p>
            <lightning-formatted-number
                value={oppty.Amount}
                format-style="currency"
            ></lightning-formatted-number>
            </p>
            <p>
            <lightning-formatted-date-time value={oppty.CloseDate}></lightning-formatted-date-time>
            </p>
            <p><lightning-badge label={oppty.StageName}></lightning-badge></p>
        </div>
        </template>
    </template>
    </div>
</lightning-card>
</template>


OpportunitiesOverAmount.js

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
// opportunitiesOverAmount.js
import { LightningElement, api, wire } from "lwc";
import { refreshApex } from "@salesforce/apex";
import getOppty from "@salesforce/apex/OpptiesOverAmountApex.getOpptyOverAmount";
import {
    subscribe,
    unsubscribe,
    onError,
    setDebugFlag,
    isEmpEnabled,
} from 'lightning/empApi';



export default class OpportunitiesOverAmount extends LightningElement {
  @api recordId;
  @api channelName = '/event/Refresh_Custom_Components__e';
  @wire(getOppty, { recordId: "$recordId" })
  getOppty;

    connectedCallback() {  
        const self = this;
        const callbackFunction = function (response) {
            console.log("🚀 ~ callbackFunction:"+JSON.stringify(response));
            self.refreshMyData();
        }
        subscribe(this.channelName, -1, callbackFunction).then(response => {
            console.log("🚀 ~ subscribe:"+JSON.stringify(response));
        });

    }
    refreshMyData() { 
        refreshApex(this.getOppty).then(() => {
            console.log('###Refreshing Data');    
        });
    }
}


OpportunitiesOverAmount.js-meta.xml

1
2
3
4
5
6
7
8
<?xml version="1.0" encoding="UTF-8"?>
<LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata">
    <apiVersion>59.0</apiVersion>
    <isExposed>true</isExposed>
    <targets>
        <target>lightning__RecordPage</target>
    </targets>
</LightningComponentBundle>


Output


Checkout Complete Video Below

 If you have any question please leave a comment below.

If you would like to add something to this post please leave a comment below.
Share this blog with your friends if you find it helpful somehow !

Thanks
Happy Coding :)

Post a Comment

0 Comments