Passing values across the components using pub-sub (LWC)
I came across a question in my recent interview : If we want to pass values from component A to B to C where all 3 components are not in a relationship and in a same page , what can be the best way ? pub-sub or Messaging -Service . After I try to implement the same , it turns out that both can do that job.
PubSub : So We will create 3 components one having Country picklist , 2nd State picklist and 3rd Capital picklist and try to send information when picklist values changes across these components .
Country Component
import { LightningElement, wire } from 'lwc';
import { CurrentPageReference } from 'lightning/navigation';
import { fireEvent } from 'c/pubsub';
export default class MyPublisher extends LightningElement {
@wire(CurrentPageReference) pageRef;
selectedCountry;
options = [
{label : 'India' , value : 'India'},
{label : 'Canada' , value : 'Canada'}
]
handlePicklistChange(event){
this.selectedCountry = event.target.value;
fireEvent(this.pageRef , "selectedCountry" ,this.selectedCountry );
}
}
State Component
import { LightningElement , wire , track } from 'lwc';
import { registerListener, unregisterAllListeners } from 'c/pubsub';
import { CurrentPageReference } from 'lightning/navigation';
import { fireEvent } from 'c/pubsub';
export default class MySubscriber extends LightningElement {
@track details;
@wire(CurrentPageReference) pageRef;
selectedCountry;
selectedState;
connectedCallback() {
registerListener("selectedCountry" , this.updateCountry , this);
}
updateCountry(country){
this.selectedCountry = country;
}
countryOptions = [
{label : 'India' , value : 'India'},
{label : 'Canada' , value : 'Canada'}
]
stateOptions = [
{label : 'Uttarakhand' , value : 'Uttarakhand'},
{label : 'Bristish Columbia' , value : 'Bristish Columbia'}
]
handleStateChange(event){
this.selectedState = event.target.value;
fireEvent(this.pageRef , "selectedState" ,this.selectedState );
}
disconnectedCallback() {
unregisterAllListeners(this);
}
}
Capital Component
import { LightningElement , wire , track } from 'lwc';
import { registerListener, unregisterAllListeners } from 'c/pubsub';
import { CurrentPageReference } from 'lightning/navigation';
export default class CapitalComponent extends LightningElement {
@track details;
@wire(CurrentPageReference) pageRef;
selectedCountry;
selectedState;
selectedCapital;
connectedCallback() {
registerListener("selectedState" , this.updateState , this);
}
updateCountry(country){
this.selectedCountry = country;
}
updateState(state){
this.selectedState = state;
}
capitalOptions = [
{label : 'Dehradun' , value : 'Dehradun'},
{label : 'Chandigarh' , value : 'Chandigarh'}
]
handleCountryChange(event){
this.selectedCountry = event.target.value;
}
handleStateChange(event){
this.selectedState = event.target.value;
}
handleCapitalChange(event){
this.selectedCapital = event.target.value;
}
disconnectedCallback() {
unregisterAllListeners(this);
}
}
And also remember you need to create a pub-sub file : > so under LWC : create folder : pubsub : and a file : pubsub.js and paste below code
/**
* 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
};
Subscribe to my newsletter
Read articles from Kapil Chauhan directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by