Emp API use case – track asynchronous operations

The Emp API module enables lightning web components to listen to event messages. This blog post covers a use case where event message streaming is used to track the status of an asynchronous operation.

Scenario: An account creation trigger is getting and attaching files to the new account. This runs in a future method. Depending on how long this asynchronous method takes to complete, a user has to repeatedly refresh the account page before they can see the related files.

By using Emp API (documentation) to subscribe to a streaming channel for account change events, we can instead provide visual feedback to the user while the operation is running and when it finished. This example uses a Lightning web component on the account record page.

Custom Field

First, create a custom Boolean field on the account object (Files_Attached__c) that is unchecked by default. The future method that attaches the files also checks this field.

@future
private static void attachFiles(Id accountId){
    // ...
    // some logic that gets and attaches files to the account
    // ...

    // after attaching the files, set Files_Attached__c = true
    try{
        Account a = new Account (Id = accountId, Files_Attached__c = true);
        update a;
    } catch (Exception e){
        System.debug('Account Update Error: ' + e.getMessage());
        // more error handling
    }
}

Change Data Capture

As a pre-requisite, you will have to enable change data capture for the Account object in Setup> Integrations > Change Data Capture.

Lightning Web Component

The Lightning web component HTML is super simple: While our variable isOperationComplete is false, show “Attaching files”. Once isOperationComplete is true, show “Files Attached”. I also use custom css classes to display the messages in different color schemes.

<template>
    <template if:false={isOperationComplete}>
        <div class="slds-box message not-ready">
            Attaching files ...
        </div>
    </template>
    <template if:true={isOperationComplete}>
        <div class="slds-box message ready">
            Files Attached
        </div>
    </template>
</template>
div.message {
    position: relative;
    padding: 1px;
    padding-left: 5px;
    margin: 5px 5px;
    box-shadow: 0 2px 5px rgba(0, 0, 0, .3);
    background: #BBB;
    color: #FFF;
}

div.message.ready {
    color: white;
    background: green;
}
div.message.not-ready {
    color: white;
    background: orange;
}

The Lightning web component javascript imports the lightning/empApi library and subscribes to the AccountChangeEvent channel in the connectedCallback(). The subscribe callback function runs each time the account is updated. When the field Files_Attached__c is updated, we use refreshApex to re-query the wired account and refresh the component.

TL;DR: When the future method completes and sets Files_Attached__c to true, the component listens to this update and refreshes to show the “Files Attached” message.

import { LightningElement, wire, api, track } from 'lwc';
import { getRecord, getFieldValue } from 'lightning/uiRecordApi';
import { refreshApex } from '@salesforce/apex';
import {subscribe,unsubscribe,onError,setDebugFlag,isEmpEnabled,} from 'lightning/empApi';

import OperationComplete from '@salesforce/schema/Contract.Files_Attached__c';

const fields = [OperationComplete];


export default class AccountFilesReady extends LightningElement {
    @api recordId;
    channelName = '/data/AccountChangeEvent';
    subscription = {};


    @wire(getRecord, {recordId: '$recordId', fields})
    account;

    get isOperationComplete(){
        return getFieldValue(this.account.data, OperationComplete);
    }

    connectedCallback() {
        // subscribing to account change events 
        subscribe(this.channelName, -1, (response) => {
            // uncomment below to see the response sent for each change event
            // console.log(JSON.stringify(response));

            // if the files_attached__c field was changed, requery it => when it's updated to true, it will update the message in the lwc html
            if(response.data.payload.ChangeEventHeader.changedFields.includes('Files_Attached__c')){
                refreshApex(this.account);
            }
            // we don't need to keep listening to account change events after files were attached => unsubscribe once these conditions are met
            if(this.isOperationComplete && this.subscription){
                unsubscribe(this.subscription, (response) => {
                    console.log('unsubscribe() response: ', JSON.stringify(response));
                    // Response is true for successful unsubscribe
                });
            }
        })
        .then(response => {
            this.subscription = response;
        });
        
    }
}

Since we don’t need to keep listening to any future updates, we can unsubscribe from the channel once isOperationComplete is true.

Want to read more blog entries about Salesforce development? Follow me.