3

I'm trying to build a custom datable because I want the functionality to sort, search and paginate and also add action buttons in each row. Initially, my VF page was using jQuery DataTables but we have now decided to rebuild that page in LWC.

I found a similar post but it hasn't really worked for me.

I then found Vanilla Datatables (Javascript) and Simple Datatables (ES2018) I have tried to implement Vanilla Datatables but I keep running into issues such as:

TypeError: Cannot read property 'length' of undefined

The above error is thrown because the library is unable to find the body of the table.

My point is that I feel this library won't work because it's using document, window and so on.

I was wondering if there is a way I could convert this library to something that is more suited to LWC basically have it "export" the modules. Since I'm not a JS expert I'm unable to put the pieces together.

Just for some context, I'll post my code here:

<template>
    <table class="sobject-table slds-table slds-table_cell-buffer slds-table_bordered">
        <thead>
            <tr class="slds-line-height_reset">
                <template for:each={fieldSetData} for:item="field">
                    <th key={field.apiName} title={field.fieldLabel} class="" data-scope="col">
                        <div class="slds-truncate" title={field.fieldLabel}>{field.fieldLabel}</div>
                    </th>
                </template>
            </tr>
        </thead>
        <tbody>
            <template for:each={records} for:item="row">
                <tr key={row} class="slds-hint-parent">
                    <template for:each={row} for:item="data">
                        <td key={data.id} data-label="Name" data-scope="row">{data.value}</td>
                    </template>
                </tr>
            </template>
        </tbody>
    </table>
</template>


import {LightningElement, api, wire} from 'lwc';
import {processRecords} from 'c/recordGenerator';
import getFieldSetData from '@salesforce/apex/FieldSetData.getFieldSetData';
import getSobjectRecords from '@salesforce/apex/DeomoCTRL.getRecords';
import VanillaDatatable from '@salesforce/resourceUrl/VanillaDatatable';
import {loadScript, loadStyle} from 'lightning/platformResourceLoader';

export default class ManageSelectedIndicators extends LightningElement {
    @api recordId;
    records;
    fieldSetData;
    @wire(
        getFieldSetData,
        {objectName: 'SOBJECTName', fieldSet: 'FIELSETName'}
    ) wiredFieldSetData({error, data}) {
        console.log('wired called in Child')
        if (data) {
            this.fieldSetData = data;
            this.getData();
        }
    }


    constructor() {
        super();
        console.log('Constructor called in Child');
    }

    connectedCallback() {
        console.log('Connected callback');
    }

    renderedCallback() {
        console.log('Rendered callback');
        Promise.all([
             loadScript(this, VanillaDatatable + '/VanillaDataTables/src/vanilla-dataTables.js')
         ]).then(() => {
             const table = new DataTable(
                 document.getElementsByClassName('.sobject-table'),
                 //Also tried this.template.querySelector('.sobject-table'),
                 {searchable: true, sortable : true}
             );
         })
         .catch(error => {
            console.log(error);
         });
    }

    getData() {
        getSobjectRecords ({
            sobjectId: this.recordId
        })
        .then(response => {
            this.records= processRecords(response, this.fieldSetData);
        })
        .catch((error) => {
            console.log(error);
        });
    }

}

Edit: I have also tried lwc:dom="manual" that didn't seem to work either.

Is there any other way I could achieve these functionalities? Or do I have to write custom code to achieve this?

1 Answer 1

3

The library "may" work if you use lwc:dom="manual" and construct the table that way, but it has to be done entirely in JavaScript without a template. I'm not sure that's something you want to do.

That said, lightning:datatable already has sorting capabilities built-in, and you could build pagination around it if you wanted to. There's no need to get an external library involved.

If you want an example in Aura, I wrote this gist. It's "old", but demonstrates everything you want to do in Aura.

If you insist on LWC, you can do that, too, demonstrated by this Playground.

I'll include the code, below.

import { LightningElement, track, api } from 'lwc';

export default class App extends LightningElement {
    @track pageRecords;
    @track allRecords;
    @track thisPage = 0;
    @track pageSize = 10;
    @track sortedBy = 'value1';
    @track sortedDirection = 'asc';
    @track columns = [
        { fieldName: 'value1', label: 'Value 1', sortable: true, type: 'number' },
        { fieldName: 'value2', label: 'Value 2', sortable: true, type: 'number' }
    ];
    connectedCallback() {
        let data = [];
        while(data.length < 1000) {
            data.push({
                id: data.length+'',
                value1: Math.random()*1000,
                value2: Math.random()*1000
            });
        }
        this.allRecords = data;
        this.maxPage = Math.floor((this.allRecords.length+this.pageSize-1)/this.pageSize);
        this.sort();
    }
    sort(event) {
        if(event) {
            this.sortedDirection = event.detail.sortDirection;
            this.sortedBy = event.detail.fieldName;
        }
        let direction = this.sortedDirection === 'asc'? 1: -1;
        this.allRecords = this.allRecords.sort(
            (a,b) => (a[this.sortedBy]-b[this.sortedBy])*direction
        );
        this.loadPage();
    }
    loadPage() {
        this.pageRecords = this.allRecords.filter(
            (v,i) => Math.floor(i/this.pageSize)===this.thisPage
        );
    }
    prevPage() {
        if(this.thisPage) {
            this.thisPage = this.thisPage - 1;
            this.loadPage();
        }
    }
    nextPage() {
        if(this.thisPage < this.maxPage) {
            this.thisPage = this.thisPage + 1;
            this.loadPage();
        }
    }
}

<template>
    <lightning-datatable 
        columns={columns}
        data={pageRecords}
        sorted-by={sortedBy}
        sorted-direction={sortedDirection}
        key-field="id"
        onsort={sort}>
    </lightning-datatable>
    <lightning-button label="Prev" onclick={prevPage}>
    </lightning-button>
    {thisPage}/{maxPage}
    <lightning-button label="Next" onclick={nextPage}>
    </lightning-button>
</template>
4
  • I have tried the lwc:dom="manual" and that didn't seem to work as well. I'll edit my question to reflect that. I can't use the lightning:datatable because, I would have to add other functionalities in the future which might not be scalable. But I think you are right, I might end up not using a library.
    – d_k
    Feb 28, 2020 at 12:19
  • @d_k Maybe, maybe not? The LWC lightning-datatable actually lets you extend it with custom data types.
    – sfdcfox
    Feb 28, 2020 at 13:05
  • yeah but what about a hidden column and search that would have to be custom too yeah I think?
    – d_k
    Feb 28, 2020 at 13:09
  • @d_k You don't need a hidden column, as the data is driven from the table. Searching is also pretty trivial.
    – sfdcfox
    Feb 28, 2020 at 13:19

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .