Skip to content

LWC of Objects/Fields

Sample

Apex Class

ObjectSchemaController.cls
public with sharing class ObjectSchemaController {

@AuraEnabled(cacheable=true)
public static List<ObjectWrapper> getObjectList() {
    List<ObjectWrapper> objectList = new List<ObjectWrapper>();
    Map<String, Schema.SObjectType> globalDescribe = Schema.getGlobalDescribe();

    for(Schema.SObjectType objType : globalDescribe.values()){
        Schema.DescribeSObjectResult res = objType.getDescribe();
        objectList.add(new ObjectWrapper(res.getLabel(), res.getName(), res.isQueryable()));
    }
    objectList.sort();
    return objectList;
}

@AuraEnabled(cacheable=true)
public static List<FieldWrapper> getFieldsForObject(String objectApiName) {
    if (String.isBlank(objectApiName)) return new List<FieldWrapper>();

    List<FieldWrapper> fieldList = new List<FieldWrapper>();
    Schema.SObjectType t = Schema.getGlobalDescribe().get(objectApiName);
    if (t == null) return fieldList;

    Map<String, Schema.SObjectField> fieldMap = t.getDescribe().fields.getMap();

    for(Schema.SObjectField field : fieldMap.values()) {
        Schema.DescribeFieldResult f = field.getDescribe();
        fieldList.add(new FieldWrapper(
            f.getLabel(), 
            f.getName(), 
            String.valueOf(f.getType()),
            f.getLength(),
            f.isCalculated(),
            f.getCalculatedFormula(),
            f.isUnique(),
            f.isExternalId()
        ));
    }
    fieldList.sort();
    return fieldList;
}

public class ObjectWrapper implements Comparable {
    @AuraEnabled public String label;
    @AuraEnabled public String apiName;
    @AuraEnabled public Boolean isQueryable;

    public ObjectWrapper(String label, String apiName, Boolean isQueryable) {
        this.label = label;
        this.apiName = apiName;
        this.isQueryable = isQueryable;
    }

    public Integer compareTo(Object compareTo) {
        return this.label.compareTo(((ObjectWrapper)compareTo).label);
    }
}

public class FieldWrapper implements Comparable {
    @AuraEnabled public String label;
    @AuraEnabled public String apiName;
    @AuraEnabled public String type;
    @AuraEnabled public Integer length;
    @AuraEnabled public Boolean isFormula;
    @AuraEnabled public String formulaSql;
    @AuraEnabled public Boolean isUnique;
    @AuraEnabled public Boolean isExternalId;

    public FieldWrapper(String label, String apiName, String type, Integer length, 
                        Boolean isFormula, String formulaSql, Boolean isUnique, Boolean isExternalId) {
        this.label = label;
        this.apiName = apiName;
        this.type = type;
        this.length = length;
        this.isFormula = isFormula;
        this.formulaSql = (formulaSql == null) ? '' : formulaSql;
        this.isUnique = isUnique;
        this.isExternalId = isExternalId;
    }

    public Integer compareTo(Object compareTo) {
        return this.label.compareTo(((FieldWrapper)compareTo).label);
    }
}
}

Apex Test Class

ObjectSchemaControllerTest.cls
@isTest
private class ObjectSchemaControllerTest {
    @isTest
    static void testAllLogic() {
        Test.startTest();
        // Test Objects
        List<ObjectSchemaController.ObjectWrapper> objs = ObjectSchemaController.getObjectList();
        Assert.isFalse(objs.isEmpty(), 'Should find objects');

        // Test Fields
        List<ObjectSchemaController.FieldWrapper> fields = ObjectSchemaController.getFieldsForObject('Account');
        Assert.isFalse(fields.isEmpty(), 'Account should have fields');

        // Test Null/Empty handling
        Assert.areEqual(0, ObjectSchemaController.getFieldsForObject('').size());
        Test.stopTest();
    }

    @isTest
    static void testConstructorAndSort() {
        // Test Object Sort
        ObjectSchemaController.ObjectWrapper o1 = new ObjectSchemaController.ObjectWrapper('B', 'B', true);
        ObjectSchemaController.ObjectWrapper o2 = new ObjectSchemaController.ObjectWrapper('A', 'A', true);
        List<ObjectSchemaController.ObjectWrapper> oList = new List<ObjectSchemaController.ObjectWrapper>{o1, o2};
        oList.sort();
        Assert.areEqual('A', oList[0].label);

        // Test Field Sort (8 parameters)
        ObjectSchemaController.FieldWrapper f1 = new ObjectSchemaController.FieldWrapper('B','B','String',255,false,'',false,false);
        ObjectSchemaController.FieldWrapper f2 = new ObjectSchemaController.FieldWrapper('A','A','String',255,false,'',false,false);
        List<ObjectSchemaController.FieldWrapper> fList = new List<ObjectSchemaController.FieldWrapper>{f1, f2};
        fList.sort();
        Assert.areEqual('A', fList[0].label);
    }
}

LWC - Object Javascript

objectListExporter.js
import { LightningElement, wire, track } from 'lwc';
import getObjectList from '@salesforce/apex/ObjectSchemaController.getObjectList';

const COLUMNS = [
    { label: 'Object Label', fieldName: 'label', type: 'text' },
    { label: 'API Name', fieldName: 'apiName', type: 'text' },
    { label: 'Queryable', fieldName: 'isQueryable', type: 'boolean' }
];

export default class ObjectListExporter extends LightningElement {
    @track objects = [];
    columns = COLUMNS;

    @wire(getObjectList)
    wiredObjects({ error, data }) {
        if (data) {
            this.objects = data;
        } else if (error) {
            console.error('Error fetching objects:', error);
        }
    }

    // Logic for the HTML button disabled attribute
    get isDownloadDisabled() {
        return !this.objects || this.objects.length === 0;
    }

    downloadCSV() {
        console.log('Object Export Button Clicked');
        try {
            const headers = ['Object Label', 'API Name', 'Is Queryable'];
            let csvContent = headers.join(',') + '\n';

            this.objects.forEach(obj => {
                let row = [
                    `"${obj.label || ''}"`,
                    `"${obj.apiName || ''}"`,
                    obj.isQueryable
                ];
                csvContent += row.join(',') + '\n';
            });

            this.executeDownload(csvContent, 'All_Org_Objects.csv');
        } catch (error) {
            console.error('Export Failed:', error);
        }
    }

    executeDownload(csvContent, fileName) {
    try {
        // 1. Encode the CSV string to Base64 to bypass LWS MIME type restrictions
        // btoa() creates a base-64 encoded ASCII string from a "string" of binary data
        const base64Data = btoa(unescape(encodeURIComponent(csvContent)));
        const dataUri = 'data:text/csv;base64,' + base64Data;

        // 2. Create the download link
        const link = document.createElement('a');
        link.href = dataUri;
        link.download = fileName;

        // 3. Trigger the download
        document.body.appendChild(link);
        link.click();
        document.body.removeChild(link);

        console.log('Download successful via Base64/DataURI');
    } catch (err) {
        console.error('LWS Download Error: ', err);

        // Fallback for very simple browsers if Base64 fails
        const encodedUri = 'data:text/csv;charset=utf-8,' + encodeURIComponent(csvContent);
        const link = document.createElement('a');
        link.setAttribute('href', encodedUri);
        link.setAttribute('download', fileName);
        document.body.appendChild(link);
        link.click();
        document.body.removeChild(link);
        }
    }
}

LWC - Object HTML

objectListExporter.html
<template>
    <lightning-card title="Org Object Metadata" icon-name="standard:multi_select_checkbox">
        <lightning-button 
            slot="actions" 
            label="Download as CSV" 
            icon-name="utility:download" 
            onclick={downloadCSV}>
        </lightning-button>

        <div class="slds-var-p-around_medium">
            <div style="height: 600px;">
                <lightning-datatable
                    key-field="apiName"
                    data={objects}
                    columns={columns}
                    hide-checkbox-column="true">
                </lightning-datatable>
            </div>
        </div>
    </lightning-card>
</template>

LWC - Field Javascript

fieldListExporter.js
import { LightningElement, wire, track } from 'lwc';
import getObjectList from '@salesforce/apex/ObjectSchemaController.getObjectList';
import getFieldsForObject from '@salesforce/apex/ObjectSchemaController.getFieldsForObject';

const COLUMNS = [
    { label: 'Label', fieldName: 'label', type: 'text', initialWidth: 160 },
    { label: 'API Name', fieldName: 'apiName', type: 'text', initialWidth: 160 },
    { label: 'Type', fieldName: 'type', type: 'text', initialWidth: 100 },
    { label: 'Len', fieldName: 'length', type: 'number', initialWidth: 70 },
    { label: 'Formula', fieldName: 'isFormula', type: 'boolean', initialWidth: 80 },
    { label: 'Formula SQL', fieldName: 'formulaSql', type: 'text', initialWidth: 250 },
    { label: 'Unique', fieldName: 'isUnique', type: 'boolean', initialWidth: 80 },
    { label: 'Ext ID', fieldName: 'isExternalId', type: 'boolean', initialWidth: 80 }
];

export default class FieldListExporter extends LightningElement {
    @track selectedObject = '';
    @track objectOptions = [];
    @track fields = [];
    columns = COLUMNS;

    @wire(getObjectList)
    wiredObjects({ error, data }) {
        if (data) {
            this.objectOptions = data.map(obj => ({ label: obj.label, value: obj.apiName }));
        }
    }

    @wire(getFieldsForObject, { objectApiName: '$selectedObject' })
    wiredFields({ error, data }) {
        if (data) {
            this.fields = data;
        } else {
            this.fields = [];
        }
    }

    get isDownloadDisabled() {
        return !this.fields || this.fields.length === 0;
    }

    handleObjectChange(event) {
        this.selectedObject = event.detail.value;
    }

    downloadCSV() {
        console.log('Field Export Button Clicked for: ' + this.selectedObject);
        try {
            const headers = ['Label', 'API Name', 'Type', 'Length', 'Is Formula', 'Formula SQL', 'Unique', 'External ID'];
            let csvContent = headers.join(',') + '\n';

            this.fields.forEach(f => {
                let row = [
                    `"${f.label || ''}"`,
                    `"${f.apiName || ''}"`,
                    `"${f.type || ''}"`,
                    f.length,
                    f.isFormula,
                    `"${f.formulaSql ? f.formulaSql.replace(/"/g, '""') : ''}"`,
                    f.isUnique,
                    f.isExternalId
                ];
                csvContent += row.join(',') + '\n';
            });

            this.executeDownload(csvContent, `${this.selectedObject}_Fields.csv`);
        } catch (error) {
            console.error('Field Export Failed:', error);
        }
    }

    executeDownload(csvContent, fileName) {
    try {
        // 1. Encode the CSV string to Base64 to bypass LWS MIME type restrictions
        // btoa() creates a base-64 encoded ASCII string from a "string" of binary data
        const base64Data = btoa(unescape(encodeURIComponent(csvContent)));
        const dataUri = 'data:text/csv;base64,' + base64Data;

        // 2. Create the download link
        const link = document.createElement('a');
        link.href = dataUri;
        link.download = fileName;

        // 3. Trigger the download
        document.body.appendChild(link);
        link.click();
        document.body.removeChild(link);

        console.log('Download successful via Base64/DataURI');
    } catch (err) {
        console.error('LWS Download Error: ', err);

        // Fallback for very simple browsers if Base64 fails
        const encodedUri = 'data:text/csv;charset=utf-8,' + encodeURIComponent(csvContent);
        const link = document.createElement('a');
        link.setAttribute('href', encodedUri);
        link.setAttribute('download', fileName);
        document.body.appendChild(link);
        link.click();
        document.body.removeChild(link);
        }
    }
}

LWC - Field HTML

fieldListExporter.hml
<template>
    <lightning-card title="Field List Exporter" icon-name="standard:field_sales">
        <lightning-button 
            slot="actions" 
            label="Download as CSV" 
            icon-name="utility:download" 
            onclick={downloadCSV}
            disabled={isDownloadDisabled}>
        </lightning-button>

        <div class="slds-var-p-around_medium">
            <lightning-combobox
                name="objectSelector"
                label="Select an Object"
                value={selectedObject}
                placeholder="Search for an object..."
                options={objectOptions}
                onchange={handleObjectChange}
                class="slds-var-m-bottom_medium">
            </lightning-combobox>

            <div style="height: 500px;">
                <lightning-datatable
                    key-field="apiName"
                    data={fields}
                    columns={columns}
                    hide-checkbox-column="true">
                </lightning-datatable>
            </div>
        </div>
    </lightning-card>
</template>