Friday, 18 October 2019

Lightning Message Service (LMS) | MessageChannel

In Winter 20 Salesforce Introduce the new feature Lightning Message Service and it is generally available in Summer 20. Lightning Message Service (LMS) allow you to communicate between Visualforce and Lightning Components (Aura and LWC both) on any Lightning page. LMS API allow you to publish message throughout the lightning experience and subscribe the same message anywhere with in lightning page. It is similar to Aura Application Events to communication happens between components.


Lightning Message Service is based on a new metadata type: Lightning Message Channels. We need to use Lightning Message Channel to access the Lightning Message Service API.
  1. In LWC we can access Lightning Message Channel with the scoped module @salesforce/messageChannel
  2. In Visualforce, we can use global variable $MessageChannel
  3. In Aura, use lightning:messageChannel in your component

 

When to use Lightning Message Service. 

In Lightning Experience, if we want a Visualforce page to communicate with a Lightning web component then we have to implement a custom publish-subscribe solution because this is currently not possible with LWC Event. Now, we can use the Lightning Message Service API to handle this communication.
Lightning Message Service LMS and Message Channel


Let see how we can implements this

Create Lightning Message Channel :-

Currently we can create Lightning Message channel with Metadata API. You can create the same with the help of VsCode. I hope you have VsCode installed in your machine if not please check this post. 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.

MyMessageChannel.messageChannel-meta.xml
<?xml version="1.0" encoding="UTF-8"?>
<LightningMessageChannel xmlns="http://soap.sforce.com/2006/04/metadata">
   <masterLabel>MyMessageChannel</masterLabel>
   <isExposed>true</isExposed>
   <description>This Lightning Message Channel sends information from VF to LWC</description>

   <lightningMessageFields>
       <fieldName>messageToSend</fieldName>
       <description>message To Send</description>
   </lightningMessageFields>

   <lightningMessageFields>
       <fieldName>sourceSystem</fieldName>
       <description>My source?</description>
   </lightningMessageFields>

</LightningMessageChannel>

And Package.xml should be like below
package.xml
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Package xmlns="http://soap.sforce.com/2006/04/metadata">
    <types>
       <members>*</members>
       <name>LightningMessageChannel</name>
    </types>
    <version>47.0</version>
</Package>
Right click on LightningMessageChannel file and Deploy in Org.

 

Create Visualforce page and LWC

 

1) LMS with Visualforce page

Salesforce introduced new sforce.one APIs — publish, subscribe, and unsubscribe — in Visualforce to interact with LMS (only available in Lightning).

LMSVisualforcePage.page
<apex:page>
  
    <div>
        <p>Enter Your Message Here:</p>
        <input type="text" id="theMessage" />
        <button onclick="publishMC()"> Publish Msg</button>
        <br/><br/>
            <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.MyMessageChannel__c}";
        var subscriptionToMC;

        function publishMC() {
            const message = {
                messageToSend: document.getElementById('theMessage').value,
                sourceSystem: "From VisualForce Page"
            };
            sforce.one.publish(SAMPLEMC, message);
        }
      
        // 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);
            }
        }

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

    </script>

</apex:page>

  • "subscribeMC" method is used to subscribe the Message Channel with "sforce.one.subscribe(SAMPLEMC, displayMessage);"
  • "unsubscribeMC" method to unsubscribe the Message Channel with "sforce.one.unsubscribe(subscriptionToMC);"
  • "publishMC" to publish the message withe the help of "sforce.one.publish(SAMPLEMC, message);"

2) LMS with Lightning Web Components

lMCWebComponentDemo.js
import { LightningElement,track } from 'lwc';
import { publish,subscribe,unsubscribe,createMessageContext,releaseMessageContext } from 'lightning/messageService';
import SAMPLEMC from "@salesforce/messageChannel/MyMessageChannel__c";

export default class LMCWebComponentDemo extends LightningElement {
    @track receivedMessage = '';
    @track myMessage = '';
    subscription = null;
    context = createMessageContext();

    constructor() {
        super();
    }

    handleChange(event) {
        this.myMessage = event.target.value;
    }

    publishMC() {
        const message = {
            messageToSend: this.myMessage,
            sourceSystem: "From LWC"
        };
        publish(this.context, SAMPLEMC, message);
    }

    subscribeMC() {
        if (this.subscription) {
            return;
        }
        this.subscription = subscribe(this.context, SAMPLEMC, (message) => {
            this.displayMessage(message);
        });
     }
 
     unsubscribeMC() {
         unsubscribe(this.subscription);
         this.subscription = null;
     }

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

     disconnectedCallback() {
         releaseMessageContext(this.context);
     }

}
  • First, we have to ensure that we import both the methods to interact with LMS
    import { publish,subscribe,unsubscribe,createMessageContext,releaseMessageContext } from 'lightning/messageService';
    import SAMPLEMC from "@salesforce/messageChannel/MyMessageChannel__c";

lMCWebComponentDemo.html
<template>
    <lightning-card title="LMC Web Component" icon-name="custom:custom14">
        <div class="slds-m-around_medium">
            <p>MessageChannel: MyMessageChannel__c</p>
            <br>
        </div>
        <!-- Default/basic -->
        <div class="slds-p-around_medium lgc-bg">
            <lightning-input type="text" label="Enter some text" value={myMessage} onchange={handleChange}></lightning-input>
            <lightning-button label="Publish" onclick={publishMC}></lightning-button>
        </div>

        <div class="slds-p-around_medium lgc-bg">
            <lightning-button label="Subscribe" onclick={subscribeMC}></lightning-button>
            <lightning-button label="Unsubscribe" onclick={unsubscribeMC}></lightning-button>
            <p>Latest Message Received</p>
            <lightning-formatted-text value={receivedMessage}></lightning-formatted-text>
        </div>

    </lightning-card>
</template>


Reference:

Some other related post:
  1. Event Communication in Lightning Web Components
  2. Event Communication in Lightning Components
  3. Setup VsCode for Salesforce
  4. Lightning We Components

Thanks
Amit Chaudhary


14 comments:

  1. Hi Amit
    Thanks for sharing this.I have followed your steps but I am facing an issue while deploying messageChannel to my devorg . Here is my error message -force-app/main/default/messageChannels/MyMessageChannel.messageChannel-meta.xml Not available for deploy for this organization
    I have checked my api version, it is 47.0 .
    Any help would be appreciated.
    Thanks
    Ashwin

    ReplyDelete
    Replies
    1. It's not GA yet outside of Dev Orgs and Scratch Orgs: https://developer.salesforce.com/docs/atlas.en-us.222.0.api_meta.meta/api_meta/meta_lightningmessagechannel.htm

      Delete
  2. I hope you added below tag in package.xml
    LightningMessageChannel

    ReplyDelete
    Replies
    1. Yes, added 'LightningMessageChannel' in package.It works in personal dev sandbox, not sure why it is not working my work sandbox.Can we use 'LightningMessageChannel' in lightning communities ?

      Delete
  3. Hi Amit,

    how to display records using LMS future in lwc

    ReplyDelete
  4. Can I use only for one aura component to another aura component???

    ReplyDelete
  5. Hi,

    I have used this for communication from my LWC to VF pages but my VF page is throwing error 'sforce.one.subscribe is not a function' while trying to subscribe to the channel

    ReplyDelete
  6. Nice article, but:
    1. You've left out the example for Aura.
    2. You didn't mention VF pages need to be configured to allow execution in a Lightning context and then displayed in a lightning context for 'sforce' to be available (I'm not sure if there are any valid use cases where LMS would be useful outside of LX).
    3. The code on this page is now a little outdated. For subscribing in both VF and LWC, you need to include the scope. Also, in LWC, context is handled a little differently.

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

    ReplyDelete
  7. Does the Local Development Server for LWC support LMS? I keep geting an error that it can't find the Message Channel, but it's working in a sandbox.

    ReplyDelete
  8. Hi, I'm doing a similar implementation where I want to create a channel for an apex component to communicate with a LWC component. However, the messageChannels are different for me in the Apex and the LWC component.

    In apex, my messageChannel is loaded by "{!$MessageChannel.RecordUpdate__c}". In LWC, my messageChannel is loaded by: import RECORD_UPDATE_MESSAGE_CHANNEL from '@salesforce/messageChannel/RecordUpdate__c';

    Thus, my apex component and lwc component fail to publish and subscribe to the same channel. Does anyone know why this happens?

    ReplyDelete
    Replies
    1. I realize the channel tokens being different is ok. The problem I have I was that I use LMS to publish from a VF page (not lightningOut) and catch the event in a LWC (in lightningOut). I wasn't able to catch the event.

      Delete