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.