Display Contact and their related Cases in different component using Pub-Sub in LWC


In this post we will see how to communicate between components that are not in same DOM tree.By using a singleton library that follows the publish-subscribe pattern we can communicate.

Let see how we can communicate between two components using Pub-Sub for the below scenario

Component 1 : 
  • Display the contacts on selected account on account view page 
  • Adding row action (case list) in each contact
Component 2 :
  • Display the cases on selected contact when user click the row action (case list) in component 1.

Implementation:

Step 1:

First we need to copy the pubsub.js file from salesforce library and create the name LWC named "pubsub".


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


Component 1: 

contactsForm.html

<template>
<lightning-card class="slds-card slds-card_boundary related_list_card_border_top"
title="Contact view (Publisher Component)">

<!--- Contact Table-->
<div style="width: auto;">
<template if:true={emptyList}>
<lightning-datatable data={data} columns={columns} key-field="id" show-row-number-column
hide-checkbox-column="true" onrowaction={handleRowActions}></lightning-datatable>
</template>
<template if:false={emptyList}>
There is no contacts
</template>
</div> <br />
</lightning-card>
</template>

contactsForm.js

import { LightningElement, track, wire, api } from 'lwc';
//Import Apex methods
import getContatcs from '@salesforce/apex/ContactUtilities.getContacts';
import { CurrentPageReference } from 'lightning/navigation';
import { fireEvent } from 'c/pubsub';

// row actions
const actions = [
{ label: 'caseList', name: 'caseList' }
];

// datatable columns with row actions
const columns = [
{ label: 'First Name', fieldName: 'FirstName', type: "string", sortable: true },
{ label: 'Last Name', fieldName: 'LastName', type: "string", sortable: true },
{ label: 'Email', fieldName: 'Email', type: "email", sortable: true },
{
type: 'action',
typeAttributes: {
rowActions: actions,
menuAlignment: 'right'
}
}
];

export default class ContactPublisher extends LightningElement {

@wire(CurrentPageReference) pageRef;
// reactive variable
@api recordId;
@track data;
@track columns = columns;
@track record = [];
@track emptyList = false;

// retrieving the data using wire service
@wire(getContatcs, { sourceAccount: '$recordId' })
relations(result) {
this.refreshTable = result;
if (result.data) {
this.data = result.data;
this.emptyList = true;
}
}

//To handle the row actions
handleRowActions(event) {
let actionName = event.detail.action.name;
let row = event.detail.row;
// eslint-disable-next-line default-case
switch (actionName) {
case 'caseList':
this.caseListReturn(row);
break;
}
}

/* Method to fire the event */
caseListReturn(currentRow) {
fireEvent(this.pageRef, "currentRowId", currentRow.Id);
}

}


caseSubscriber.html

<template>
<lightning-card class="slds-card slds-card_boundary related_list_card_border_top"
title="Case View (Subscriber component )">
<!--- Case Table-->
<div style="width: auto;">
<template if:true={emptyList}>
<lightning-datatable data={data} columns={columns} key-field="id" show-row-number-column
hide-checkbox-column="true"></lightning-datatable>
</template>
<template if:false={emptyList}>
There is no case
</template>
</div> <br />
</lightning-card>
</template>


caseSubscriber.js

import { LightningElement, track, wire } from 'lwc';
//Import Apex methods
import getCases from '@salesforce/apex/ContactUtilities.getCases';
import { registerListener, unregisterAllListeners } from 'c/pubsub';
import { CurrentPageReference } from 'lightning/navigation';

// datatable columns with row actions
const columns = [
{ label: 'Origin', fieldName: 'Origin', type: "text", sortable: true },
{ label: 'Subject', fieldName: 'Subject', type: "text", sortable: true },
{ label: 'Description', fieldName: 'Description', type: "text", sortable: true }
];

export default class CaseSubscriber extends LightningElement {

@wire(CurrentPageReference) pageRef;
// reactive variable

@track data;
@track columns = columns;
@track record = [];
@track emptyList = false;
@track contactId = '';

connectedCallback() {
registerListener("currentRowId", this.getContact, this);
}

disconnectedCallback() {
unregisterAllListeners(this);
}

getContact(currentRecordId) {
this.contactId = currentRecordId;

}

@wire(getCases, { sourceContact: '$contactId' })
relations(result) {
this.refreshTable = result;
if (result.data) {
this.data = result.data;
this.emptyList = true;
}
}

}


Result :






No comments:

Post a Comment