Data Tables in LWC gets painful to work with when you want to display the data  from related objets into a single data table.

Data tables in LWC showing data from related objects

The bottle neck that we face here is when we get the data back, that’s going to be in nested json format.

There are a couple of things on which we need to work on to get the Data Tables in LWC to work as expected.

  • The response that we get from Apex method should resemble the json we declared for the columns in data table. Something like this ..
const columns = [
    { label: 'Label', fieldName: 'Name' },
    { label: 'Website', fieldName: 'LeadSource' },
    { label: 'Phone', fieldName: 'Phone', type: 'phone' },
    { label: 'Account Name', fieldName: 'Account.Name'},
    { label: 'Account Industry', fieldName: 'Account.Industry'}
    
];
  • The response we get will be similar to the format shown below and we need to flatten it to resemble the json declared for the columns. So the below json response should be flattened to ..
{
    "Id": "0032v00002lLzgQAAS",
    "Name": "Rose Rose",
    "Phone": "(512) 757-6000",
    "LeadSource": "Trade Show",
    "AccountId": "0012v00002M0O2CAAV",
    "Account": {
      "Name": "Edge Communications",
      "Industry": "Electronics",
      "Id": "0012v00002M0O2CAAV"
    }
}

something like this ..

{
    "Id": "0032v00002lLzgQAAS",
    "Name": "Rose Rose",
    "Phone": "(512) 757-6000",
    "LeadSource": "Trade Show",
    "AccountId": "0012v00002M0O2CAAV",
    "Account.Name": "Edge Communications",
    "Account.Industry": "Electronics",
    "Account.Id": "0012v00002M0O2CAAV"     
}

Okay! Let's get started.

This is how my template file is going to look like.

<template>
    <div style="height: 500px;">
        <lightning-datatable
                key-field="id"
                data={allContacts}
                columns={columns}>
        </lightning-datatable>
    </div>    
</template>

Then comes the js file.

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

import getContacts from '@salesforce/apex/ExploreCustomContactController.getContacts';

const columns = [
    { label: 'Label', fieldName: 'Name' },
    { label: 'Website', fieldName: 'LeadSource' },
    { label: 'Phone', fieldName: 'Phone', type: 'phone' },
    { label: 'Account Name', fieldName: 'Account.Name'},
    { label: 'Account Industry', fieldName: 'Account.Industry'}
    
];

export default class ExploreCustomDataTable extends LightningElement {

    @track allContacts = [];
    @track columns = columns;
    
    //wiring an apex method to a function
    @wire(getContacts)
    wiredContacts({ error, data }) {
        if(data) {
           //this is the final array into which the flattened response will be pushed. 
           let contactsArray = [];
            
           for (let row of data) {
                // this const stroes a single flattened row. 
                const flattenedRow = {}
                
                // get keys of a single row — Name, Phone, LeadSource and etc
                let rowKeys = Object.keys(row); 
               
                //iterate 
                rowKeys.forEach((rowKey) => {
                    
                    //get the value of each key of a single row. John, 999-999-999, Web and etc
                    const singleNodeValue = row[rowKey];
                    
                    //check if the value is a node(object) or a string
                    if(singleNodeValue.constructor === Object){
                        
                        //if it's an object flatten it
                        this._flatten(singleNodeValue, flattenedRow, rowKey)        
                    }else{
                        
                        //if it’s a normal string push it to the flattenedRow array
                        flattenedRow[rowKey] = singleNodeValue;
                    }
                    
                });
               
                //push all the flattened rows to the final array 
                contactsArray.push(flattenedRow);
            }
            
            //assign the array to an array that's used in the template file
            this.allContacts = contactsArray;
        } else if (error) {
            this.error = error;
        }
    }
    
    //create keys in the format of Account.Id, Account.Rating, Account.Industry and etc
    //we can avoid using this function by reusing the above function. To understand in easily I used a separate function 
    //Feel free to refactor it
    _flatten = (nodeValue, flattenedRow, nodeName) => {        
        let rowKeys = Object.keys(nodeValue);
        rowKeys.forEach((key) => {
            let finalKey = nodeName + '.'+ key;
            flattenedRow[finalKey] = nodeValue[key];
        })
    }

}

It's not that we can only show data from two related objects. We can also show data from multiple objects which are in relationship, as long as the flattened json response is resembling the json we used in declaring the columns.

Liked the post? Let me know what you think of it.

Interested in learning LWC Development with me? Subscribe to my course (use this if you are out of India).