Forcetrails: Expose LWC events to Lightning App Builder - Dynamic Interactions

publish-events-to-app-builder-from-lwc-salesforce

Hello Trailblazers! In this post, I have explained how we can expose events from Lightning web components to the Lightning App Builder. 

Please note that this is only available in the Salesforce org with Winter 2022 or greater releases. Also, it is supported only on the lightning app pages.

Why do we need to expose events to the Lightning App Builder?

In Winter 2022 release Salesforce announced the Dynamic Interactions, which means that developers can define custom events on Lightning web components and admins can add interactions to those events from Lightning App Builder, from the new Dynamic Interaction UI in the app builder.

Basically, admins can configure which other component can handle that event without code. So when the event gets fired from the source component it will update the public properties of the target component. 

View all code on GitHub: Expose LWC events to Lightning App Builder


Code with example & explanation

Assume that you have a Lightning App a component showing list of Accounts. When picked you want to show the related contacts, on another component (Note that another way to achieve this is to use LMS but the Dynamic Interactions are much simpler than that).

So here is the code followed by an explanation.

Account List Component

accountList.html

<template>
    <div class="slds-var-p-around_xx-small slds-theme_default">
        <div
            class="slds-var-p-around_xx-small slds-text-body_regular"
            if:true={wiredAccounts.error}
        >
            No data
        </div>

        <div
            class="slds-var-p-around_xx-small"
            if:true={wiredAccounts.data}
        >
            <div class="slds-text-body_regular slds-var-p-around_xx-small">
                Select an Account
            </div>
            <!-- sldsValidatorIgnoreNextLine -->
            <lightning-datatable
                key-field="id"
                data={wiredAccounts.data}
                show-row-number-column
                columns={columnsList}
                max-row-selection="1"
                onrowselection={handleRowSelection}
            >
            </lightning-datatable>
        </div>
    </div>
</template>

accountList.js

import { LightningElement, api, wire } from "lwc";

import getAccounts from "@salesforce/apex/AccountListController.getAccounts";

const ACCOUNT_COLUMNS = [
    { fieldName: "Name", label: "Name" },
    { fieldName: "Type", label: "Type" }
];

export default class AccountList extends LightningElement {
    @api apiName;
    @api listViewApiName;

    columnsList = ACCOUNT_COLUMNS;

    @wire(getAccounts)
    wiredAccounts;

    handleRowSelection(event) {
        const itemSelected = new CustomEvent("itemselected", {
            detail: {
                recordId: event.detail.selectedRows[0].Id,
                accountName: event.detail.selectedRows[0].Name
            }
        });
        this.dispatchEvent(itemSelected);
    }
}

accountList.js-meta.xml

<?xml version="1.0" encoding="UTF-8" ?>
<LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata">
    <apiVersion>53.0</apiVersion>
    <isExposed>true</isExposed>
    <masterLabel>Account List</masterLabel>
    <targets>
        <target>lightning__AppPage</target>
    </targets>
    <targetConfigs>
        <targetConfig targets="lightning__AppPage">
            <event
				name="itemselected"
				label="Item Selected"
				description="This event fires when an item is selected."
			>
                <schema>
                    {
                        "type": "object",
                        "properties": {
                            "recordId": {
                                "type": "string",
                                "title": "Record Id",
                                "description": "Enter an 18-digit record Id."
                            },
                           "accountName": {
                               "type": "string",
                               "title": "Acount Name"
                           }
                       }
                    }
                </schema>
            </event>
        </targetConfig>
    </targetConfigs>
    <description>Defines an event for an Account List component</description>
</LightningComponentBundle>


Explanation of the above code

1: I have created the component AccountList and implemented the code to show the Accounts in a data table. Also, I have set the max row selection to 1 so users can pick max one account from the list see accountList.html code above.

2: I have handled the rowselection of the lightning data table using the handleRowSelection function. Which fires the event itemselected. Note this event name.

3: Now in the meta XML file of the component I have exposed the event itemselected to the lightning app builder. Note that this event name is exactly the same as the event that we have dispatched in the handleRowSelection controller function.

4: Note that itemselected event detail contains two properties recordId (selected account id) and accountName (selected account name).

handleRowSelection(event) {
        const itemSelected = new CustomEvent("itemselected", {
            detail: {
                recordId: event.detail.selectedRows[0].Id,
                accountName: event.detail.selectedRows[0].Name
            }
        });
        this.dispatchEvent(itemSelected);
}

5: See the target config of the component where I have exposed the event from the accountList component to the app builder. The target config uses the same event name and property name and defines the labels and descriptions to display in the app builder. 

Please note that the event name and event detail property names should be the same as the schema defined.

<targetConfig targets="lightning__AppPage">
    <event
        name="itemselected"
        label="Item Selected"
        description="This event fires when an item is selected."
    >
        <schema>
            {
                "type": "object",
                "properties": {
                    "recordId": {
                        "type": "string",
                        "title": "Record Id",
                        "description": "Enter an 18-digit record Id."
                    },
                   "accountName": {
                       "type": "string",
                       "title": "Acount Name"
                   }
               }
            }
        </schema>
    </event>
</targetConfig>


Contact List Component

This component simply displays the list of contacts related to the selected account.

contactList.html

<template>
    <div class="slds-var-p-around_xx-small slds-theme_default">
        <div
            class="slds-var-p-around_xx-small slds-text-body_regular"
            if:true={wiredContacts.error}
        >
            No data
        </div>

        <div
            class="slds-var-p-around_xx-small"
            if:true={wiredContacts.data}
        >
            <div class="slds-text-body_regular slds-var-p-around_xx-small">
                Related Contacts of the selected account: <b>{accountName}</b>
            </div>
            <!-- sldsValidatorIgnoreNextLine -->
            <lightning-datatable
                key-field="id"
                data={wiredContacts.data}
                show-row-number-column
                hide-checkbox-column
                columns={columnsList}
                onrowselection={handleRowSelection}
            >
            </lightning-datatable>
        </div>
    </div>
</template>

contactList.js

import { LightningElement, api, wire } from "lwc";

import getContact from "@salesforce/apex/AccountListController.getContacts";

const CONTACT_COLUMNS = [
    {
        fieldName: "Name",
        label: "Name"
    },
    {
        fieldName: "Email",
        label: "Email",
        type: "email"
    },
    {
        fieldName: "Phone",
        label: "Phone",
        type: "phone"
    }
];
export default class ContactList extends LightningElement {
    @api recordId;
    @api accountName;

    columnsList = CONTACT_COLUMNS;

    @wire(getContact, { accountId: "$recordId" })
    wiredContacts;
}

contactList.js-meta.xml

<?xml version="1.0" encoding="UTF-8" ?>
<LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata">
    <apiVersion>53.0</apiVersion>
    <isExposed>true</isExposed>
    <masterLabel>Contact List</masterLabel>
    <targets>
        <target>lightning__AppPage</target>
    </targets>
    <targetConfigs>
        <targetConfig targets="lightning__AppPage">
            <property name="recordId" type="String" label="Account Record Id" />
            <property name="accountName" type="String" label="Account Name" />
        </targetConfig>
    </targetConfigs>
    <description>Shows related contacts of the account</description>
</LightningComponentBundle>


Apex Controller Class

public with sharing class AccountListController {
    @AuraEnabled(cacheable = true)
    public static List<Account> getAccounts(){
        return [SELECT Name, Type FROM Account];
    }

    @AuraEnabled(cacheable = true)
    public static List<Contact> getContacts(Id accountId){
        return [SELECT Name, Email, Phone FROM Contact WHERE AccountId = :accountId];
    }
}


Admin Configuration

After you deploy the component creates a new app page and both components on that. Then follow the below steps to configure the Dynamic Interactions

  1. Open the app builder and select the component and go to the interactions section






  2. Set the target component public property values using event properties.

  3. Save and activate the app.
Which is your favorite update from Winter 2022 release? Comment below.

3 comments:
  1. Hi, Can you please tell me how to enable dynamic interactions in SF Org.
    I'm getting "lwc/accountList/accountList.js-meta.xml: The '' tag is only supported in orgs that are enabled for Dynamic Interactions.: Source" this error.

    ReplyDelete
    Replies
    1. Hi Sushil, please check if your org is in Winter 2022 release? Also, as of now Dynamic interactions are not available for record pages or home pages, only App pages are supported. So check if you are using that only on app page?

      Delete
    2. Thanks for your reply and this post. Now I'm able to perform dynamic interactions.

      Delete

Hi there, comments on this site are moderated, you might need to wait until your comment is published. Spam and promotions will be deleted. Sorry for the inconvenience but we have moderated the comments for the safety of this website users. If you have any concern, or if you are not able to comment for some reason, reach email us at rahul@forcetrails.com