Salesforce Interview Questions & Answers Book is now available!

Grab Now

LWC

Custom Data Table in LWC

Custom Data table in LWC is a solution to a lot of things. We will look at creating a table that fetches fields dynamically based on our selection.

By Teja, ex-Salesforce engineer · Updated April 2, 2020 · 2 min read

I say this time and again, Lightning Data Tables are awesome, but there are a few caveats that give us no other option other than creating our own Custom Data Tables in LWC.

What are Data Tables in LWC?

It's a standard component given to us by the framework which we can use when we want to display the content in tabular format. The beauty of this component is we can make a lot of changes to the table just by altering the JSON input given to the tag.

Conceptually you can think of these as Page Block Tables in visualforce, with the only difference that Data Tables in LWC are built using a different architecture altogether.

Why Custom Data Tables in LWC?

We cannot have pick-list fields, lookup fields, dynamically fetching rows and etc in Lightning Data Tables. In those scenarios, we will end up using Custom Data Tables in LWC.

Any other alternative to Custom Data Tables in LWC?

In this example, I have just played around with the JSON input given to the tag and I have also used the standard <lightning-datatable></lightning-datatable> tag.

We can also create our own component which takes care of displaying the data in tabular format using <table></table> & <tr></tr><td><td/> tags.

In this post, we will look at how to create dynamic data tables that fetch the list of fields that we select from a multi-select pick-list.

We will start off with the Apex class.

In case you are interested in knowing how to use @AuraEnabled(cacheable=true) method and Schema.getGlobalDescribe() I have written a separate blog post, check it out.

public with sharing class ExploreCustomContactController {
    public ExploreCustomContactController() {

    }

    @AuraEnabled
    public static Map<String, String> getFieldTypes(List<String> selectedFields){
        String objectName = 'Contact';
        Map<String, String> fieldTypeMap = new Map<String, String>();

        for(String field : selectedFields){

            Schema.DisplayType fieldType = Schema.getGlobalDescribe().get(objectName).getDescribe().fields.getMap().get(field).getDescribe().getType();
            fieldTypeMap.put(field, String.valueOf(fieldType));
        }
        return fieldTypeMap;
    }
    
    @AuraEnabled(cacheable=true)
    public static List<Contact> getContacts(List<String> selectedFields){
        String s = '';
        Integer i = 0;

        for(String field : selectedFields){
            if(i == 0){
                s+= field;
            }else{
                s+= ','+field;
            }
            i++;

        }
        String query = 'SELECT ' + s + ' FROM Contact';
        
        return Database.query(query);
    }

    @AuraEnabled(cacheable=true)
    public static List<String> getContactFields(){
        SObjectType contactType = Schema.getGlobalDescribe().get('Contact');
        Map<String,Schema.SObjectField> mfields = contactType.getDescribe().fields.getMap();

        List<String> sObjectDetailsList = new List<String>();
        for(String s : mfields.keySet()){
            sObjectDetailsList.add(String.valueOf(mfields.get(s)));
        }
        return sObjectDetailsList;
    }
}

Then the interesting part is going to be the JS file, as we will be doing a lot of magic there.

import { LightningElement, track, wire } from 'lwc';
import getContacts from '@salesforce/apex/ExploreCustomContactController.getContacts';
import getContactFields from '@salesforce/apex/ExploreCustomContactController.getContactFields';
import getFieldTypes from '@salesforce/apex/ExploreCustomContactController.getFieldTypes';

export default class ExploreReimplementDataTable extends LightningElement {
    @track columns = [
        { label: 'Id', fieldName: 'Id' },
        { label: 'Name', fieldName: 'Name' }
    ];
    @track selected = ['Id', 'Name'];
    @track fetchedContacts = [];

    @wire(getContactFields) wiredContactFields;
    @wire(getContacts, { selectedFields: '$selected' })
    wiredContacts({ error, data }) {
        if (data) {
            this.fetchedContacts = data;
        } else if (error) {
            console.log(error);
        }
    }

    handleSelectedFields(event) {
        //clean the array
        this.selected.splice(0, this.selected.length);
        this.fetchedContacts = [];
        this.columns = [];

        for (var option of this.template.querySelector('select').options) {
            if (option.selected) {
                this.selected.push(option.value);
            }
        }

        getContacts({ selectedFields: this.selected })
            .then(result => {
                this.fetchedContacts = result;
                this.handleFetchFieldTypes();
            })
            .catch(error => {
                console.log(error);
            });
    }

    handleFetchFieldTypes() {
        getFieldTypes({ selectedFields: this.selected })
            .then(result => {
                this.columns = this.selected.map(field => {
                    const dType = result[field];
                    if (dType === 'STRING' || dType === 'ID') {
                        return { label: field, fieldName: field };
                    } else if (dType === 'DATE') {
                        return { label: field, fieldName: field, type: 'date' };
                    } else if (dType === 'DATETIME') {
                        return {
                            label: field,
                            fieldName: field,
                            type: 'datetime'
                        };
                    } else if (dType === 'Integer') {
                        return {
                            label: field,
                            fieldName: field,
                            type: 'Integer'
                        };
                    } else if (dType === 'BOOLEAN') {
                        return {
                            label: field,
                            fieldName: field,
                            type: 'text'
                        };
                    } else {
                        return { label: field, fieldName: field };
                    }
                });
            })
            .catch(error => {
                console.log(error);
            });
    }
}

And finally comes the template file.

<template>
    <label>Select the fields you want to display in the Data Table</label>
    <select class="slds-select" name="optionSelect" multiple>
        <template for:each={wiredContactFields.data} for:item="field">
            <option key={field} value={field}>{field}</option>
        </template>
    </select>

    <lightning-button
        label="Looks Fine"
        onclick={handleSelectedFields}
    ></lightning-button>

    <lightning-datatable
        key-field="id"
        data={fetchedContacts}
        columns={columns}
    >
    </lightning-datatable>
</template>

Here is the source code.

I have written a whole bunch of articles on LWC and in case you are interested you can check this out.

Hope this helps!


Tip

Grab your FREE course

I have put together a FREE course on Javascript that helps you master Lightning Web Components.

It just takes 2 hours to finish the course and you will be able to feel the difference.

JavaScript for LWC →

Learn it by building it

Every course, every code repository, the discussion group and certificates of completion — one subscription.

Keep me in the loop!

Email me when a new episode is published (promise, I will not spam you)

You're signing up to receive emails from Salesforce Casts