Lightning Message Service (LMS) to communicate between components

In winter ’20 release (API 47.0) salesforce has introduced Lightning Message Service which can be utilised to communicate between components within a single Lightning page or across multiple pages. The advantage of using a Lightning message channel over pubsub or Aura Application event is that message channels are not restricted to a single page. Any component in a Lightning Experience application that listens for events on a message channel updates when it receives a message. It works between Lightning web components, Aura components, and Visualforce pages in any tab or in any pop-out window in Lightning Experience. 

Some solution might be required to have classic Visualforce page in place. If we need to communicate between a visualforce page (which is hosted as iframe within the lightning component) and a Lightning component, window.postMessage() was the only option. But with LMS, it can be easily achievable.

Creating Lightning Message Channel

Currently we can create Lightning Message channel with Metadata API. You can create the same with the help of VsCode. You need to create one DX project then you need to place your message channel definition with the suffix .messageChannel-meta.xml in the force-app/main/default/messageChannels directory. like below folder structure.Deploy the message channel to your org using sfdx force:source:deploy (even better, sfdx force:source:push in a scratch org), ensuring that you’re using at least API v47. Then your org knows about the message channel, and you can reference it in code.

With this publish subscriber model, any of the component (listed below) can act as a publisher or a subscriber. Below table listed how to achieve every function in different component.

FunctionAuraLWCVF Page
Listen to message
channel
<lightning:messageChannel type="SampleMC__c" onMessage="{!c.handleChanged}" scope="APPLICATION"/>
//pre-requisite to subscribe for payload to the channel
import { subscribe, unsubscribe, APPLICATION_SCOPE, MessageContext } from ‘lightning/messageService’;
//Import the definition for subscribe, unsubscribe, Scope and context of the message
import SAMPLEMC from ‘@salesforce/messageChannel/SampleMC__c’;
//message channel metadata
//pre-requisite to subscribe for payload to the channel
var SAMPLEMC = “{!$MessageChannel.SampleMC__c}”;
//pre-requisite to subscribe for payload to the channel and also for publish to the channel
publish to message channel<lightning:messageChannel type=”SampleMC__c”
aura:id=”sampleMessageChannel”/>
//pre-requisite to publish payload to the channel
import { publish, MessageContext } from 'lightning/messageService';
import SAMPLEMC from '@salesforce/messageChannel/SampleMC__c';
//pre-requisite to publish payload to the channel
publishcomponent.find(‘sampleMessageChannel’).publish(payload);
//publish the payload to the channel
publish(this.messageContext, SAMPLEMC, message);
//publish the payload to the channel
sforce.one.publish(SAMPLEMC, message);
//publish the payload to the channel. Salesforce has introduced new sforce API to publish message to a channel
subscribeonMessage method on the first row will handle the
received payload.
subscribe( this.messageContext, SAMPLEMC, (message) => { this.handleMessage(message); }, {scope: APPLICATION_SCOPE});sforce.one.subscribe(SAMPLEMC, handleMessage);
unsubscribeunsubscribe(this.subscription);sforce.one.unsubscribe(subscription);

Now enough of theory, lets jump into the real world example where we will see how this pub-sub model actually works.

  • Aura component in the above image works as publisher. This will publish account info to the message channel.
  • LWC which is added as a utilityItem will listen to the message channel and whenever a message will be published by Aura, it will be received there. LWC will not explicitly subscribe to the channel but when the component is loaded then only it subscribes to the channel.
  • VF page will explicitly subscribe to the channel by clicking subscribe button and when a payload is published, that will be received by the page.

Aura:

LMS_PublishAccountInfo.cmp

<aura:component implements="flexipage:availableForAllPageTypes,force:hasRecordId" access="global">
<aura:attribute name="record" type="Object" description="The record object to be displayed" />
<aura:attribute name="simpleRecord" type="Object" description="A simplified view record object to be displayed" />
<aura:attribute name="recordError" type="String" description="An error message bound to force:recordData" />
<!--Lightning Message Channeel where a message can be pubslihed-->
<lightning:messageChannel type="SampleMC__c"
            aura:id="sampleMessageChannel"/>

<force:recordData aura:id="record" layoutType="FULL" recordId="{!v.recordId}" targetError="{!v.recordError}"
    targetRecord="{!v.record}" targetFields="{!v.simpleRecord}" mode="VIEW" />
    <lightning:card>
        <aura:set attribute="title">
            <lightning:icon iconName="standard:account" alternativeText="Account" title="Account" />
            Publish Account Info
        </aura:set>
        <p class="slds-p-horizontal_small">
            <lightning:button variant="success" label="Publish" title="Publish" onclick="{! c.handleClick }"/>
        </p>
    </lightning:card>
</aura:component>	

LMS_PublishAccountInfoController.js

({
    handleClick : function(component, event, helper) {
        const payload =
        {
            recordId: component.get('v.recordId'),
            recordData : {
                Name: component.get('v.simpleRecord.Name'),
                Industry: component.get('v.simpleRecord.Industry')
            }
        }
        console.log(payload);
        component.find('sampleMessageChannel').publish(payload);
    }
})

LWC:

lMS_subscribeMessageChannel.html

<template>
    <lightning-card title="Listen Account Data" icon-name="custom:custom14">
        <div class="slds-m-around_medium">
            <p>Received message:</p>
            <textarea id="receivedMessageTextArea" class="textareaReceivedMessage" rows="9" disabled>{receivedMessage}</textarea>
        </div>
    </lightning-card>
</template>

lMS_subscribeMessageChannel.js


import { LightningElement, wire } from 'lwc';
import { subscribe, unsubscribe, APPLICATION_SCOPE, MessageContext } from 'lightning/messageService';

import SAMPLEMC from '@salesforce/messageChannel/SampleMC__c';

export default class lMS_subscribeMessageChannel extends LightningElement {
    @wire(MessageContext)
    messageContext;

    subscription = null;
    receivedMessage;

    connectedCallback(){
        if (this.subscription) {
            return;
        }
        this.subscription = subscribe(
            this.messageContext,
            SAMPLEMC, (message) => {
                this.handleMessage(message);
            },
            {scope: APPLICATION_SCOPE});
    }
    disconnectedCallback(){
        unsubscribe(this.subscription);
        this.subscription = null; 
    }

    handleMessage(message) {
        this.receivedMessage = message ? JSON.stringify(message, null, '\t') : 'no message payload';
    }
}

Calling subscribe method in the connectedcallback function, so whenever the component is getting loaded it will subscribe to the channel. In this similar way, pubsub module works.

Unsubscribe from channel in disconnectedcallback.

Since LWC can’t been added as utilityItem we need a wrapper Aura Component to add the LWC there.

<!--LMS_WrapAccountDataListenerLWC-->
<aura:component implements="flexipage:availableForAllPageTypes">
    <c:lMS_subscribeMessageChannel/>
</aura:component>

VF Page

LMS_SubscribeToLMS.page

<apex:page standardController="Account">
  
    <div>
        <p>This VF page will subscribe to the LMS to receive the Account Payload</p>
            <button onclick="subscribeMC()">Subscribe</button>
            <button onclick="unsubscribeMC()">Unsubscribe</button>
        <br/>
        <br/>
        <p>Received message:</p>
        <label id="MCMessageText"/>
    </div>
  
    <script>
      
        // Load the MessageChannel token in a variable
        var SAMPLEMC = "{!$MessageChannel.SampleMC__c}";
        var subscriptionToMC;
      
        // Display message in the textarea field
        function displayMessage(message) {
            var textLabel = document.querySelector("#MCMessageText");
            textLabel.innerHTML = message ? JSON.stringify(message, null, '\t') : 'no message payload';
        }

        function subscribeMC() {
            if (!subscriptionToMC) {
                subscriptionToMC = sforce.one.subscribe(SAMPLEMC, displayMessage, { scope: "APPLICATION" });
                console.log('message recvd',displayMessage);
            }
        }

        function unsubscribeMC() {
            if (subscriptionToMC) {
                sforce.one.unsubscribe(subscriptionToMC);
                subscriptionToMC = null;
            }
        }

    </script>

</apex:page>

See the video

Resource guide: https://developer.salesforce.com/blogs/2019/10/lightning-message-service-developer-preview.html

https://developer.salesforce.com/docs/component-library/documentation/en/lwc/lwc.use_message_channel