Scenarios where fired event is required to handle on the same application page however in different components which aren't directly/indirectly related?
Like in aura we have application event, LWC is enriched with Pubsub module. All we just need to import Pubsub module and use its listener methods.
Component Bundles:
- furnitureOnRentDemo (Parent Comp )
- furnitureDemo (Child Comp)
- pubsubDemo (Comp not in any relation but on same page)
- pubsub (Salesforce provided module)
** components are created as a part of proof of concept. it can be modified and optimized as per requirement.**
References:
- Manish Choudhary LWC learning videos [Available in Udemy]
Snippet:
Code:
furnitureOnRentDemo.html
<template>
<lightning-card title="Available Furnitures">
<lightning-layout>
<lightning-layout-item size="4" padding="around-small">
<ul>
<template for:each={furnitureinfo} for:item="furniture">
<li key={furniture.name} style="padding: 10px">
<c-furniture-demo furnitureinfo={furniture} ></c-furniture-demo>
</li>
</template>
</ul>
</lightning-layout-item>
</lightning-layout>
</lightning-card>
</template>
-------------------------------------------------------------------------------------------------------------------------
furnitureOnRentDemo.js
import { LightningElement, wire } from 'lwc';
import {CurrentPageReference} from 'lightning/navigation';
export default class FurnitureOnRentDemo extends LightningElement {
furnitureinfo=[
{name:'Crystal chairs',type:'chair'},
{name:'Poly tables',type:'table'},
{name:'Wooden furnitures',type:'sofa'},
{name:'Iron furnitures',type:'bed'}
];
}
-------------------------------------------------------------------------------------------------------------------------
furnitureDemo.html
<template>
<div class="slds-p-around_medium lgc-bg" onclick={tileClickHandler}>
<lightning-tile label={furnitureinfo.name} >
<p class="slds-truncate" title={furnitureinfo.type}>Furniture Type: {furnitureinfo.type}</p>
</lightning-tile>
</div>
</template>
-------------------------------------------------------------------------------------------------------------------------
furnitureDemo.js
import { LightningElement,api,wire } from 'lwc';
import {fireEvent} from 'c/pubsub';
import {CurrentPageReference} from 'lightning/navigation';
export default class FurnitureDemo extends LightningElement {
@api furnitureinfo;
@wire(CurrentPageReference) pageReference;
tileClickHandler(){
fireEvent(this.pageReference, 'selectedtile', this.furnitureinfo);
}
}
-------------------------------------------------------------------------------------------------------------------------
pubsubDemo.html
<template>
<lightning-card title="Selected Furniture">
You have seleteced : {selectedTile.name}
</br>
type : {selectedTile.type}
</lightning-card>
</template>
-------------------------------------------------------------------------------------------------------------------------
pubsubDemo.js
import { LightningElement,track,wire } from 'lwc';
import {registerListener, unregisterAllListeners} from 'c/pubsub';
import {CurrentPageReference} from 'lightning/navigation';
export default class PubsubDemo extends LightningElement {
@track selectedTile={};
@wire(CurrentPageReference) pageRef;
connectedCallback(){
registerListener('selectedtile', this.furnitureSelectHandler, this);
}
disconnectedCallback(){
unregisterAllListeners(this);
}
furnitureSelectHandler(payload){
this.selectedTile = payload;
}
}
-------------------------------------------------------------------------------------------------------------------------
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
};