Wednesday, July 31, 2019

PubSub event in Lightning Web Component 


Description : Event is used when we have to send some data from one component/LWC to the other. 
When the relationship between two components is Parent-Child, we can send data directly using regular events, discussed in the post. But when we have the Sibling-relationships, i.e. the two components are not linked with each-other but one of them needs inputs from the other for some operations, we need the Pub-Sub (Publisher-Subscriber) event-model.

For pub-sub model to be executed, make sure both the components are on the same DOM.


--------------- pubSub.js (a stand-alone LWC) ---------------

/**
 * A basic pub-sub mechanism for sibling component communication
 *
 * TODO - adopt standard flexipage sibling communication mechanism 
        when it's available.
 *
 * Temporarily removing all the pageRef checking in pubsub - not 
    supported in communities
 * https://salesforce.stackexchange.com/questions/252021/
    lightning-web-component-use-publish-subscribe-event-in-community-cloud
 * https://gist.github.com/kmesic/262887799fb70be94707cb0b87936e7b
 */

const events = {};

/**
 * 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 = (eventNamecallbackthisArg=> {

    if (!events[eventName]) {
        events[eventName] = [];
    }

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

    if (!duplicate) {
        events[eventName].push({ callbackthisArg });
    }
};

/**
 * 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 = (eventNamecallbackthisArg=> {
    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 = (eventNamepayload=> {
    if (events[eventName]) {
        const listeners = events[eventName];
        listeners.forEach(listener => {
            try {
                listener.callback.call(listener.thisArgpayload);
            } catch (error) {
                // fail silently
            }
        });
    }
};
const utility = {
    navigateToPage: (pageNameurlParams = ""=> {
        window.open(`/er/s/${pageName}?${urlParams}`'_self');
    }
};

var sharedDataForLeads = {};
export {
    registerListener,
    unregisterListener,
    unregisterAllListeners,
    fireEvent,
    utility,
    sharedDataForLeads
};

--------------- Sibling 1 (firing the Event) ---------------

.html -->>

<!--firing the event at some onclick/onchange event : -->
<template>
    <lightning-card>
        <lightning-input value={datVal} onchange={setInputVal} 
            label="The msg\">
        </lightning-input>
    </lightning-card>
</template>
.js -->>

import { LightningElement } from 'lwc';
import { fireEvent } from 'c/pubSub';

export default class PubsubFireCmp extends LightningElement {

    datVal = '';

    setInputVal(event){
        this.datVal = event.detail.value;

        fireEvent('msg'this.datVal);
        //msg -> an arbitary event name
        console.log(" ->> event fired ");
    }
}


--------------- Sibling 2 (subscribing the Event) ---------------

.html -->>
<template>
    <lightning-card>
        The message is : {myMsg}
    </lightning-card>
</template>
.js -->>

import { LightningElementwire } from 'lwc';
import { CurrentPageReferenceNavigationMixin } from 
        'lightning/navigation';
import {  registerListener } from 'c/pubSub';

export default class PubsubHandleCmp extends 
                NavigationMixin(LightningElement) {

    @wire(CurrentPageReferencepageRef;

    connectedCallback() //the subscriber method to be added 
                        inside connectedCallback method
    {
        registerListener('msg',this.getVal,this);
        //'msg' is the same event name being fired
        //getval is the method using the event data for business operations
    }

    myMsg = '';
    getVal(val    //val is the data sent during the event
                    component
    {
        this.myMsgval;
        console.log('message '+this.myMsg);
    }
}

No comments:

Post a Comment