0

I have two almost identical Lightning Web Components with only difference that they use two different Apex Controller with two different Apex method to get data, the first component uses version with three parameters and the other component uses version with five parameters. I would like to follow the DRY principle (Don't Repeat Yourself) and to remove duplicate code for sorting datatable but I can't.

Documentation sounds very unclear how to implement that.

Listing of simpleTable.html

<template>
    <template if:true={error}>
        <template if:true={error.body}>
            {error.body.message}
        </template>
    </template>
    <lightning-datatable data={data} columns={columns} key-field="id"  hide-checkbox-column 
        onsort={updateColumnSorting}></lightning-datatable>
</template>

Listing of simpleTable.js

import { LightningElement, track, api } from 'lwc';
import getColumnsAndData from '@salesforce/apex/SimpleTableCont.getColumnsAndData';
export default class SimpleTable extends LightningElement {
    @api a;
    @api b;
    @api c;

    @track data;
    @track columns;
    @track error;
    //@track sortedBy;
    //@track sortDirection;
    connectedCallback() {
        getColumnsAndData({a:this.a, b:this.b.split(','), c: this.c }).then(result=>{
            this.data = result.data;
            this.columns = result.columns;
        }).catch(error => {
            this.error = error;
        });
    }

    // Client-side controller called by the onsort event handler
    updateColumnSorting(e) {
        //this.sortedBy = e.detail.fieldName;
        //this.sortDirection = e.detail.sortDirection;
        e.srcElement.sortedBy = e.detail.fieldName;
        e.srcElement.sortDirection = e.detail.sortDirection;

        this.sortData(e.detail.fieldName, e.detail.sortDirection);
    }

    sortData(fieldName, sortDirection) {
        var reverse = sortDirection !== 'asc';
        //sorts the rows based on the column header that's clicked
        var data = this.data;
        var primer = (data && data.length && data[0].Origin) ? (x, field)=>x.Origin[field] : null;
        data.sort(this.sortBy(fieldName, reverse, primer))
        this.data = JSON.parse(JSON.stringify(data)) ;
    }
    sortBy(field, reverse, primer) {
        var key = primer ?
            function(x) {return primer(x,field)} :
            function(x) {return x[field]};
        //checks if the two rows should switch places
        reverse = !reverse ? 1 : -1;
        return function (a, b) {
            a = key(a);
            b = key(b);
            return reverse * ((a > b) - (b > a));
        }
    }
}

Listing of Table.html

<template>
    <template if:true={error}>
        <template if:true={error.body}>
            {error.body.message}
        </template>
    </template>
    <lightning-datatable data={data} columns={columns} key-field="id"  hide-checkbox-column 
        onsort={updateColumnSorting}></lightning-datatable>
</template>

Listing of Table.js

import { LightningElement, track, api } from 'lwc';
import getColumnsAndData from '@salesforce/apex/TableCont.getColumnsAndData';
import {updateColumnSorting} from 'c/simpleTable';
import SimpleTable from 'c/simpleTable';
export default class DataTableLWC extends LightningElement {
    @api a;
    @api b;
    @api c;
    @api d;
    @api e;

    @track data;
    @track columns;
    @track error;
    //@track sortedBy;
    //@track sortDirection;
    connectedCallback() {
        getColumnsAndData({ a:this.a, b:this.b.split(',')
            , c: this.c, d: JSON.parse(this.d.replace(/'/g, '"'))
            , e: JSON.parse(this.e.replace(/'/g, '"')) })
        .then(result=>{
            this.data = result.data;
            this.columns = result.columns;
        }).catch(error => {
            this.error = error;
        });
    }
    updateColumnSorting(e) {
        new SimpleTable().updateColumnSorting(e);
    }
    /*// Client-side controller called by the onsort event handler
    updateColumnSorting(e) {
        //this.sortedBy = e.detail.fieldName;
        //this.sortDirection = e.detail.sortDirection;
        e.srcElement.sortedBy = e.detail.fieldName;
        e.srcElement.sortDirection = e.detail.sortDirection;
        this.sortData(e.detail.fieldName, e.detail.sortDirection);
    }

    sortData(fieldName, sortDirection) {
        var reverse = sortDirection !== 'asc';
        //sorts the rows based on the column header that's clicked
        var data = this.data;
        data.sort(this.sortBy(fieldName, reverse));
        this.data = JSON.parse(JSON.stringify(data)) ;
    }
    sortBy(field, reverse, primer) {
        var key = primer ?
            function(x) {return primer(x[field])} :
            function(x) {return x[field]};
        //checks if the two rows should switch places
        reverse = !reverse ? 1 : -1;
        return function (a, b) {
            a = key(a);
            b = key(b);
            return reverse * ((a > b) - (b > a));
        }
    }*/
}

I would like to remove commented code from B.js and reuse the same method from A.js. However, both when I try to use

import {updateColumnSorting} from 'c/SimpleTable';

and

updateColumnSorting(e);

and when I try to use

import a from 'c/SimpleTable';

and

SimpleTable.updateColumnSorting(e);

I receive an error

Uncaught TypeError: SimpleTable.updateColumnSorting is not a function throws at /auraFW/javascript/pxtF0ZdGYF-z00XsmRUcGQ/aura_proddebug.js:6146:17

I even tried to add line

export * from SimpleTable;

to the end of SimpleTable.js file but it didn't compile.

How do I reuse Javascript code from one LWC in another LWC?


I tried to follow @muenzpraeger suggestion and try again to use suggestion from this article

import SimpleTable from 'c/SimpleTable';
updateColumnSorting(e) {
    new SimpleTable().updateColumnSorting(e); 
}

so I changed line

SimpleTable.updateColumnSorting(e); 

to

new SimpleTable().updateColumnSorting(e); 

However, it still throws me an exception enter image description here

Uncaught ReferenceError throws at https://velocity-nosoftware-4149-dev-ed.lightning.force.com/auraFW/javascript/0Rz6fp7Oruhim-Qzw_AgRQ/aura_proddebug.js:8235:21

Looks like the problem is that I am unable to skip execution of parent constructor of BaseLightningElement() which checks some local variable vmBeingConstructed which I am not able to set or override at line 7195 of aura_proddebug.js

function BaseLightningElement() {
    if (isNull(vmBeingConstructed)) {
        throw new ReferenceError();
    }

Looks like this complication is made intentional, there are some strange comments in the internal LWC code:

// interaction hooks
// We are intentionally hiding this argument from the formal API of LWCElement because
// we don't want folks to know about it just yet.

When I try to avoid execution of inherited constructor by defining a constructor at SimpleTable

constructor() {
    //super();
}

which doesn't call super(), I receive another error enter image description here

Uncaught ReferenceError: Must call super constructor in derived class before accessing 'this' or returning from derived constructor throws at https://velocity-nosoftware-4149-dev-ed.lightning.force.com/auraFW/javascript/0Rz6fp7Oruhim-Qzw_AgRQ/aura_proddebug.js:8235:21

3 Answers 3

0

Put your shared logic in shared modules (think: like static resources).

See here in LWC Recipes how it's done. There's also a great blog post about that topic available.

1
  • Thanks, I have read them and tried to apply that approach but that didn't work!
    – Patlatus
    Commented May 4, 2019 at 14:56
0

If you are using a component from any other component(parent-child) then you just need to use one LWC and pass the @api param. According to this param, call your class method and make some of the changes as per the requirement.

If you are using this component in any app builder then create target attributes in meta and use this attribute to pass the value in @api param.

If both options are not suitable and want to use 2 LWC then I would recommend you to follow the link from @muenzpraeger.

0

Gosh! It took me 2 days to find a very simple and very elegant solution to remove duplicate code from my components!

I struggled so much to follow lwc recipes, documentation and articles that I have completely forgot about some basic OOP feature that ES6 supports for modules, namely, inheritance.

I can achieve my goal by simply extending module for Table from module for SimpleTable.

import { api } from 'lwc';
import getColumnsAndData from '@salesforce/apex/TableCont.getColumnsAndData';
import SimpleTable from 'c/simpleTable';
export default class Table extends SimpleTable {
    //@api a;
    //@api b;
    //@api c;
    @api d;
    @api e;

    //@track data;
    //@track columns;
    //@track error;
    //@track sortedBy;
    //@track sortDirection;
    connectedCallback() {
        getColumnsAndData({ a:this.a, b:this.b.split(',')
            , c: this.c, d: JSON.parse(this.d.replace(/'/g, '"'))
            , e: JSON.parse(this.e.replace(/'/g, '"')) })
        .then(result=>{
            this.data = result.data;
            this.columns = result.columns;
        }).catch(error => {
            this.error = error;
        });
    }
}

By using a single line to extend one module from another,

export default class Table extends SimpleTable {

I am able to remove all the duplicated variables and methods in Javascript module!

You must log in to answer this question.

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