December 1, 2020

Expandable Table Rows in Salesforce LWC

Sample Code:

Apex Class:

public with sharing class AccountController {
 
    @AuraEnabled( cacheable = true )
    public static List< AccountWrapper > fetchAccounts() {
     
        List< AccountWrapper > listWrap = new List< AccountWrapper >();
        
        for ( Account objAcc : [ SELECT Id, Name, Industry, AccountNumber, Rating, Type,  
                                        ( SELECT Id, FirstName, LastName, Email FROM Contacts )
                                   FROM Account
                                  LIMIT 10 ] ) {
            
            AccountWrapper objWrap = new AccountWrapper( true, objAcc, objAcc.Contacts.size() > 0 ? true : false );
            listWrap.add( objWrap );

        }

        return listWrap;
         
    }

    public class AccountWrapper {

        @AuraEnabled
        public Boolean hideBool;
        @AuraEnabled
        public Boolean contactBool;
        @AuraEnabled
        public Account objAccount;

        public AccountWrapper( Boolean hideBool, Account objAccount, Boolean contactBool ) {

            this.hideBool = hideBool;
            this.objAccount = objAccount;
            this.contactBool = contactBool;

        }

    }
     
}

HTML:
<template>
    <div class="slds-box slds-theme--default">
        <div class="slds-text-color_inverse slds-text-heading_large" style="padding:0.5rem;background:#16325c">        
            Accounts
        </div>
        <div style="padding:2px;">
            <lightning-button variant="brand" label="Expand All" onclick={expandAll} class="slds-m-left_x-small"></lightning-button>
            <lightning-button variant="brand" label="Collapse All" onclick={collapseAll} class="slds-m-left_x-small"></lightning-button>
        </div>
        <table class="slds-table slds-table_cell-buffer slds-table_bordered slds-table_striped">
            <thead>
                <tr class="slds-line-height_reset">
                    <th>
                        Account Name
                    </th>
                    <th>
                        Account Number
                    </th>
                    <th>
                        Industry
                    </th>
                    <th>
                        Rating
                    </th>
                    <th>
                        Type
                    </th>
                </tr>
            </thead>
            <tbody>
                <template iterator:it={records}>
                    <tr class="slds-hint-parent" key={it.value.objAccount.Id} style="cursor: pointer;">                        
                        <td data-label="Account Name">       
                            <a href="#" onclick={hideAndShow} data-record-id={it.index} style="display:block;text-decoration:none;color:black;">                 
                                {it.value.objAccount.Name}
                            </a>
                        </td>
                        <td data-label="Account Number">
                            <a href="#" onclick={hideAndShow} data-record-id={it.index} style="display:block;text-decoration:none;color:black;">                 
                                {it.value.objAccount.AccountNumber}
                            </a>
                        </td>
                        <td data-label="Industry">
                            <a href="#" onclick={hideAndShow} data-record-id={it.index} style="display:block;text-decoration:none;color:black;">                 
                                {it.value.objAccount.Industry}
                            </a>
                        </td>
                        <td data-label="Rating">
                            <a href="#" onclick={hideAndShow} data-record-id={it.index} style="display:block;text-decoration:none;color:black;">                 
                                {it.value.objAccount.Rating}
                            </a>
                        </td>
                        <td data-label="Type">
                            <a href="#" onclick={hideAndShow} data-record-id={it.index} style="display:block;text-decoration:none;color:black;">                 
                                {it.value.objAccount.Type}
                            </a>
                        </td>    
                    </tr>
                    <template if:false={it.value.hideBool} key={it.value.objAccount.Id} style="padding: 5px;">
                        <tr key={it.value.objAccount.Id}>
                            <td colspan="5">
                                <template if:true={it.value.contactBool}>    
                                    <b key={it.value.objAccount.Id}>Contacts found for this Account are below:</b>
                                        <template iterator:it={it.value.objAccount.Contacts}>
                                            <div style="padding:1px;" key={it.value.Id}>
                                                Name: {it.value.FirstName} {it.value.LastName}<br/>
                                                Email: {it.value.Email}
                                            </div>
                                        </template>
                                </template>
                                <template if:false={it.value.contactBool}>    
                                    <b key={it.value.objAccount.Id}>No Contacts found for this Account!!!</b>
                                </template>
                            </td>
                        </tr>
                    </template>                    
                </template>
            </tbody>
        </table>
    </div>
</template>

JavaScript:
import { LightningElement, wire } from 'lwc';
import fetchAccounts from '@salesforce/apex/AccountController.fetchAccounts';

export default class Sample extends LightningElement {

    records;
    error;

    @wire( fetchAccounts )  
    wiredAccount( { error, data } ) {

        if (data) {

            console.log( 'Fetched Data ' + JSON.stringify( data ) );
            this.records = data;

        } else if ( error ) {

            this.error = error;
            this.records = undefined;

        }

    }  

    hideAndShow( event ) {

        let indx = event.target.dataset.recordId;
        console.log( 'Index is ' + indx );

        if ( this.records ) {

            let recs =  JSON.parse( JSON.stringify( this.records ) );
            let currVal = recs[ indx ].hideBool;
            console.log( 'Current Val ' + currVal );
            recs[ indx ].hideBool = !currVal;
            this.records = recs;
            console.log( 'After Change ' + this.records[ indx ].hideBool );

        }

    }

    expandAll() {

        if ( this.records ) {

            let recs =  JSON.parse( JSON.stringify( this.records ) );

            for ( let rec of recs ) {
                rec.hideBool = false;
            }

            this.records = recs;

        }

    }

    collapseAll() {

        if ( this.records ) {

            let recs =  JSON.parse( JSON.stringify( this.records ) );

            for ( let rec of recs ) {
                rec.hideBool = true;
            }

            this.records = recs;

        }

    }

}

Output:




4 comments:

  1. Very much needed doc for me. Thanks man!

    ReplyDelete
  2. on click of each row its not working

    ReplyDelete
    Replies
    1. Check console logs and see whether you are hitting any exceptions in JavaScript.

      Delete
  3. Every time a row is expanded, the component disappears and re-appears. Expand all works correctly.
    After some messing about I found out this is because of the href="#".
    Moving the onclick={hideAndShow} into the td elements works.

    ReplyDelete