How to Refresh Lightning Datatable after triggering standard events(new/edit) in LWC

Use Case: 

As a general use case, we have to show a custom related list which shows all related child records to a parent record. We, in general, implement Lightning-datatable to achieve this functionality. Whilst lightning-datatable is responsible for rendering all related child records of a parent record, we have to also provide an action New button to create a new child record with parent field auto-populated with the parent record id on child new record form. 

Let's consider an example to understand the problem. We have an Account object which is also at the parent side of related Opportunities and an Opportunity object which has lookup relationship with Account. In this way, we have 1: N relationship between Account and Opportunity
Now the requirement is to show all related opportunities of a given account record in a custom related list having row action as Edit. Along with this, we also need to have a New button which will allow us to create a new opportunity on any given account. 

So now we have two requirements:
  1. New action: when we try to create a record with NavigationMixin Create event, we are redirected to record detail page of opportunity. But what if the need is to stay on the same page(Account Record in our case) once Opportunity record is created successfully?  Also the related list should be updated with the newly created opportunity record. What do we need to do to achieve this?
  2. Edit action: when we try to edit the related opportunity record using row action(Edit), which is equivalent to NavigationMixin Edit, the related list should get auto-refreshed without having to refresh/reload the complete page. How can we achieve this?
Let's do some brainstorming on the above scenarios.

Implementation:

Now as we have understood the requirement, let's see how can we achieve this. 
  1. Create a Platform Event.
  2. Create Apex Class to get the related opportunities.
  3. Create Process Builder to create the platform event record.
  4. Create a LWC component to test.
Now let's deep dive and understand the steps thoroughly.

1. Create a Platform Event: Platform events are power tool to connect business processes in Salesforce and external apps through the exchange of real-time event data. 
Why Platform Event: Unlike in Aura, we don't any equivalent OOB force:refreshView or force:showToast events in LWC and can't listen to those events. So one approach could be to enclose our LWC with in Aura and then listen to those events. Here I am using all LWC and hence platform event is best fit in our use case. This will allow us to refresh the related list in more real time whenever there is any standard action performed like New/Edit of a record.
Login to your salesforce environment and create platform event named Refresh Related List. Also create a custom field Record Id. Once you are done, the platform event should look like below.

2. Create Apex Class to get the related opportunities: Create apex class named OpportunityRelatedListController as shown below. This apex class is responsible to get all the related opportunities for any given account record.
public class OpportunityRelatedListController {
    @AuraEnabled
    public static List<Opportunity> getRelatedRecords(Id accountId) {
        return [
            SELECT Id, Name, Account.Name, AccountId, Amount, CloseDate, StageName 
            FROM Opportunity 
            WHERE AccountId = :accountId
        ];
    }
}
3. Create Process Builder to create the platform event record: Creating the platform event record is one of many actions that a process builder can do, when an opportunity record is updated or created. Login to your salesforce environment and create a process builder on Opportunity object with action as A Record Changes. 
  • Object: select Opportunity
  • Start the process: select when a record is created or edited
  • Add Criteria: 
    • Criteria Name: No Criteria
    • Condition: select No criteria—just execute the actions!
  • Immediate Action:
    • Action Type: Create a Record
    • Action Name: Fire Refresh Related List Platform Event
    • Record Type: Refresh Related List
    • Set Field Values:
      • Field: Record Id
      • Type: Field Reference
      • Value: Opportunity Id
Once you are done with the process builder creation, Activate the process builder. Your process builder should look like below.

Note: You can also use trigger to fire the platform event, but I am reluctant to use trigger unnecessarily unless otherwise absolutely required, as trigger is bulkier and heavier in operation and relatively less effective.

4. Create a LWC component to test: Last but not least, we have to create a LWC component to cater to our very problem discussed. Create a LWC component named dataTableActions as outlined below
  • dataTableActions.html:
    <template>
        <lightning-card title={title}>
            <lightning-button label="New" slot="actions" onclick={createOpportunity}></lightning-button>
            <lightning-datatable key-field="id" data={data} columns={columns} onrowaction={handleRowAction}
                hide-checkbox-column show-row-number-column>
            </lightning-datatable>
        </lightning-card>
        <lightning-spinner alternative-text="Loading" size="small" if:true={isLoading}></lightning-spinner>
    </template>
  • dataTableActions.js:
    import { LightningElement, api } from 'lwc';
    import { NavigationMixin } from 'lightning/navigation';
    import getRelatedRecords from "@salesforce/apex/OpportunityRelatedListController.getRelatedRecords";
    import { subscribe, unsubscribe, onError } from 'lightning/empApi';
    import { encodeDefaultFieldValues } from "lightning/pageReferenceUtils";
    
    const actions = [
        { label: 'Edit', name: 'edit' },
    ];
    
    const columns = [
        { label: 'Name', fieldName: 'Name' },
        { label: 'Amount', fieldName: 'Amount'},
        { label: 'CloseDate', fieldName: 'CloseDate' },
        { label: 'StageName', fieldName: 'StageName'},
        {
            type: 'action',
            typeAttributes: { rowActions: actions },
        },
    ];
    
    export default class DataTableActions extends NavigationMixin( LightningElement ) {
        columns = columns;
        title;
        data = [];
        error;
        @api recordId;
        isLoading = false;
    
        subscription = {};
        CHANNEL_NAME = '/event/Refresh_Related_List__e';
    
        connectedCallback() {
            this.isLoading = true;
            this.getRelatedRecords();
            this.title = 'Related Opportunities';
            subscribe(this.CHANNEL_NAME, -1, this.refreshList).then(response => {
                this.subscription = response;
            });
            onError(error => {
                console.error('Server Error--->'+error);
            });
        }
    
        refreshList = ()=> {
            this.isLoading = true;
            this.getRelatedRecords();
        }
        
        getRelatedRecords() {
            getRelatedRecords({accountId: this.recordId})
                .then(result => {
                    this.data = result;
                    this.error = undefined;
                    this.isLoading = false;
                    this.title = `Related Opportunities (${this.data.length})`;
                })
                .catch(error => {
                    this.error = error;
                    this.data = undefined;
                    this.isLoading = false;
                });
        }
    
        handleRowAction(event) {
            const actionName = event.detail.action.name;
            const row = event.detail.row;
            let recordId = row.Id;
            switch (actionName) {
                case 'edit':
                    this[NavigationMixin.Navigate]({
                        type: 'standard__recordPage',
                        attributes: {
                            recordId: recordId,
                            objectApiName: 'Opportunity',
                            actionName: 'edit'
                        }
                    });
                    break;
                default:
            }
        }
    
        createOpportunity() {
            const defaultFieldValues = encodeDefaultFieldValues({AccountId: this.recordId});
            this[NavigationMixin.Navigate]({
                type: 'standard__objectPage',
                attributes: {
                    objectApiName: 'Opportunity',
                    actionName: 'new'
                },
                state:{
                    navigationLocation: 'RELATED_LIST',
                    defaultFieldValues: defaultFieldValues
                }
            }); 
        }
    
        disconnectedCallback() {
            unsubscribe(this.subscription, () => {
                console.log('Unsubscribed Channel');
            });
        }
    }
    Note : Look at the NavigationMixin state property
    navigationLocation: 'RELATED_LIST'
     Actually this is responsible which stops the page redirection to opportunity detail page when the opportunity record is created. 
  • dataTableActions.js-meta.xml:
    <?xml version="1.0" encoding="UTF-8"?>
    <LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata">
        <apiVersion>51.0</apiVersion>
        <isExposed>true</isExposed>
        <targets>
            <target>lightning__RecordPage</target>
        </targets>
    </LightningComponentBundle>
Now as we have gone through all the above steps, and deployed our LWC component, lastly we need to update our Account Record Page and include the newly created LWC Component dataTableActions anywhere in the lightning record page app builder.



Hurrah!! Fantastic, you have done extremely well. Sit back with cup of tea 🍵, relax and check your progress. 🙂

← Picklist in Lightning Datatable


If you like this blog content and find inciteful, please comment and let me know.

Comments

  1. how did you use subscribe, unsubscribe and onError properties of of empApi in js?

    ReplyDelete

Post a Comment