2

I have an LWC invoked via quick action that creates child record(s) using a lightning-record-edit-form. The lightning page has two tabs: Details and Related. On the Related tab, we have a "Dynamic Related List - Single" component showing the child records.

The issue I'm facing is the related list does not refresh when the quick action is closed after creating child records. My issue is very similar to this post, but the solution is not working for me.

Here's my onsuccess method that runs after the lightning-record-edit-form successfully creates the child records:

import { RefreshEvent } from 'lightning/refresh';

....

handleSuccess() {
    this.submittingRecord = false;

    if(this.saveAndNew) {
        // clear the form
        this.handleReset();
        this.throwToastMessage('info', 'Select a Venue', 'Use the dropdown on the right-hand side');
    } else {
        // this.dispatchEvent(new RefreshEvent());
        this.dispatchEvent(new CloseActionScreenEvent());
        this.dispatchEvent(new RefreshEvent());
    }

    // let user know the record was created
    this.throwToastMessage('success', 'Successfully Added Venue to List', 'Refresh list view to view new record(s)');
}

Any ideas what I'm missing? The detail page seems to refresh automatically. I have multiple rollup fields in the detail section that immediately update after closing the quick action - without the need for RefreshEvent().

9
  • Maybe you should state you don't want to use the rather ugly eval("$A.get('e.force:refreshView').fire();");, which is likely to work?! Oct 26, 2023 at 21:06
  • Is this in lightning console tabs? Oct 27, 2023 at 4:40
  • @NagendraSingh, the eval will work inside of your regular Javascript. Oct 27, 2023 at 6:17
  • @FelixvanHove Yeah it might work, I was just checking with the author, if his application is a lightning console tab? Oct 27, 2023 at 6:25
  • 1
    I had a similar issue I used a workaround, I updated the record with the updateRecord function from uiRecordApi and only passed the Id field to it. Nov 2, 2023 at 11:49

6 Answers 6

1

Any ideas what I'm missing?

From the documentation of the RefreshView API it appears to me that it should be supported for QuickActions on record pages. I don't think you are missing anything.

Here's a workaround that works for Lightning Locker and LWS, regular apps and Lightning Console apps:

  • Create a "Change Data Capture" event notification for your child SObject.

  • Create a LWC that subscribes to this event and fires the RefreshEvent.

  • Put this LWC in your page at a place above or below your tabs (but not inside any of your tabs).

The LWC should look like this:

import { LightningElement } from 'lwc';
import { subscribe} from 'lightning/empApi';
import { RefreshEvent } from "lightning/refresh";

export default class SubscribeToRefresh extends LightningElement {
    // TODO: adjust
    channelName = '/data/Child__ChangeEvent';

    subscription = {};

    connectedCallback() {

        const self = this;
        const callback = function(response) {
            self.dispatchEvent(new RefreshEvent());
        }

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

I've done some preliminary testing and apparently the "Dynamic Related List - Single" does listen to RefreshEvents. RefreshEvents just don't reach it coming from the QuickAction.

However, the use of "Change Data Capture" events comes with a number of limitations. E.g. if the Streaming Api is used for other purposes, or batch processes make for a large number of inserts, this might not be for you. In the end, it is just a bypass of a lack of up-to-dateness of displayed data.

4
  • Just FYI : This solution comes with a limitation as it consumes limit of Streaming API for each user who has logged into the application. This would consume "Maximum number of clients" , "Maximum number of concurrent clients", "Maximum number of events per day". So use this wisely. Oct 30, 2023 at 3:45
  • 1
    @NagendraSingh, I've added a paragraph clarifying the limitations of this approach. Oct 30, 2023 at 8:58
  • This worked like a charm, thank you. I made one slight modification - instead of using CDC, I used a Lightning Message Channel to alert the "Refresh" component of the update. Nov 10, 2023 at 21:47
  • @CaseyGehling, this is an important and excellent modification! You can create your own answer and mark this instead as solved. If you don't, alternatively, I add an additional section "Update" to my answer to explain the "Message Channel" approach. Nov 12, 2023 at 13:11
1

If nothing works here and you cannot use donot / cant use beta feature of lightning tab refresh.

Then try this. This is an internal api event of refresh tab event which basically triggers a tag refresh as you would do in Primary and Secondary tabs.

refreshTab() {
    this.invokeWorkspaceAPI('isConsoleNavigation').then(isConsole => {
        if (isConsole) {
            this.invokeWorkspaceAPI('getFocusedTabInfo').then(focusedTab => {
                this.invokeWorkspaceAPI('refreshTab', {
                    tabId: focusedTab.tabId
                }).then(response => {
                    this.dispatchEvent(new CloseActionScreenEvent());
                })
            });
        }
    });
}

invokeWorkspaceAPI(methodName, methodArgs) {
    return new Promise((resolve, reject) => {
        const apiEvent = new CustomEvent("internalapievent", {
            bubbles: true,
            composed: true,
            cancelable: false,
            detail: {
                category: "workspaceAPI",
                methodName: methodName,
                methodArgs: methodArgs,
                callback: (err, response) => {
                    if (err) {
                        return reject(err);
                    } else {
                        
                        return resolve(response);
                    }
                }
            }
        });
        this.dispatchEvent(apiEvent);
        window.dispatchEvent(apiEvent);
    });
}

UPDATE

I tested this and this perfectly well with quick action + console application.

html : Just to test workspace api

<!-- Update Related Lost -->
<template>
    <lightning-button class="slds-m-around_large" onclick={refreshTab} title="Refresh Tab" label="Refresh Tab"></lightning-button>
</template>

JS :

/**
 * Created by nagendrakumar.singh on 30/10/23.
 */

import {LightningElement} from 'lwc';
import {CloseActionScreenEvent} from "lightning/actions";

export default class UpdateRelatedLost extends LightningElement {

    refreshTab() {
        this.invokeWorkspaceAPI('isConsoleNavigation').then(isConsole => {
            console.log('isConsole -> '+isConsole);
            if (isConsole) {
                this.invokeWorkspaceAPI('getFocusedTabInfo').then(focusedTab => {
                    console.log('focusedTab -> '+focusedTab.tabId);
                    this.invokeWorkspaceAPI('refreshTab', {
                        tabId: focusedTab.tabId
                    }).then(response => {
                        this.dispatchEvent(new CloseActionScreenEvent());
                    })
                });
            }
        });
    }

    invokeWorkspaceAPI(methodName, methodArgs) {
        return new Promise((resolve, reject) => {
            const apiEvent = new CustomEvent("internalapievent", {
                bubbles: true,
                composed: true,
                cancelable: false,
                detail: {
                    category: "workspaceAPI",
                    methodName: methodName,
                    methodArgs: methodArgs,
                    callback: (err, response) => {
                        if (err) {
                            return reject(err);
                        } else {

                            return resolve(response);
                        }
                    }
                }
            });
            this.dispatchEvent(apiEvent);
            window.dispatchEvent(apiEvent);
        });
    }
}

meta xml :

<?xml version="1.0" encoding="UTF-8"?>
<LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata">
    <apiVersion>59.0</apiVersion>
    <description>Update Related Lost</description>
    <isExposed>true</isExposed>
    <masterLabel>Update Related Lost</masterLabel>
    <targets>
        <target>lightning__RecordAction</target>
    </targets>
    <targetConfigs>
        <targetConfig targets="lightning__RecordAction">
            <actionType>ScreenAction</actionType>
        </targetConfig>
    </targetConfigs>
</LightningComponentBundle>

I see all console logs properly printing

enter image description here

And also related list being refreshed and fetching data.

14
  • I guess you suggest to call refreshTab() each time the QuickAction is closed?! This seems to work sometimes. According to what I can see: If you reach the detail page, do your QuickAction thingy and navigate to the tab: fine, you see the new entry. But click on "Details", add another one by QuickAction, navigate again to the tab: the last addition is not there. In fact the above mechanism stops working. And what about the user reaching the page, clicking on "Related", doing the QuickAction thing - is the code still meant to work?! Oct 27, 2023 at 10:03
  • you need to call CloseActionScreenEvent after refresh has happened. I updated code. Oct 27, 2023 at 10:31
  • Your change means the QuickAction modal is not even closed. isConsole remains false, of course, and getFocusedTabInfo is not supported in the most common Detail page, according to my tests. Is the latter not for Lightning Console environments?! Oct 27, 2023 at 11:19
  • 1
    I've upvoted this answer, because I've tested this and its latest code works - in a Lightning Console. This latter fact should be stated clearly. I'm also wondering to what degree Salesforce commits to support such APIs, when they are posted by Salesforce employees as answers here. But maybe I'm unfair: The answer admits right in the start that this is yet another workaround... Oct 31, 2023 at 11:47
  • 2
    Just FYI : My responses or comments here should not be considered as an official statement of my employer unless explicitly mentioned. Oct 31, 2023 at 12:11
1

I took @Felix's idea and made a slight modification: instead of subscribing to CDC, I created a custom message channel and published a message from the quick action when applicable. The refresh component embedded on the lightning record page looks like this:

import { LightningElement, wire, api } from 'lwc';
import { RefreshEvent } from "lightning/refresh";
import {
    subscribe,
    unsubscribe,
    APPLICATION_SCOPE,
    MessageContext,
} from 'lightning/messageService';
import refreshChannel from '@salesforce/messageChannel/Refresh__c';

export default class RefreshCoordinator extends LightningElement {
    @api recordId;

    // LMS config
    subscription = null;
    @wire(MessageContext)
    messageContext;

    connectedCallback() {
        this.subscribeToMessageChannel();
    }

    disconnectedCallback() {
        this.unsubscribeToMessageChannel();
    }

    subscribeToMessageChannel() {
        if (!this.subscription) {
            this.subscription = subscribe(
                this.messageContext,
                refreshChannel,
                (message) => this.handleMessage(message),
                { scope: APPLICATION_SCOPE }
            );
        }
    }

    handleMessage(message) {
        if(message.recordId == this.recordId) {
            this.dispatchEvent(new RefreshEvent());
        }
    }

    unsubscribeToMessageChannel() {
        unsubscribe(this.subscription);
        this.subscription = null;
    }
}
0

Might be a stretch at this point, but did you try issuing the "close" event after the refresh one? Events are handled synchronously, so what I think first here is that the component might be disconnected/disconnecting and not responding to events after the "close" event is fired.

So I would just order it in a different way:

handleSuccess() {
    this.dispatchEvent(new RefreshEvent());
    this.throwToastMessage('success', 'Successfully Added Venue to List', 'Refresh list view to view new record(s)');
    this.dispatchEvent(new CloseActionScreenEvent());
}
0

Create a record trigger flow on your main object where your LWC component is sitting.

Create a new field in the main object, FlowTrigger__c (just an example). Now when, you are updating your record-edit-form add another logic to check this box. (or you can just show this box and leave it checked and hide it)

Your record trigger flow, will start/follow decision, if this field is checked. Get all the records from your "Dynamic Related List - Single" and update them without changing anything (PLEASE SEE EDIT SECTION BELOW FOR THIS METHOD).

Or

Use the update related record, and choose the object that you used in your "Dynamic Related List - Single".

This will trigger the Dynamic Related List - Single to refresh once you have saved it.

Of course, once this is run you can add another flow element to uncheck the custom field again, so that next time when the user updates again using the LWC. The flow will be triggered again.

Edit: After reading @Rahul Gawale's comment on the original post, I realised, which I also tried yesterday, you don't have to get all the related records. Just get one (let flow get only one record) and update the record, without changing anything.

-1

Use the notifyRecordUpdateAvailable method from the lightning/uiRecordApi module.

This method can inform Lightning Data Service that record data has changed so that Lightning Data Service can take the appropriate actions to keep wire adapters updated with the latest record data.

Call this function to notify Lightning Data Service that a record has changed outside its mechanisms, such as via imperative Apex or by calling User Interface API via a third-party framework.

Syntax:

import { notifyRecordUpdateAvailable } from 'lightning/uiRecordApi';

notifyRecordUpdateAvailable(items: Array<{recordId: string}>)

Example:

import { LightningElement, wire } from 'lwc';
import { getRecord, notifyRecordUpdateAvailable } from 'lightning/uiRecordApi';
import apexUpdateRecord from '@salesforce/apex/Controller.apexUpdateRecord';

export default class Example extends LightningElement {
  @api recordId;

  // Wire a record
  @wire(getRecord, { recordId: '$recordId', fields: ... })
  record;

  async handler() {
    // Do something before the record is updated
    showSpinner();

    // Update the record via Apex
    await apexUpdateRecord(this.recordId);

    // Notify LDS that you've changed the record outside its mechanisms
    // Await the Promise object returned by notifyRecordUpdateAvailable()
    await notifyRecordUpdateAvailable([{recordId: this.recordId}]);
    hideSpinner();
  }
}

For more information refer to this documentation.

4

You must log in to answer this question.

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