How to create Reusable Related Object Datatable in Lightning Web Component(LWC) Salesforce

by Rijwan Mohmmed
How-to-create-Reusable-Related-Object-Datatable-in-Lightning-Web-Component(LWC)-Salesforce

Hello Friends , today we will learn how to create a reusable related lightning Datatable in LWC. Many time we create Datatable for each object , so instead of this we can create one Datatable and change their objectname and other fields by Design attributes (target Configs ). By target configs we can change variable name when we put our component on page, tab etc.

reusableCustomRelatedListView.html :

<template> 

    <div class="slds-page-header slds-page-header_record-home">
		<div class="slds-page-header__row">
			<div class="slds-page-header__col-title">
				<div class="slds-media">
					<div class="slds-media__figure">
						<span class="slds-icon_container slds-icon-standard-opportunityy">
								<lightning-icon icon-name={ObjectIcon} alternative-text={Title} title={Title} ></lightning-icon>
						</span>
					</div>
					<div class="slds-media__body">
						<div class="slds-page-header__name">
							<div class="slds-page-header__name-title">
								<h1>
									<span class="slds-page-header__title slds-truncate">{Title}</span>
								</h1>
							</div>
						</div>
					</div>
				</div>
			</div>
		</div>
		<div class="slds-page-header__row slds-page-header__row_gutters">
			<div class="slds-page-header__col-details">
				<ul class="slds-page-header__detail-row pageheader">
                    <template if:true = {listRecs}>
                            <lightning-datatable key-field="Id"  
                                                    data={listRecs}  
                                                    columns={columns}  
                                                    hide-checkbox-column="true"  
                                                    show-row-number-column="true"
                                                    default-sort-direction={defaultSortDirection}
                                                    sorted-direction={sortDirection}
                                                    sorted-by={sortedBy}
                                                    onsort={onHandleSort}
                                                    onrowaction={handleRowAction}>  
                            </lightning-datatable> 
                    </template> 
                    <template if:true = {error}>
                        {error} 
                    </template> 
				</ul>
                </div>
		</div>
	</div>
        
</template>

ReusableCustomRelatedListView.Js :

import { api, LightningElement, track, wire } from 'lwc';  
import relatedListRecord from '@salesforce/apex/ReusableCustomListViewController.relatedListRecord';   
import { NavigationMixin } from 'lightning/navigation';
import { CurrentPageReference } from 'lightning/navigation';

export default class ReusableCustomRelatedListView extends LightningElement {
    listRecs;  
    initialListRecs;
    error;  
    columns;  
    ParentId = '';
    @api recordId;
    @api ParentObject;
    @api RelatedObject;
    @api Fields;
    @api RelatedField;
    @api TableColumns;
    @api Title;
    @api ObjectIcon;
    @api RecordPage;

    sortedBy;
    defaultSortDirection = 'asc';
    sortDirection = 'asc';
    currentPageReference = null; 
    urlStateParameters = null;
    /* Params from Url */
    urlId = null;

    connectedCallback() {
        this.columns = JSON.parse(this.TableColumns.replace( /([a-zA-Z0-9]+?):/g, '"$1":' ).replace( /'/g, '"' ));
        //this.fetchRelatedRecords();
    }

    get vals() {
        return this.RelatedObject + '-' + this.Fields + '-' +   
               this.RelatedField + '-' + this.ParentId;
    }

    @wire(CurrentPageReference)
    getStateParameters(currentPageReference) {
       if (currentPageReference) {
           this.fetchRelatedRecords();
          //this.urlStateParameters = currentPageReference.state;
          //this.setParametersBasedOnUrl();
       }
    }
 
    setParametersBasedOnUrl() {
        this.ParentId = this.recordId;
        this.fetchRelatedRecords();
        this.urlId = this.urlStateParameters.id || null;
        if(this.urlId != null){
            this.ParentId = this.recordId;
            this.fetchRelatedRecords();
        }
    }

    fetchRelatedRecords(){   
        relatedListRecord({ listValues: this.vals, recordId: this.recordId })
		.then(result => {
			/*this.listRecs = result;
            this.initialListRecs = result;*/
			this.error = undefined;
            
            //this.listRecs = result;
            let newData;   
            let objName = this.RecordPage;
            newData = JSON.parse(JSON.stringify(result));
            newData.forEach(function(entry) {  
                if (entry.CreatedBy){   
                    entry.CreatedByName = entry.CreatedBy.Name;   
                }
                if(entry.Id){
                    entry.recordLink = "/customers/s/" + objName + "?id=" + entry.Id;
                }  
            }); 

            let newRecords = [...newData];   
            this.listRecs  = newRecords; 

		})
		.catch(error => {
			this.error = error;
			this.listRecs = null;
            this.initialListRecs = null;
		})
    }

  
 
    handleKeyChange( event ) {  
          
        const searchKey = event.target.value.toLowerCase();  
        console.log( 'Search Key is ' + searchKey );
 
        if ( searchKey ) {  

            this.listRecs = this.initialListRecs;
 
             if ( this.listRecs ) {

                let recs = [];
                for ( let rec of this.listRecs ) {

                    console.log( 'Rec is ' + JSON.stringify( rec ) );
                    let valuesArray = Object.values( rec );
                    console.log( 'valuesArray is ' + valuesArray );
 
                    for ( let val of valuesArray ) {
                        
                        if ( val.toLowerCase().includes( searchKey ) ) {

                            recs.push( rec );
                            break;
                        
                        }

                    }
                    
                }

                console.log( 'Recs are ' + JSON.stringify( recs ) );
                this.listRecs = recs;

             }
 
        }  else {

            this.listRecs = this.initialListRecs;

        }
 
    }  

    onHandleSort( event ) {

        const { fieldName: sortedBy, sortDirection } = event.detail;
        const cloneData = [...this.listRecs];
        cloneData.sort( this.sortBy( sortedBy, sortDirection === 'asc' ? 1 : -1 ) );
        this.listRecs = cloneData;
        this.sortDirection = sortDirection;
        this.sortedBy = sortedBy;

    }

    sortBy( field, reverse, primer ) {

        const key = primer
            ? function( x ) {
                  return primer(x[field]);
              }
            : function( x ) {
                  return x[field];
              };

        return function( a, b ) {
            a = key(a);
            b = key(b);
            return reverse * ( ( a > b ) - ( b > a ) );
        };

    }

    handleRowAction( event ) {

        const actionName = event.detail.action.name;
        const row = event.detail.row;
        switch ( actionName ) {
            case 'view':
                this[NavigationMixin.GenerateUrl]({
                    type: 'standard__recordPage',
                    attributes: {
                        recordId: row.Id,
                        actionName: 'view',
                    },
                }).then(url => {
                     window.open(url);
                });
                break;
            case 'edit':
                this[NavigationMixin.Navigate]({
                    type: 'standard__recordPage',
                    attributes: {
                        recordId: row.Id,
                        objectApiName: this.RelatedObject,
                        actionName: 'edit'
                    }
                });
                break;
            default:
        }

    }

 
}

reusableCustomRelatedListView.js-meta.xml :

<?xml version="1.0"?>
<LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata">
	<apiVersion>51.0</apiVersion>
	<isExposed>true</isExposed>
	<targets>
		<target>lightning__RecordPage</target>
		<target>lightning__HomePage</target>
		<target>lightningCommunity__Page</target>
		<target>lightningCommunity__Default</target>
	</targets>
	<targetConfigs>
        <targetConfig targets="lightningCommunity__Default, lightning__HomePage,lightning__RecordPage">
			<property name="ParentObject" type="String" default="Account"/>
            <property name="RelatedObject" type="String" default="Contact"/>
            <property name="RelatedField" type="String" default="AccountId"/>
			<property name="Fields" type="String" default="FirstName,LastName,Email"/>
            <property name="TableColumns" type="String" default="[{label:'First Name',fieldName:'FirstName',sortable:true},{label:'Last Name',fieldName:'LastName'},{ label:'Email',fieldName: 'Email'}]"/>
            <property name="Title" type="String" default="Project"/>
			<property name="ObjectIcon" type="String" default="custom:custom:16"/>
			<property name="RecordPage" type="String"/>
        </targetConfig>
    </targetConfigs>
</LightningComponentBundle>

ReusableCustomListViewController.cls :

public without sharing class ReusableCustomListViewController {
   
    @AuraEnabled(cacheable = true)  
    public static List <sObject> relatedListRecord(String listValues, String recordId) {
        System.debug(listValues);
        List<String> strList = listValues.split('-'); 
        String strObject = strList.get(0);
        String strFields = strList.get(1);
        String strRelatedField = strList.get(2);
        String strAccountId = recordId;

        

        List<sObject> listRecs = new List<sObject>();
        String strSOQL = 'SELECT Id, ' + strFields + ' FROM ' + strObject;

        if (String.isNotBlank(strAccountId)){
            strSOQL  += ' WHERE ' + strRelatedField+ ' = \'' + strAccountId + '\'';
        }  
        
        strSOQL += ' LIMIT 25';
        system.debug( 'SOQL is ' + strSOQL );
        listRecs = Database.query( strSOQL );
        return listRecs;
    }
}

Now when we add the component we can see the target configs input in right side . See below image

table columns in JSON Format you can change according your requirement here I am adding contact fields json so we can put this component in account record page

[{label:'First Name',fieldName:'FirstName',sortable:true},{label:'Last Name',fieldName:'LastName'},{ label:'Email',fieldName: 'Email'}]
What’s your Reaction?
+1
0
+1
1
+1
0
+1
0
+1
0
+1
0

You may also like

3 comments

алор брокер сайт September 18, 2021 - 3:32 am

If some one desires to be updated with hottest technologies afterward he
must be pay a visit this site and be up to date every
day.

Reply
Rijwan Mohmmed September 18, 2021 - 4:39 am

Thank you for appreciation

Reply
Gena October 24, 2021 - 9:21 pm

Every weekend i used to go to see this web site,
because i want enjoyment, as this this site conations truly fastidious funny information too.

Reply

Leave a Comment