Site icon Salesforce Diaries

Apex Class Explorer in Custom Lightning Web Component

Apex Class Explorer

You have been asked to give profile level security for an apex class in your Salesforce org but you find a hard time to find it as there are thousands of apex class. This blog is going to provide you a native solution which can be used in Home page, Lightning App Page or Record page to allow to quickly find the apex class with lookup option.

Solution Design – Apex Class Explorer

We are going to have a Lightning Web Component and Apex Class controller. LWC has a search input and client side handler function bind to it defined in js file. The function invoke an apex method which receives the search term as input parameter and return list of apex class.

Metadata List – Apex Class Explorer

apexClassExplorerController

fetchApexClass returns list of apex class and accept string input parameter.

public with sharing class apexClassExplorerController {
    @AuraEnabled
    public static List<ApexClass> fetchApexClass(String searchKey){
        String key = '%' + searchKey + '%';
        try {
            return [SELECT Id, NamespacePrefix, Name, ApiVersion, Status, IsValid, BodyCrc, Body, LengthWithoutComments, CreatedDate, CreatedBy.Name, LastModifiedDate, LastModifiedBy.Name, SystemModstamp FROM ApexClass WHERE Name LIKE :key];
        } catch (Exception e) {
            throw new AuraHandledException(e.getMessage());
        }
    }
}

apexClassExplorer.html

The HTML file has lightning input of type search and an iterator which iterate through the apex class list returned by apex controller of the Lightning Web Component. HTML file also has buttons to edit, view the code and hide the inline apex code of selected apex class.

<template>
    <lightning-card title="Apex Class Search" icon-name="custom:custom57">
        <div class="slds-var-m-around_medium">
            <lightning-input type="search" onchange={handleKeyChange}
                class="slds-show slds-is-relative slds-var-m-bottom_small" label="Search">
            </lightning-input>
            <template if:true={apexClasses}>
                <template for:each={apexClasses} for:item="apex">
                    <lightning-card key={apex.Id}>
                        <h3 slot="title">
                            <lightning-icon icon-name="doctype:xml" alternative-text="XML file" title="XML">
                            </lightning-icon>
                            {apex.Name}
                        </h3>
                        <lightning-button label="Edit" onclick={handleEdit} slot="actions" data-url={apex.Id}>
                        </lightning-button>
                        <lightning-button label="View" onclick={handleView} data-id={apex.Id} slot="actions">
                        </lightning-button>
                        <lightning-button if:true={apex.showCode} label="Hide" onclick={handleHide} data-id={apex.Id}
                            slot="actions">
                        </lightning-button>
                        <div class="slds-scrollable">
                            <div class="slds-text-longform">
                                <div class="slds-page-header__row slds-page-header__row_gutters">
                                    <div class="slds-page-header__col-details">
                                        <ul class="slds-page-header__detail-row" style="list-style: none;">
                                            <li class="slds-page-header__detail-block">
                                                <div class="slds-text-title slds-truncate" title="Api Version">Api
                                                    Version
                                                </div>
                                                <div class="slds-truncate" title={apex.ApiVersion}>{apex.ApiVersion}
                                                </div>
                                            </li>
                                            <li class="slds-page-header__detail-block">
                                                <div class="slds-text-title slds-truncate" title="Status">Status</div>
                                                <div class="slds-truncate" title={apex.Status}>{apex.Status}</div>
                                            </li>
                                            <li class="slds-page-header__detail-block">
                                                <div class="slds-text-title slds-truncate" title="Namespace Prefix">
                                                    Namespace Prefix
                                                </div>
                                                <div class="slds-truncate" title={apex.NamespacePrefix}>
                                                    {apex.NamespacePrefix}
                                                </div>
                                            </li>
                                            <li class="slds-page-header__detail-block">
                                                <div class="slds-text-title slds-truncate" title="IsValid">Created Date
                                                </div>
                                                <div class="slds-truncate" title={apex.IsValid}>{apex.CreatedDate}</div>
                                            </li>
                                            <li class="slds-page-header__detail-block">
                                                <div class="slds-text-title slds-truncate" title="IsValid">Created By
                                                    Name
                                                </div>
                                                <div class="slds-truncate" title={apex.IsValid}>{apex.CreatedBy.Name}
                                                </div>
                                            </li>
                                            <li class="slds-page-header__detail-block">
                                                <div class="slds-text-title slds-truncate" title="IsValid">Last Modified
                                                    Date
                                                </div>
                                                <div class="slds-truncate" title={apex.IsValid}>{apex.LastModifiedDate}
                                                </div>
                                            </li>
                                            <li class="slds-page-header__detail-block">
                                                <div class="slds-text-title slds-truncate" title="IsValid">Last Modified
                                                    By Name
                                                </div>
                                                <div class="slds-truncate" title={apex.IsValid}>
                                                    {apex.LastModifiedBy.Name}
                                                </div>
                                            </li>
                                        </ul>
                                    </div>
                                </div>
                                <blockquote if:true={apex.showCode}>
                                    <pre>
                                        <code class="language-html">
                                            {apex.Body}
                                        </code>
                                    </pre>
                                </blockquote>
                            </div>
                        </div>
                    </lightning-card>
                </template>
            </template>
        </div>
    </lightning-card>
</template>

ApexClassExplorer.js

JavaScript file of the Lightning Web Component has below methods:-

import { LightningElement, track } from 'lwc';
import fetchApexClass from '@salesforce/apex/apexClassExplorerController.fetchApexClass';
/** The delay used when debouncing event handlers before invoking Apex. */
const DELAY = 350;
export default class ApexClassExplorer extends LightningElement {
    @track apexClasses;
    error;

    handleKeyChange(event) {
        // Debouncing this method: Do not actually invoke the Apex call as long as this function is
        // being called within a delay of DELAY. This is to avoid a very large number of Apex method calls.
        window.clearTimeout(this.delayTimeout);
        const searchKey = event.target.value;
        // eslint-disable-next-line @lwc/lwc/no-async-operation
        this.delayTimeout = setTimeout(() => {
            fetchApexClass({ searchKey: searchKey })
                .then((result) => {
                    result.forEach(element => {
                        element.showCode = false;
                    });
                    this.apexClasses = result;
                    this.error = undefined;
                })
                .catch((error) => {
                    this.error = error;
                    this.apexClasses = undefined;
                });
        }, DELAY);
    }

    handleView(event) {
        const dataid = event.target.dataset.id;
        this.apexClasses = this.apexClasses.map(x => {
            if (x.Id == dataid) {
                x.showCode = true;
            }
            return x;
        });
    }

    handleHide(event) {
        const dataid = event.target.dataset.id;
        this.apexClasses = this.apexClasses.map(x => {
            if (x.Id == dataid) {
                x.showCode = false;
            }
            return x;
        });
    }

    handleEdit(event) {
        const url = window.location.origin + '/' + event.target.dataset.url;
        window.open(url);
    }
}

ApexClassExplorer.js-meta.xml

XML file of the component has target defined to expose it to Lightning App Builder for home, app and record page.

<?xml version="1.0" encoding="UTF-8"?>
<LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata">
    <apiVersion>52.0</apiVersion>
    <isExposed>true</isExposed>
    <targets>
        <target>lightning__AppPage</target>
        <target>lightning__RecordPage</target>
        <target>lightning__HomePage</target>
    </targets>
</LightningComponentBundle>

Demo- Apex Class Explorer

Keep checking this for more update on Lightning Web Component scenarios.

Exit mobile version