Lightning Web Components

Communication Between Unrelated Components Using Publisher -Subscriber pattern(LWC)

In this post we are going to discuss about communication between components which are not within the same Dom tree using publisher subscriber pattern in Lightning web components.

Lets say we have two components firstly the “pubComp” component and second is a “subComp” component. “pubComp” component is a component from where we want to fire an event on certain button click or some UI event and “subComp”component is a component where we want to handle this event fired by the pub component.

  • Step 1 : Import pubsub.js into LWC project.

So for this the first thing we need to do is to include a JavaScript file name “pubsub” in your lwc directory. It is nothing but the shared JavaScript file which your two components will be using to communicate between themselvesSo for this the first thing we need to do is to include a JavaScript file name “pubsub” in your lwc directory. It is nothing but the shared JavaScript file which your two components will be using to communicate between themselves

pubsub.js

/**
 * A basic pub-sub mechanism for sibling component communication
 *
 * TODO - adopt standard flexipage sibling communication mechanism when it's available.
 */

const events = {};

/**
 * Confirm that two page references have the same attributes
 * @param {object} pageRef1 - The first page reference
 * @param {object} pageRef2 - The second page reference
 */
const samePageRef = (pageRef1, pageRef2) => {
    const obj1 = pageRef1.attributes;
    const obj2 = pageRef2.attributes;
    return Object.keys(obj1)
        .concat(Object.keys(obj2))
        .every(key => {
            return obj1[key] === obj2[key];
        });
};

/**
 * Registers a callback for an event
 * @param {string} eventName - Name of the event to listen for.
 * @param {function} callback - Function to invoke when said event is fired.
 * @param {object} thisArg - The value to be passed as the this parameter to the callback function is bound.
 */
const registerListener = (eventName, callback, thisArg) => {
    // Checking that the listener has a pageRef property. We rely on that property for filtering purpose in fireEvent()
    if (!thisArg.pageRef) {
        throw new Error(
            'pubsub listeners need a "@wire(CurrentPageReference) pageRef" property'
        );
    }

    if (!events[eventName]) {
        events[eventName] = [];
    }
    const duplicate = events[eventName].find(listener => {
        return listener.callback === callback && listener.thisArg === thisArg;
    });
    if (!duplicate) {
        events[eventName].push({ callback, thisArg });
    }
};

/**
 * Unregisters a callback for an event
 * @param {string} eventName - Name of the event to unregister from.
 * @param {function} callback - Function to unregister.
 * @param {object} thisArg - The value to be passed as the this parameter to the callback function is bound.
 */
const unregisterListener = (eventName, callback, thisArg) => {
    if (events[eventName]) {
        events[eventName] = events[eventName].filter(
            listener =>
                listener.callback !== callback || listener.thisArg !== thisArg
        );
    }
};

/**
 * Unregisters all event listeners bound to an object.
 * @param {object} thisArg - All the callbacks bound to this object will be removed.
 */
const unregisterAllListeners = thisArg => {
    Object.keys(events).forEach(eventName => {
        events[eventName] = events[eventName].filter(
            listener => listener.thisArg !== thisArg
        );
    });
};

/**
 * Fires an event to listeners.
 * @param {object} pageRef - Reference of the page that represents the event scope.
 * @param {string} eventName - Name of the event to fire.
 * @param {*} payload - Payload of the event to fire.
 */
const fireEvent = (pageRef, eventName, payload) => {
    if (events[eventName]) {
        const listeners = events[eventName];
        listeners.forEach(listener => {
            if (samePageRef(pageRef, listener.thisArg.pageRef)) {
                try {
                    listener.callback.call(listener.thisArg, payload);
                } catch (error) {
                    // fail silently
                }
            }
        });
    }
};

export {
    registerListener,
    unregisterListener,
    unregisterAllListeners,
    fireEvent
};

Lets explore the four important methods present inside this pubsub.js:


fireEvent :

The “fireEvent” method is used by the component which is supposed to fire the event and the parameter would be the page reference of a component which is
firing the event(pageRef) , the name given to the event(eventName) and whatever data the component needs to pass along with the event(payload).

registerListener :

register listener method would be used by the component which is handling the event. The parameter would be the “eventName” which will be same as
the name given earlier to the event . Next paramter is the callback method. This callback method would be any method inside the handling component which will handle the event and perform certain operations.

unregisterListener:

unregisterListener method is called by the handling component only when the component needs to unregister to the listener which it has already subscribed earlier.

unregisterAllListeners:

unregisterAllListeners removes all the listeners from the current component.

Other components will be using this library to communicate between them and other components can access this method because if you can see at the end we have exported these methods.

  • Step 2 : Publishing Event from “pubComp”
<template>
    <lightning-button label="call Event" onclick = {calEvent}></lightning-button>
</template>
import { LightningElement,wire,track } from 'lwc';
import {CurrentPageReference} from 'lightning/navigation';
import {fireEvent} from 'c/pubsub';


export default class PubComp extends LightningElement {
@wire (CurrentPageReference) pageRef;

    calEvent(event){
        var eventParam = {'firstname': 'KnowledgePredator'};
        fireEvent(this.pageRef,'pubsubevent',eventParam);

    }
}
<?xml version="1.0" encoding="UTF-8"?>
<LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata">
    <apiVersion>47.0</apiVersion>
    <isExposed>true</isExposed>
    <targets>
        <target>lightning__RecordPage</target>
        <target>lightning__AppPage</target>
        <target>lightning__HomePage</target>
    </targets>
</LightningComponentBundle>

We need to import the CurrentPageReference from lightning navigation library. CurrentPageReference will be contain the URL of our current page.

Import fireEvent from pubsub why because in the pub/sub library we have this fire event method and we have exported this method so our component can import this.

Use the fireEvent method which we have imported from our pub/sub library and pass the corresponding paramters to it.

Step 3 : Subscribe Event in “subComp”.

<template>
    <lightning-card  title = "Contact Details" icon-name="standard:contact">
        <p>{myName}</p>
    </lightning-card>
</template>
/* eslint-disable no-alert */
import { LightningElement,wire,track} from 'lwc';
import {registerListener,unregisterAllListeners} from 'c/pubsub';
import {CurrentPageReference} from 'lightning/navigation';
export default class SubComp extends LightningElement {
    @track myName = "Salesforce Predator";
    @wire (CurrentPageReference) pageRef;
    connectedCallback(){
        registerListener('pubsubevent',this.handleCallback,this);
    }

    disconnectedCallback(){
        unregisterAllListeners(this);
    }
 
    handleCallback(detail){
        alert('paramter from publisher'+ detail.firstname);
        this.myName = detail.firstname;
        
    }
}
<?xml version="1.0" encoding="UTF-8"?>
<LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata">
    <apiVersion>47.0</apiVersion>
    <isExposed>true</isExposed>
    <targets>
        <target>lightning__RecordPage</target>
        <target>lightning__AppPage</target>
        <target>lightning__HomePage</target>
    </targets>
</LightningComponentBundle>

In the subscriber component we need to import registerListener, unregisterAllListeners methods to register and unregister to the events.

In the connected callback we need to register to the same event which we published in “pubComp”. Also we need to pass the callback method while doing this, so there we will be handling paramters recieved from publisher component.

In the disconnected callback I need to unregister this particular event using unregisterAllListeners or unregisterListener methods.

In th handleCallback we are changing the value of paramter displayed with the new value recieved from publisher component as event paramter.

Lets deploy the changed to salesforce org and include the component inside a record page.

Initially you will notice that the value insde contact details is “Salesforce Predator”. Now If you will click on that “call Event” button , Event will be fired and value will be changed to “KnowledgePredator”.

So , we will wrap up this post here . Stay tuned for next posts !

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s