apexClassExplorer.html
<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
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);
}
}
apexClassExplorerController
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.xml
<?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>
