April 1, 2020

Reusable Related List using LWC in Salesforce

Sample code:

HTML:

<template>       
    <lightning-card title={titleWithCount} icon-name="standard:record">
        <lightning-button label="New" slot="actions" onclick={createNew}></lightning-button>
        <div slot="footer">
            <div  if:true={countBool}>
                <lightning-button label="View All" onclick={navigateToRelatedList}></lightning-button>
            </div>
        </div> 
        <div class="slds-m-around_medium">   
            <div if:true={listRecords}>   
                <template for:each={listRecords} for:item="rec">    
                    <div key={rec.Id} class="slds-box">                         
                        <lightning-record-view-form record-id={rec.Id} object-api-name={objectName}>
                            <div class="slds-grid">
                                <div class="slds-col slds-size_1-of-2">
                                    <lightning-output-field field-name={field1}></lightning-output-field>
                                    <lightning-output-field field-name={field2}></lightning-output-field>
                                </div>
                                <div class="slds-col slds-size_1-of-2">
                                    <lightning-output-field field-name={field3}></lightning-output-field>
                                    <lightning-output-field field-name={field4}></lightning-output-field>
                                </div>
                            </div>
                        </lightning-record-view-form><br/><br/>
                    </div>                       
                </template>   
            </div>   
        </div>   
    </lightning-card>       
</template>


JavaScript:

import { LightningElement, api, wire, track } from 'lwc'; 
import fetchRecords from '@salesforce/apex/RelatedListController.fetchRecords'; 
import { NavigationMixin } from 'lightning/navigation';
 
export default class RelatedList extends NavigationMixin( LightningElement ) { 
 
    @api objectName; 
    @api parentObjectName;
    @api fieldName; 
    @api fieldValue; 
    @api parentFieldAPIName; 
    @api recordId; 
    @api strTitle; 
    @api filterType; 
    @api operator; 
    @api fieldsList;
    @api relationshipApiName;
    @track field1;
    @track field2;
    @track field3;
    @track field4;
    @track listRecords;
    @track titleWithCount;
    @track countBool = false;

    connectedCallback() {

        var listFields = this.fieldsList.split( ',' );
        console.log( 'Fields are ' + listFields );
        this.field1 = listFields[ 0 ].trim();
        this.field2 = listFields[ 1 ].trim();
        this.field3 = listFields[ 2 ].trim();
        this.field4 = listFields[ 3 ].trim();
        console.log( 'Field 1 is ' + this.field1 );
        console.log( 'Field 2 is ' + this.field2 );
        console.log( 'Field 3 is ' + this.field3 );
        console.log( 'Field 4 is ' + this.field4 );

    }

    get vals() { 

        return this.recordId + '-' + this.objectName + '-' +  
               this.parentFieldAPIName + '-' + this.fieldName + '-' +  
               this.fieldValue + '-' + this.filterType + '-' + this.operator + '-' + this.fieldsList; 

    } 
     
    @wire(fetchRecords, { listValues: '$vals' }) 
    accountData( { error, data } ) {

        if ( data ) {
          
            this.listRecords = data.listRecords;

            if ( data.recordCount ) {
               
                if ( data.recordCount > 3 ) {

                    this.titleWithCount = this.strTitle + '(3+)';
                    this.countBool = true;
               
                } else {

                    this.countBool = false;
                    this.titleWithCount = this.strTitle + '(' + data.recordCount + ')';

                }
            }

        }

    }

    createNew() {

        this[NavigationMixin.Navigate]({
            type: 'standard__objectPage',
            attributes: {
                objectApiName: this.objectName,
                actionName: 'new'
            }
        });

    }

    navigateToRelatedList() {
       
        this[NavigationMixin.Navigate]({
            type: 'standard__recordRelationshipPage',
            attributes: {
                recordId: this.recordId,
                objectApiName: this.parentObjectName,
                relationshipApiName: this.relationshipApiName,
                actionName: 'view'
            }
        });

    }
 



JavaScript-meta.xml:

<?xml version="1.0" encoding="UTF-8"?> 
<LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata" fqn="RelatedList"> 
    <apiVersion>48.0</apiVersion> 
    <isExposed>true</isExposed> 
    <targets> 
        <target>lightning__RecordPage</target> 
    </targets> 
    <targetConfigs> 
        <targetConfig targets="lightning__RecordPage"> 
            <property name="strTitle" type="String" label="Title" description="Enter the title"/> 
            <property name="objectName" type="String" label="Object Name" description="Enter the object name"/> 
            <property name="parentObjectName" type="String" label="Parent Object Name" description="Enter the parent object name"/> 
            <property name="relationshipApiName" type="String" label="Relationship Name" description="Enter the relationship API name"/> 
            <property name="parentFieldAPIName" type="String" label="Parent Field API Name" description="Enter the parent field API Name"/> 
            <property name="fieldName" type="String" label="Field Name" description="Enter the field name"/> 
            <property name="fieldValue" type="String" label="Field Value" description="Enter the field value"/> 
            <property name="filterType" type="String" label="Filter Type" description="Enter the filter type"/> 
            <property name="operator" type="String" label="Operator" description="Enter the operator"/> 
            <property name="fieldsList" type="String" label="Fields List" description="Enter the field API names separated by coma. Do not enter more than 4 fields"/> 
        </targetConfig> 
    </targetConfigs> 
</LightningComponentBundle> 



Apex Class:

public class RelatedListController { 
 
    @AuraEnabled( cacheable=true ) 
    public static RelatedListWrapper fetchRecords( String listValues )  { 

        system.debug( 'values are ' + listValues ); 
        List < String > strList = listValues.split( '-' ); 
        system.debug( 'values are ' + strList ); 
        RelatedListWrapper objWrap = new RelatedListWrapper();
         
        if ( strList.size() == 8 ) { 
         
            String recordId = strList.get( 0 ); 
            String objectName = strList.get( 1 ); 
            String parentFieldAPIName = strList.get( 2 ); 
            String fieldName = strList.get( 3 ); 
            String fieldValue = strList.get( 4 ); 
            String filterType = strList.get( 5 ); 
            String operator = strList.get( 6 ); 
            String fieldsList = strList.get( 7 ); 
             
            String strSOQL = 'SELECT Id';
            strSOQL += ', ' + fieldsList;
            String filter = ' FROM ' + objectName + ' WHERE ' + parentFieldAPIName + ' = \'' + recordId + '\' AND '; 
            if ( filterType == 'String' )     
                filter += fieldName + ' = \'' + fieldValue + '\''; 
            else if ( filterType == 'Boolean' )     
                filter += fieldName + ' = ' + fieldValue; 
            else 
                filter += fieldName + ' ' + operator + ' ' + fieldValue; 
            String strCountSOQL = ' SELECT COUNT() ' + filter;
            objWrap.recordCount = Database.countQuery( strCountSOQL );
            strSOQL += filter + ' LIMIT 3';     
            objWrap.listRecords = Database.query( strSOQL ); 
             
        }   
        return objWrap; 
         
    } 

    public class RelatedListWrapper {

        @AuraEnabled
        public List < SObject > listRecords;
        @AuraEnabled
        public Integer recordCount;

    }
         
}


Output:






8 comments:

  1. Thank you for this post. I am getting the following error while trying to add the component to a lightning page.

    afterRender threw an error in 'c:relatedList' [Cannot read property 'split' of undefined]

    Function: connectedCallback.

    I tried looking for cause of this error, but was unable to find one. I am trying to add this component to a Contact page. Do you know what would cause this error and how to solve it?

    Thank you for your response.

    ReplyDelete
    Replies
    1. Make sure in the Fields List, you have at least two fields separated by a coma(,).

      Delete
  2. I am getting the following error while trying to add this component to a Contact page:

    afterRender threw an error in 'c:relatedList' [Cannot read property 'split' of undefined]

    function: connectedCallback.

    Do you know what would cause this error and how to solve it?

    Thank you for your response.

    ReplyDelete
    Replies
    1. Make sure in the Fields List, you have at least two fields separated by a coma(,).

      Delete
  3. I am getting an error Cannot read property 'trim' of undefined

    c:smartList

    ReplyDelete
    Replies
    1. check the component smartList. Remove it from the current page and test. Looks like it is a custom component from where you are getting the error.

      Delete
    2. So this will not work on custom objects?

      Delete
    3. I got rid of the error, but now it is not returning anything. Does it work on custom objects? and do you use API Names __c?

      Delete