Communication Through LMS in LWC

Lightning Message Service (LMS) is a powerful feature in Salesforce that enables communication between Lightning Web Components (LWC) across the DOM hierarchy. It allows components to communicate with each other without the need for a direct parent-child relationship, making it a versatile tool for building complex applications. In this blog, we will explore how to use LMS for communication between LWCs, along with examples demonstrating its implementation.


What is Lightning Message Service?

Lightning Message Service (LMS) is a communication framework provided by Salesforce that enables effective communication between Lightning Web Components (LWC) and other components on a Lightning page.

It allows components to communicate with each other regardless of their hierarchical relationship, making it easier to build modular and interconnected applications.

LMS provides a way for components to publish messages containing data and for other components to subscribe to these messages and react accordingly. This decoupled approach to communication enhances the flexibility and maintainability of Lightning applications.

Below is the Lightning Message Service Flow diagram showcasing the communication between different components with the help of Lightning Message Channels.


Understanding Lightning Message Service (LMS):

LMS provides a pub-sub (publish-subscribe) messaging pattern, where components can publish messages (events) and subscribe to receive those messages. This decoupled communication mechanism promotes loose coupling between components, enhancing reusability and maintainability.


In Lightning Message Services, there are two key concepts: Publishers and Subscribers.

  1. Publisher:
    • A Publisher component is responsible for sending messages to a specific message channel.
    • These messages can carry data or be empty, and subscribers can react to them accordingly.
    • When creating a Publisher component, you need to import the `publish()` function from the `@salesforce/messageChannel` module.
    • This function allows you to send messages through the Lightning Message Channel.

  2. Subscriber:
    • A Subscriber component actively listens to a designated message channel.
    • Whenever a message is published on this channel, all subscribed components that are listening can respond to that message.
    • Importantly, Subscribers don’t need to know specific details about the Publisher, which helps maintain a flexible and loosely coupled system.

 MIND IT !

How Component will communicate through Lightning Message Service (LMS) in LWC


Defining Message Channel Metadata:

To establish communication channels between components, we first define message channel metadata in our Salesforce org. Follow these steps:

  1. Create a folder named "messageChannels" under `force-app/main/default` in your Salesforce project structure.
  2. Within this folder, create an XML file with the format "messageChannelName.messageChannel-meta.xml", replacing "messageChannelName" with your desired channel name.
  3. Define `lightning message field` in .xml file, see code below



    SampleMessageChannel
    true
    This is a sample Lightning Message Channel.
    
    
    
        recordId
        This is the record Id that changed
    
    
        recordData
        The current data representing the record that changed
    


Explanation:

  • The `isExposed` tag serves the purpose of making the component available for use.
  • The `lightningMessageFields` tag can be used to pass specific information defining the field for example `recordId`.
  • You can use the `description` tag to add a description about your lightning message channel explaining the goal behind creating this message channel
  • Once the `.messageChannel-meta.xml` is ready, deploy it to your Salesforce org.

Importing Message Service Features:

In your Lightning Web Component, import the necessary message service features as follows:

import { publish, subscribe, unsubscribe, APPLICATION_SCOPE, MessageContext } from 'lightning/messageService';
import msgService from '@salesforce/messageChannel/messageChannelName__c';

Defining Message Service Scope:

You can define the scope of the message service using the `@wire adapter`.
For Lightning web components, scoping is available only with `@wire adapter`.

Example:

@wire(MessageContext)
messageContext;

Publish Message Channel:

To publish a message on a message channel, use the `publish()` method of the Lightning message service. It accepts the message context, message channel, and message payload as parameters.

const messagePayload = {
    recordId: '0012y00000L5R6jAAF',
    recordData: { /* Data representing the record */ }
};
publish(this.messageContext, msgService, messagePayload);

`publish()` method accept 3 parameter :

  1. Message Context (Type Object)
  2. Message Channel (Type Object)
  3. Message Payload (The message payload is a JSON object)

messageContext : The `MessageContext` object provides information about the Lightning web component that is using the Lightning message service. Get this object via the `MessageContext` wire adapter or via `createMessageContext()`.

@wire(MessageContext)
messageContext

messageChannel : The message channel object. To import a message channel, use the scoped module `@salesforce/messageChannel`. To create a message channel in an org, use the LightningMessageChannel metadata type.

message : A serializable JSON object containing the message published to subscribers. A message can’t contain functions or symbols.


Subscribe Message Channel:

To subscribe to messages on a specific message channel, use the `subscribe()` method. Provide a listener function to handle the received messages.

this.subscription = subscribe(
    this.messageContext,
    msgService,
    (message) => this.handleMessage(message)
);

`subscribe()` method accept 4 parameters :

  1. Message Context (Type Object)
  2. Message Channel (Type Object)
  3. Listener (Type function)
  4. Subscriber Options (Type Object)

messageContext : The `MessageContext` object provides information about the Lightning web component that is using the Lightning message service. Get this object via the `MessageContext` wire adapter or via `createMessageContext()`.

@wire(MessageContext)
messageContext

messageChannel : The message channel object. To import a message channel, use the scoped module `@salesforce/messageChannel`. To create a message channel in an org, use the LightningMessageChannel metadata type.

listener : A function that handles the message once it is published.

subscriberOptions : (Optional) An object that, when set to `{scope: APPLICATION_SCOPE}`, specifies the ability to receive messages on a message channel from anywhere in the application. Import `APPLICATION_SCOPE` from `lightning/messageService`.


Un-Subscribe Message Channel:

To unsubscribe from a message channel, use the `unsubscribe()` method and provide the subscription object returned by the `subscribe()` function.

unsubscribe(this.subscription);


When to use Salesforce Lightning Message Service?

Lightning Message Service is a way to communicate or pass information between Visualforce pages, Aura, and LWC. LMS is particularly useful in scenarios where components need to interact with each other but aren't directly related in the component hierarchy. Here are some situations where you might consider using LMS:

  1. Parent-Child Component Communication: When you have a parent component and multiple child components, and you want to pass data or trigger actions between them.
  2. Sibling Component Communication: When you have sibling components on a page and need to synchronize their states or share data between them.
  3. Cross-Domain Communication: When you need to communicate between components that reside in different namespaces, such as between a managed package and a custom component.
  4. Dynamic Component Communication: When components are dynamically created or destroyed at runtime, and you need a flexible way to establish communication channels between them.

Here's an example scenario to illustrate the use of Lightning Message Service:

Suppose you have a Lightning page with two components: a parent component displaying a list of accounts, and a child component showing details of the selected account. When a user selects an account from the parent component, you want the child component to dynamically update and display the details of the selected account.



 MIND IT !

Real-Time Scenario

Real-time Scenario: Updating Related Components

Imagine a scenario where you have two Lightning Web Components (LWC) on a Lightning page: a parent component displaying a list of accounts and a child component showing details of the selected account. When a user selects an account from the parent component, you want the child component to dynamically update and display the details of the selected account.

In this scenario, you can use Lightning Message Service to facilitate communication between the parent and child components. The parent component publishes a message containing the selected account's ID, and the child component subscribes to this message channel. When a message is published, the child component receives the account ID and uses it to fetch and display the account details.

Here's an example code snippet to demonstrate this scenario:

Below is the messageChannel-meta.xml file for the above scenario:

MessageChannel: `accountSelected.messageChannel-meta.xml`



  accountSelected
  true
  This message channel is used to notify components when an account is selected.

  
  
    recordId
    The Id of the selected account record
  


Below is a sample Apex controller named AccountController for handling account-related operations:

// Apex class (AccountController)
public with sharing class AccountController {
    
    // Method to retrieve a list of accounts
    @AuraEnabled(cacheable=true)
    public static List<account> getAccounts() {
        return [SELECT Id, Name, Industry, AnnualRevenue FROM Account];
    }
}

Parent Component (AccountList):




// accountList.js
import { LightningElement, wire } from 'lwc';
import getAccounts from '@salesforce/apex/AccountController.getAccounts';
import { publish, MessageContext } from 'lightning/messageService';
import ACCOUNT_SELECTED_MESSAGE from '@salesforce/messageChannel/AccountSelected__c';

export default class AccountList extends LightningElement {
  accounts;

  @wire(MessageContext)
  messageContext;

  @wire(getAccounts)
  wiredAccounts({ error, data }) {
    if (data) {
      this.accounts = data;
    } else if (error) {
      console.error('Error fetching accounts:', error);
    }
  }

  handleAccountSelection(event) {
    const accountId = event.target.dataset.id;
    
    const message = {
      recordId: accountId
    };
    
    publish(this.messageContext, ACCOUNT_SELECTED_MESSAGE, message);
  }
}

Child Component (AccountDetails):




// accountDetails.js
import { LightningElement, wire } from 'lwc';
import { subscribe, MessageContext } from 'lightning/messageService';
import ACCOUNT_SELECTED_MESSAGE from '@salesforce/messageChannel/AccountSelected__c';
import { getRecord } from 'lightning/uiRecordApi';

export default class AccountDetails extends LightningElement {
    accountId;
    account;
    nameValue;
    industryValue;
    annualRevenueValue;
    subscription = null;

    @wire(MessageContext)
    messageContext;

    connectedCallback() {
        this.subscribeToMessageChannel();
    }

    subscribeToMessageChannel() {
        if (!this.subscription) {
            this.subscription = subscribe(
                this.messageContext,
                ACCOUNT_SELECTED_MESSAGE,
                (message) => this.handleMessage(message)
            );
        }
    }

    handleMessage(message) {
        this.accountId = message.recordId;
        console.log('Selected Account Id:', this.accountId);
    }

    @wire(getRecord, { recordId: '$accountId', fields: ['Account.Name', 'Account.Industry', 'Account.AnnualRevenue' ] })
    wiredAccount({ error, data }) {
        if (data) {
            this.account = data;
            this.nameValue = this.account.fields.Name ? (this.account.fields.Name.displayValue || this.account.fields.Name.value) : '';
            this.industryValue = this.account.fields.Industry ? (this.account.fields.Industry.displayValue || this.account.fields.Industry.value) : '';
            this.annualRevenueValue = this.account.fields.AnnualRevenue ? (this.account.fields.AnnualRevenue.displayValue || this.account.fields.AnnualRevenue.value) : '';
        } else if (error) {
            console.error('Error loading account details:', error);
        }
    }
}

Output:


  • Initially, the parent component (AccountList) displays a list of accounts.
  • When a user selects an account from the list, the child component (AccountDetails) dynamically updates and displays the details of the selected account.
  • The selected account's details (Name, Industry, and Annual Revenue) are shown in the child component.


Scenario:Demonstrates bidirectional communication between two Lightning web components using Lightning Message Service, allowing them to exchange data and respond to events independently.

In this scenario, we have two Lightning web components (LWC) - a parent component (Component A) and a child component (Component B). Component A allows users to input a message and publish it, while Component B listens for messages and displays the received message. The components communicate with each other bidirectionally using Lightning Message Service.

Here's an example code snippet to demonstrate this scenario:

Below is the messageChannel-meta.xml file for the above scenario:

MessageChannel: `lmsDemo.messageChannel-meta.xml`



  lmsDemo 
  true
  This is a sample Lightning Message Channel.
   
    lmsData
    Field used to pass data
  


Component A (lmsDemoCompA):

This component allows users to input a message and publish it.

lmsDemoCompA.html:




lmsDemoCompA.js:

//lmsDemoCompA.js
import { LightningElement, wire } from 'lwc';
import msgService from '@salesforce/messageChannel/lmsDemo__c';
import { publish, MessageContext } from 'lightning/messageService';


export default class LMSDemoCompA extends LightningElement {

  // Define the Scope of the Message Service
  @wire(MessageContext)
  messageContext

  inputMessage = ''
  handleInputChange(event) {
    this.inputMessage = event.target.value
  }

  publishMessage() {
    //publish(this messageContext, messageChannel, message)
    const message = {
      lmsData: {
        data: this.inputMessage
      }
    }
    publish(this.messageContext, msgService, message)
  }
}

Explanation:

  • This component allows users to input a message and publish it.
  • It contains an input field where users can enter a message.
  • Upon clicking the "Publish" button, the entered message is published using the Lightning Message Service.

Component B (lmsDemoCompB):

This component listens for messages and displays the received message.

lmsDemoCompB.html:




lmsDemoCompB.js:

//lmsDemoCompB.js
import { LightningElement, wire } from 'lwc';
import msgService from '@salesforce/messageChannel/lmsDemo__c'
import { subscribe, MessageContext, APPLICATION_SCOPE, unsubscribe } from 'lightning/messageService';

export default class LMSDemoCompB extends LightningElement {
  messageReceived = ''
  subscription

  // Define the Scope of the Message Service
  @wire(MessageContext)
  messageContext

  // message listener function
  handleMessage(message) {
    this.messageReceived = message.lmsData.data ? message.lmsData.data : 'No Data Received'
  }

  connectedCallback() {
    // subscibe(messageContext, messageChannel, listener, subscriberOption)
    this.subscription = subscribe(this.messageContext, msgService, (message) => {this.handleMessage(message)}, { scope: APPLICATION_SCOPE })
  }

  // Handle subscription through button
  subscribeHandler() {
    // subscibe(messageContext, messageChannel, listener, subscriberOption)
    this.subscription = subscribe(this.messageContext, msgService, (message) => {this.handleMessage(message)}, { scope: APPLICATION_SCOPE })
  }

  // Handle un-subscription through button
  unsuncribeHandler() {
    unsubscribe(this.subscription)
    this.subscription = null
  }
}

Explanation:

  • This component displays the message received from Component A.
  • It provides buttons to subscribe and unsubscribe from the message channel.
  • When subscribed, it listens for messages published on the specified message channel using Lightning Message Service.
  • Upon receiving a message, it updates the UI to display the received message.

Output:


  • Users can input a message in Component A and publish it.
  • Component B listens for messages and displays the received message.
  • Users can subscribe and unsubscribe from the message channel using Component B.
  • When subscribed, Component B dynamically updates to display the received message.
  • When unsubscribed, Component B stops receiving messages from Component A.

Overall, this scenario demonstrates bidirectional communication between two Lightning web components using Lightning Message Service, allowing them to exchange data and respond to events independently.


Limitations of Salesforce Lightning Message Service

  1. LMS does not have the capability to integrate with the Salesforce Mobile app, AppExchange, Lightning Out, and Lightning Communities.
  2. LMS Doesn’t work in iframes.
  3. LMS doesn’t work in salesforce classic.
  4. Doesn’t support creating lightning message channels directly in the salesforce UI.

Conclusion:

Lightning Message Service is a powerful tool for enabling communication between Lightning Web Components in Salesforce. By implementing a pub-sub pattern, components can communicate in a decoupled manner, enhancing the flexibility and scalability of Lightning applications. With proper usage and understanding, LMS facilitates building complex and dynamic user interfaces in Salesforce seamlessly.


“Stay tuned for more exciting LWC topics, and keep exploring the world of Lightning Web Components to become a Salesforce development pro.”

Happy coding with LWC!

Share This Post:

About The Author

Hey, my name is Saurabh Samir and I am Salesforce Developer. I have been helping to elevate your Lightning Web Components (LWC) Knowledge. Do comment below if you have any questions or feedback's.