Using the Lightning Message Service

When working with Lightning Aura Components, if we wanted to communicate between two unrelated components, we used Application Events. With Lightning Web Components, the equivalent to Application events was the Publish Subscribe model, or PubSub for short. This had some drawbacks, as it required developers to download the pubsub class and deploy it to their org in order to communicate between two lightning web components that didn’t have a parent – child relationship. If the pubsub class changed, we would have to download a newer version of it.

The Lightning Message Service deprecates the need to use pubsub. With the Lightning Message Service, a Lightning Web Component can publish or subscribe to a Message Channel that the developer defines.

I was trying to think of an example of how I could use this, and I thought about sending a search string from one component, publishing that string over a message channel, and having another component subscribe to that channel and use that string to populate a datatable.

From the publisher component, we type in a search parameter. The string is sent over a message channel, and handled by the subscriber component, where it is used to query the database and return a list of Account records whose name match the search string.

We have to create a message channel before we can publish to it. In VSCode, in the force-app/main/default folder, create a folder called messageChannels. Then create a file with the name SampleMessageChannel.messageChannel-meta.xml

<?xml version="1.0" encoding="UTF-8"?>
<LightningMessageChannel xmlns="http://soap.sforce.com/2006/04/metadata">
    <masterLabel>SampleMessageChannel</masterLabel>
    <isExposed>true</isExposed>
    <description>This is a sample Lightning Message Channel.</description>
</LightningMessageChannel>

The publisher component allows the user to enter a search string. We can send this string to any other components that are subscribed to the message channel, Here we use a string to search by account name

<!-- publisherComponent.html -->
<template>
    <lightning-card title="myLwcPublisher" icon-name="custom:custom14">
        <div class="slds-m-around_medium">
            <p>MessageChannel: SampleMessageChannel</p>
            <br>
            <lightning-input value={inputMessage} onchange={handleInputChange}></lightning-input>
            <lightning-button label="Search" onclick={handleClick}></lightning-button>
        </div>
    </lightning-card>
</template>
// publisherComponent.js
import { LightningElement, wire } from 'lwc';
import { publish, MessageContext } from 'lightning/messageService';
import SAMPLEMC from '@salesforce/messageChannel/SampleMessageChannel__c';

export default class PublisherComponent extends LightningElement {
    @wire(MessageContext)
    messageContext;
    inputMessage;
          
    handleClick() {
        
        const message = { nameMessage : this.inputMessage};
        publish(this.messageContext, SAMPLEMC, message);
    }

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

The subscriber component subscribes to the SampleMessageChannel, and from that message channel, takes takes the string that was sent and queries the Salesforce database for matching Accounts

<!-- subscribeComponent.html -->

<template>
    <lightning-card title="myLwcSubscriber" icon-name="custom:custom14">
        <div class="slds-m-around_medium">
            <p>MessageChannel: SampleMessageChannel</p>
        </div>

        <template if:true={accounts.data}>
            <div style="height: 300px;">
                <lightning-datatable
                        key-field="Id"
                        data={accounts.data}
                        columns={columns}
                        hide-checkbox-column>
                </lightning-datatable>
            </div>
        </template>
        

    </lightning-card>
</template>
// subscribeComponent.js
import { LightningElement, wire } from 'lwc';
import { subscribe, unsubscribe, APPLICATION_SCOPE, MessageContext } from 'lightning/messageService';

import SAMPLEMC from '@salesforce/messageChannel/SampleMessageChannel__c';
import searchAccounts from '@salesforce/apex/AccountSearch.searchAccounts';

const columns = [
    { label: 'Name', fieldName: 'Name' },
    { label: 'Type', fieldName: 'Type' }
];

export default class SubscribeComponent extends LightningElement {

    columns = columns;



    @wire(MessageContext)
    messageContext;

    subscription = null;
    receivedMessage;
    accountName;
    nameObject;


    connectedCallback(){
        this.subscribeMC();
    }

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

    unsubscribeMC() {
        unsubscribe(this.subscription);
        this.subscription = null;
    }

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

    @wire(searchAccounts, {accountName : '$accountName'})
    accounts;
}

Apex Controller:

public with sharing class AccountSearch {
    

    @AuraEnabled(cacheable=true)
    public static List<Account> searchAccounts(String accountName){
        String accountNameWildcard = '%' + accountName + '%';
        List<Account> accountList = [SELECT Id, Name, Type FROM Account WHERE Name LIKE : accountNameWildcard];
        return accountList;
    }
}

GitHub: https://github.com/tjkingsbury/LightningMessagingService