Re-using LWC Message Channel for multi-way communication
I recently had conversations with some of my team members regarding sending two-way messages across LWC components in the same Lightning page – many of them asked if we can use multiple message channels (as part of Lightning Message Service aka LMS). Though there’s no harm in using multiple message channels, you can infact just use one single message channel for simple multi-way communications.
Think of the message channel as a medium or a pipe – where multiple components can act as both publishers and subscribers at the same time and send / receive messages. You can define logic in your code to distinguish which messages to consume and which ones to ignore.
In the below example, I have created 3 Lightning Web Components – lmsSales, lmsService and lmsMarketing – and we will be sending messages from one to the other using LMS. I also have a shared module called lmsShared – as you guessed, this is where we’ll keep some of the shared code for the messaging. Finally we also have the one message channel defined called LWCMessageChannel.
The single message channel
We will define 2 fields in the message channel:
- lmsData – to contain the primary data being passed
- lmsMsgType – a category field to distinguish which component is sending the message – you can use this field to either accept or reject the message for processing in each component
Note: The reason I am using 2 fields is to show that you can in fact define multiple fields and not just one in the meta xml. If you do not wish to use 2 fields, you can even use the single lmsData field to refer to an object and use an additional key / value pair to identify the category type – like below:
lmsData = {
message: "Data being received",
msgtype: "Sales"
}
The Sales, Service and Marketing Components
We will define 3 Lightning Web Components – one each for Sales, Service and Marketing. The code to be used across the 3 components would be very similar – the only distinction shall be in the subscription channels available – for Sales, the channels available should be Service and Marketing, for Service should be Sales and Marketing and so on.
We will have the option to both Publish a message and Subscribe to multiple channels as well using the “Publish” and “Subscribe to Channels” buttons respectively. For example, if you check “Service” and “Marketing” checkboxes in the Sales component, it means you can subscribe to messages being sent from those two components. If you need to subscribe to selective channels, then you can specify those using the checkboxes as shown below.
Once subscribed, you should be able to see the messages being sent from the channels that the component has subscribed to. In the above example, the Sales component shall receive messages sent from both Service and Marketing – however Service shall get only from Sales and Marketing only from Service.
How the code works
You can import the LMS Channel and functions in each of the 3 components and implement both publish and subscribe – it should work fine even when you use the same message channel for all 3. The only point to note is that you will need to cross check the lmsMsgType field with the options you have selected in your component – and then process the received message only if the checked options contain the lmsMsgType.
For e.g. in case Service component sends over a message “Test Service message“, the lmsMsgType will be set as “Service“. When the Sales component is subscribed to the message channel, we will check if the options checked in Sales (in this case – [Service, Marketing]) contains the lmsMsgType (=Service) – which is true. Hence the message will be shown on the Sales component. But if Marketing component sends out a message “Test Mktg message“, then it won’t be visible on Service but only on Sales as the Service checked options is [Sales].
Note: This is only a sample logic on how you can use a message type classification to distinguish the publisher component and selectively process message. You may come up with your own similar logic as per your requirements when implementing the messaging.
Code Re-usability for LMS
Since all the 3 components have very similar code, it would be better to move similar code to a shared module, especially if we may end up having multiple components referring the same set of code – and need to make changes in future.
For our example, we will create a separate module under the lwc path called lmsShared – and we will create 2 files within this folder:
lmsShared.js-meta.xml
lmsShared.js
This js file shall contain our shared functions that we shall re-use across our 3 components. We shall declare the LMS functions and message channel imports at the top.
We will then have shared functions for publishing and subscribing in this module. As you can see, we will be passing arguments to these shared functions from the calling components (Sales / Service / Marketing). For publish, it’s quite straightforward – we set up the message object with the lmsData and lmsMsgType fields that are defined in our message channel, and then call the publish method.
Note: if you are using the single lmsData field for the message channel, then ensure the message type is set inside the lmsData object.
For the subscription to work, we will be passing the message context and then using a callback function written in the calling component – we will pass the subscription and the message back – and we can handle these as needed in the calling components.
Finally, we need to export these functions so that we can use them in our components.
Component Code
I’ll highlight the important sections that we need to implement in the main components (Sales / Service / Marketing) for the messaging to work. Please do check the full code in the github files including the html template and meta-xml.
First and foremost, we need to import the exported functions from the lmsShared module. We will also be using the @wire(MessageContext) to create the message context for the messaging to work.
We will have a method called handlePublish that shall be invoked when the Publish button is clicked. We will create a message context and then invoke our publishMC method residing in the lmsShared module, passing the message to be published along with the channel type.
When the Subscribe to Channels button is clicked, we will handle this through a handleSubscribe method. We have to check if the Subscription is already set – if not set, then we will call the subscribeMC method residing in the lmsShared module. If the button was clicked when subscription is already active, then it would indicate an unsubscription request – and we will call the unsubscribeMC method residing in the lmsShared module.
As you can see, the subscribeMC function has a callback defined, where we will receive the subscription and message back from the lmsShared module. We can then handle this in our component as needed – in our scenario, we will check if the returned message is valid and if the lmsMsgType returned is contained in the checked options stored in optValues – if both of these are true, then we will assign the message data to receivedMessage, else we will retain the old message as is.
Github code
You can view the full set of code files at the below github link – please do note that this is just a sample scenario utilizing a single message channel for multiple components to act as both publisher and subscriber. You should be able to implement your own logic as needed once you get the concept – the idea is to show that you don’t need to implement multiple message channels each time you want to send messages across multiple publisher / subscriber sets and re-use a single channel.