This is just a simple use case which I used it to learn some basic concepts of LWC. The use case “I want to have an overview of opportunities; we can make actions faster.”

Custom Metadata Type is a new type of setting similar to List Custom Setting. You can find the information of Custom Metadata Type here: https://help.salesforce.com/articleView?id=custommetadatatypes_about.htm&type=5
- Create a Custom Metadata Type to specify Filter Condition.


- We create some of the settings

- The Filter Condition is what we want to filter here. It should be the condition in SOQL after the where. We can see this example; we created a Active Opportunities with condition StageName != ‘Closed Won’
- The Apex Controller which has two methods to query all Opp State Settings and the number of each state using count(id)
public with sharing class OppStateController {
public OppStateController() {
}
//Query and returned mdt
@AuraEnabled(cacheable = true)
public static list<Opp_State_Setting__mdt> getOppStateSettings(){
list<Opp_State_Setting__mdt> stateList = [Select id, MasterLabel, Background_Color__c, Filter_Condition__c from Opp_State_Setting__mdt limit 100];
return stateList;
}
@AuraEnabled
public static integer getOppCount(string filter){
system.debug('qufiltersery ' + filter);
string query = 'select count(id) NOP from Opportunity';
if(String.isNotEmpty(filter)){
query += ' Where '+filter;
}
List<AggregateResult> aggs = Database.query(query);
if(aggs.isEmpty()) return 0;
AggregateResult agg = aggs[0];
return (Integer)agg.get('NOP');
}
}
- From here, We will create 2 LWC, oppState and list of oppStates.
oppState :
–oppState.css
.state-tile {
border: 1px solid #d2955d;
border-radius: .25rem;
display: block;
}
.state-silhouette {
height:100px;
}
–oppState.html
<template>
<lightning-card variant="Narrow" title={stateSetting.MasterLabel} class="state-tile" onclick={handleStateClick}>
<div class="slds-p-horizontal_small" >
<div class="slds-media">
<div class="slds-media__body">
<p class="slds-align_absolute-center">
{numberOfOpp}
</p>
</div>
</div>
</div>
</lightning-card>
</template>
–oppState.js
import { LightningElement,api,track } from 'lwc';
import getNOP from '@salesforce/apex/OppStateController.getOppCount';
export default class OppState extends LightningElement {
@api stateSetting;
@track numberOfOpp = 0;
handleStateClick(event) {
event.preventDefault();
const stateClickEvent = new CustomEvent('selected',{detail:this.stateSetting});
// eslint-disable-next-line no-console
console.log("click here ",this.stateSetting.MasterLabel);
this.dispatchEvent(stateClickEvent)
}
connectedCallback(){
getNOP({'filter':this.stateSetting.Filter_Condition__c})
.then(results => {
this.numberOfOpp = results;
})
.catch()
}
}
–oppState.js-meta.xml
<?xml version="1.0" encoding="UTF-8"?>
<LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata">
<apiVersion>48.0</apiVersion>
<isExposed>false</isExposed>
</LightningComponentBundle>
oppStateList:
–oppStateList.html
<template>
<lightning-card title="States" icon-name="action:join_group">
<div class="slds-card__body_inner">
<lightning-layout multiple-rows="true" pull-to-boundary="small">
<template for:each={oppStates.data} for:item="oppState">
<lightning-layout-item key={oppState.Id} size="3" class="slds-p-around_x-small">
<c-opp-state state-setting={oppState} onselected={handleStateSelected}></c-opp-state>
</lightning-layout-item>
</template>
</lightning-layout>
</div>
</lightning-card>
</template>
–oppStateList.js
import { LightningElement,track,wire } from 'lwc';
import getOppStateSettings from "@salesforce/apex/OppStateController.getOppStateSettings";
import {CurrentPageReference} from "lightning/navigation";
import {fireEvent} from "c/pubsub";
export default class OppStateList extends LightningElement {
@track stateSettings;
@track error;
@wire(getOppStateSettings) oppStates;
@wire(CurrentPageReference) pageRef;
handleStateSelected(event){
fireEvent(this.pageRef, 'stateSelected',event.detail);
}
}
–oppStateList.js-meta.xml
<?xml version="1.0" encoding="UTF-8"?>
<LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata">
<apiVersion>48.0</apiVersion>
<isExposed>true</isExposed>
<targets>
<target>lightning__AppPage</target>
<target>lightning__RecordPage</target>
<target>lightning__HomePage</target>
</targets>
</LightningComponentBundle>
pubsub:
–pubsub.js
/**
* 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
};
–pubsub.js-meta.xml
<?xml version="1.0" encoding="UTF-8"?>
<LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata" fqn="pubsub">
<apiVersion>45.0</apiVersion>
<isExposed>false</isExposed>
</LightningComponentBundle>
- Add the oppStateList to any Lightning Page which you see fit.
I use and leave the pubsub there; You can define and catch the event on whenever you click on a state. In my case, I displayed a list of opportunity using the same filter condition.
If you have any question let us know. Happy Coding.