Kishore
Kishore I am a Salesforce developer and a so-called blogger at SalesforceLwc.in. I love learning and sharing. SalesforceLWC aims to share relevant knowledge, examples and scenarios for understanding Salesforce Lightning Web Components and AURA.

Publish–Subscribe Pattern in Lightning Web Components (pubsub)


Publish–Subscribe Pattern in Lightning Web Components (pubsub)

How do components communicate with each other

To communicate between components that are not in the same DOM tree, we use a singleton library that follows the publish-subscribe pattern.

In a publish-subscribe pattern, one component publishes an event while the other components subscribe to receive and handle the event. Every component that subscribes to the event receives the event. Let’s say, you have two components to a Lightning page in App Builder, then use the pubsub module to send events between them. Pubsub is not given by default, you have to add it to by yourself. You can get the file from here.

PubSub file

pubSub.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
/**
 * A basic pub-sub mechanism for sibling component communication
 *
 * TODO - adopt standard flexipage sibling communication mechanism when it's available.
 */

const events = {};

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
};


Fire an event

To fire an even using Publish–Subscribe Pattern. In the js file of the component from where you want to fire the event, import the two lines shown below

1
2
3
4
import{CurrentPageReference}
      from 'lightning/navigation';
import { fireEvent } 
       from 'c/pubsub';


Whenever the required condition is met then use the following code to fire the even.

1
2
3
4
5
6
fireEvent(this.pageRef,
     'restaurantListUpdate',
       this.message);


fireEvent({pageReference}, {eventName}, {data})


Subscribe and Receive the Event

In the component where you want to subscribe and receive event, import the following,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import{CurrentPageReference} 
   from 'lightning/navigation';
import { registerListener,
   unregisterAllListeners } 
   from 'c/pubsub';

connectedCallback() {
registerListener('restaurantListUpdate' 
             ,this.handleRestaurants, 
             this);
}

disconnectedCallback() {
   unregisterAllListeners(this);
}


In connectedCallback we subscribe to the event, In disconnectedCallback we un-subscribe.

Hope this post helped you gain some knowledge, If you like the content please don’t step back to like my page and leave your feedback, It will motivate me to make more posts.

Subscribe to get the latest updates directly in your inbox.