Handling Events in Lightning Web Components – Publish-Subscribe

It is very common to come across use case of communicating between Lightning Web Components.

If you are not interested on code explanation and directly want to jump on live demo , check this playground.

Establish Communication Among Enclosed Lightning Web Components

This scenario is among the simplest and most common, where the parent LWC needs to respond to an event triggered by the child LWC.

In the image provided, Model 3 functions as the child (nested component) within Tesla.

div{
    background-color: #ddd; 
}
<template>
    <div onclick={customClick}>
        This is Model 3 Component , Click Me -  {log}
    </div>
</template>
import { LightningElement, track } from 'lwc';
export default class Model3 extends LightningElement {
    @track  log = '';
    customClick() {
        const event = new CustomEvent('modelclick', {
            // detail contains only primitives
            detail: 'Event Started in Tesla Model 3'
        });
        // Fire the event from model 3
        this.dispatchEvent(event);
        this.log = ' - Model 3 clicked';
    }
}

In the Model 3 component above, the custom event “modelclick” is triggered using the CustomEvent class.

Let’s explore how the Parent Component Tesla is structured.

div{
    background-color: #cccccc;
}
<template>
    <div >
        This is Tesla Component -  {log} 
    </div>
    <br /> 
     <c-model3 onmodelclick={modelClick}></c-model3>
</template>
import { LightningElement, track } from 'lwc';
export default class Child1 extends LightningElement {
    @track  log = ''; 
    modelClick(e){
        this.log = this.log + ' Captured event - '+ e.detail+' ';
         const event = new CustomEvent('modelclick', {
            // detail contains only primitives
            detail: 'Event Started in Tesla'
        });
        // Fire the event from model 3
        this.dispatchEvent(event);
    }
}

In the parent component Tesla above, the event triggered by the child component is managed similarly to any other JavaScript event (bubbling event). Within the onmodelclick function, Tesla also initiates a new event with the same name – “modelclick”.

That wraps up the explanation of how communication takes place between Parent and Child Lightning Web Components.

Handling Events in Lightning Web Components – Pub-Sub Framework

In above image, if we want Tesla event to be handled by Honda Component, traditional bubble based event handling will not work.

You can either use Salesforce library of Pub Sub, or Simple Library produced I’m going to use library created  Below is Javascript code for pub-sub library

const callbacks = {};

/**
 * 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.
 */
const register = (eventName, callback) => {
    if (!callbacks[eventName]) {
        callbacks[eventName] = new Set();
    }
    callbacks[eventName].add(callback);
};

/**
 * Unregisters a callback for an event
 * @param {string} eventName - Name of the event to unregister from.
 * @param {function} callback - Function to unregister.
 */
const unregister = (eventName, callback) => {
    if (callbacks[eventName]) {
        callbacks[eventName].delete(callback);
    }
};

/**
 * Fires an event to listeners.
 * @param {string} eventName - Name of the event to fire.
 * @param {*} payload - Payload of the event to fire.
 */
const fire = (eventName, payload) => {
    if (callbacks[eventName]) {
        callbacks[eventName].forEach(callback => {
            try {
                callback(payload);
            } catch (error) {
                // fail silently
            }
        });
    }
};

export default {
    register,
    unregister,
    fire
};

Now that we’ve included the aforementioned JavaScript as part of the LWC, let’s generate a Lightning Web Component named “Vehicle” that will trigger a pub-sub event.

.ele1{
    background-color: #BBB;
    padding:50px;
    border-radius: 25px;
    margin-top:20px;
    margin-left:10px;
    margin-right:10px;
    cursor: pointer;
}

.subComp{
    background-color: #fff;
    border-radius: 25px;
    margin:10px;
    padding:30px;
}
<template>
    <div class="ele1">
        Vehicle Component -  {log}
        <div class="subComp">
             <c-tesla  
                onmodelclick={captEvent}></c-tesla>  
        </div>
        <div class="subComp">
            <c-honda> </c-honda> 
        </div>
    </div>  
</template>
import { LightningElement, track } from 'lwc';
import pubsub from 'c/pubsub' ; 
export default class Container extends LightningElement {
    @track log = '';  
    captEvent(evt){
        this.log = this.log+' '+evt.detail;
        pubsub.fire('uniqueEventId', evt.detail);
    }
}

Take note of how the pubsub.js is imported in line 2 of Vehicle.js. In line 7, an event is triggered using the pub-sub library.

Now, let’s develop a Lightning Web Component designed to manage events generated by the pub-sub library.

<template>
    This is Honda Component <br />
    {data}
</template>
import { LightningElement, track,api } from 'lwc';
import pubsub from 'c/pubsub' ; 
export default class Honda extends LightningElement {
    @track data; 
    eventFiredCallback;
    eventFired(event){ 
        this.data=event  ;   
    }
    connectedCallback(){
        this.data='callback' ; 
        this.eventFiredCallback = this.eventFired.bind(this); 
        this.register();
    }
    @api
    register(){
        this.data+=' - register' ;
        pubsub.register('uniqueEventId', this.eventFiredCallback ); 
    } 
}

As observed in the code above, the register method is utilized to manage the event. Additionally, notice how a unique event identifier is employed for both firing and registering the event.

Please feel free to comment and share your feedback. Happy coding!