function readOnly(count){ }
Starting November 20, the site will be set to read-only. On December 4, 2023,
forum discussions will move to the Trailblazer Community.
+ Start a Discussion
Daniel Watson 21Daniel Watson 21 

Lighting Component DataTable Columns display related fields

Hello Devs,

I have a component that is going to be using a <lightning:datatable /> object I am attempting to set the cols with:
var cols = [
    {label: 'Vendor', fieldName: 'a02_Vendor__r.Name', type: 'text'},
    {label: 'Type', fieldName: 'RecordType.Name', type: 'text'},
    {label: 'Created', fieldName: 'CreatedDate', type: 'Date'}
];
component.set("v.tableCols", cols);
Where the a02_Vendor__r.Name is a related Account Name and the RecordType is the RecordType of the Object being returned by my controller.

When I console.log(response.getReturnValue()) from my call back function I do see that I am getting the apropreate and expected resluts from my controller, however my lighting datatabel IS displaying the CreatedDate field, but the two realted fields are empty. Can we not use related or nested objects in the datatable component? (that would be silly)...

Any help would be much apreciated!
Best Answer chosen by Daniel Watson 21
Raj VakatiRaj Vakati
This is wrong .. 
 
component.set("v.benefits", response.getReturnValue());

Do this steps to fix the issue .. 
Create a wrapper class  and convert it as JSON data and set to the benefits attributes 


 

All Answers

Raj VakatiRaj Vakati
You can do the related filed. But can you share the complete code to see the issue? I believe its problem with the data setup to the controller
Daniel Watson 21Daniel Watson 21
Sure thing Raj

Component:
<aura:component controller="ReconciliationController" implements="flexipage:availableForRecordHome,force:hasRecordId" access="global">
	<!-- Attributes for the Component -->
    <aura:attribute name="recordId" type="Id" />
    <aura:attribute name="thisRecon" type="Reconciliation__c" />
    
    <!-- Attributes for the Benefits Edit DataTable -->
    <aura:attribute name="tableCols" type="List" />
    
    <!-- Attributes for the Benefits Status List -->
    <aura:attribute name="benefits" type="AccountBenefitsDetail__c[]" />
    
    <!-- Initializer -->
    <aura:handler name="init" action="{!c.doInit}" value="{!this}"/>
    
    <!-- Component -->
    <lightning:card title="Benefit Vendors" class="container" >
            <lightning:datatable data="{! v.benefits }"
                                 columns="{! v.tableCols }"
                                 keyField="Id"/>
    </lightning:card>
</aura:component>

Controller, helper, reder:
({
    /** Client-side Controller **/
    doInit : function(component, event, helper) {
        helper.getThisReconciliation(component, helper);
        var cols = [
            {label: 'Vendor', fieldName: 'a02_Vendor__r.Name', type: 'text'},
            {label: 'Type', fieldName: 'RecordType.Name', type: 'text'},
            {label: 'Created', fieldName: 'CreatedDate', type: 'Date'}
        ];
        component.set("v.tableCols", cols);
    },
})
HELPERS
({
    // Function will get the Data for the current Reconciliation
    // object and set the corresponding aura attribute.
    getThisReconciliation : function(component, helper) {
        var action = component.get("c.getThisReconciliation");
        action.setParams({"recId":component.get("v.recordId")})
        action.setCallback(this, $A.getCallback(function(response) {
            var state = response.getState();
            if (state === "SUCCESS") {
                component.set("v.thisRecon", response.getReturnValue());
            }
            else {
                helper.counselLogErrors(response.getError());
            }
        }));
        $A.enqueueAction(action);
    },
    
    // Function will get the data for the related accounts benefit
    // detail data and set it to the corresponding aura attribute.
    getBenefitDetails : function(component, helper) {
    	var action = component.get("c.getBenefitDetails");
        var reconciliation = component.get("v.thisRecon");
        action.setParams({"accId":reconciliation.Account__c})
        action.setCallback(this, $A.getCallback(function(response) {
            var state = response.getState();
            if (state === "SUCCESS") {
                console.log(response.getReturnValue());  // <-- Works as expected...
                component.set("v.benefits", response.getReturnValue());
            }
            else {
                helper.counselLogErrors(response.getError());
            }
        }));
        $A.enqueueAction(action);
	},
    
    // Funciton logs any errors to the js browser console.
   	counselLogErrors : function(errors) {
        if (errors) {
            if (errors[0] && errors[0].message) {
                console.log("Error message: " + errors[0].message);
            }
        } else {
            console.log("Unknown error");
        }
	},
})
RENDERS
({
    afterRender: function (component, helper) {
        this.superAfterRender();
        helper.getBenefitDetails(component, helper);
    },
})

Server-Side:
public class ReconciliationController {
	@AuraEnabled
    public static Reconciliation__c getThisReconciliation (Id recId) {
        return [SELECT Id, Name, Account__c, JSON_Reconciliation_State__c FROM Reconciliation__c WHERE Id =: recId];
    }
    
    @AuraEnabled
    public static List<AccountBenefitsDetail__c> getBenefitDetails(Id accId) {        
        return [SELECT Id, a02_Vendor__r.Name, RecordType.Name, CreatedDate
                FROM AccountBenefitsDetail__c 
                WHERE Account__c =: accId ];
    }
}

Thanks!
Raj VakatiRaj Vakati
This is wrong .. 
 
component.set("v.benefits", response.getReturnValue());

Do this steps to fix the issue .. 
Create a wrapper class  and convert it as JSON data and set to the benefits attributes 


 
This was selected as the best answer
Daniel Watson 21Daniel Watson 21
Excelent! Wrapper Class did the trick for the columns. Thanks!
Sfdc learner18Sfdc learner18
Hi Daniel,

I am also facing problem to access the dot notation values in the datatable component.Can you please help me out ....how you have done in your case with wrapper classes,i am new to wrapper class.


Thanks!!
Daniel Watson 21Daniel Watson 21

Sfdc learner18,

So a wraper class is most easly defind inside of your working componenet APEX controller class. If you can think of use cases where you might want to build other componenets to retrieve similar data then you could create your wrapper in a separate file. I ended up reosolving my issue another way, but my initial solution was something like this. (Sorry, no longer have the origonal code realted to my above code)

public class myClass {

    @AuraEnabled
    public static myInnerClass getStuff(Id stuffVar) {
        myInnerClass mic = new myInnerClass();
        mic.someVar = stuffVar;
        Object__c obj =[SELECT f1, f2, f3 FROM Object__c WHERE Id =: stuffVar];
        mic.someObject = obj;
        return mic;
    }

    private class myInnerClass {
        Id someVar {get; set;}
        String someOtherVar {get; set;}
        Integer someOtherOtherVar {get; set}
        Object__c someObject {get; set;}
    }
}
This (http://sfdcmonkey.com/2017/07/06/use-wrapper-class-lightning-component/)may also help!
Deepali Gokhru 23Deepali Gokhru 23

Hi @Daniel Watson 21 & @Sfdc learner18 
 
I happened to read this thread and even though your issues have been resolved, would like to share some tips on this topic. 
Sure making a wrapper class could help here but there are few challenges with this approach as well. 
If you click on the column header of the table  which you are binding your wrapper class data with, the whole table refreshes  
 
Possible solution to this problem is Flattening the object. i. e modifying the response before binding it to the data table. 
e.g. var rows = response.getReturnValue();     
            for (var i = 0; i < rows.length; i++) { 
                var row = rows[i]; 
                //as data columns with relationship __r can not be displayed directly in data table, so generating dynamic columns 
                if (row.Opportunity__r) { 
                    row.OpportunityName = row.Opportunity__r.Name; 
                    row.AccountName = row.Opportunity__r.Account.Name;  
                } 
            } 
and then assigned the modified result to component attribute which holds the data to bind with lightning data table 
component.set("v.data", rows );  
 
And of course, you will need to have OpportunityName  and AccountName columns in the column list. 
 
Let me know if you find this helpful :) 
 
Best Regards, 
Deepali Gokhru
 

Daniel Watson 21Daniel Watson 21
Deepall,

Thanks for the additional info! Yes I did end up using this solution for a simialar component design and binding issue.
Ravish Verma 15Ravish Verma 15
Hi @Deepali Gokhru 23

how would i assigned the related records column values in data table column ? I am not able to figure this out, could you please help me on this ?
Thanks 
Ravish
Deepali Gokhru 23Deepali Gokhru 23
@Ravish Verma 15

Sure. I will try to explain it with the same example mentioned in my last comment. 
First you need to mofify the response which contains your related data like

var rows = response.getReturnValue();     //storing the response in a temporary variable 
            //looping through each row of the result 
            for (var i = 0; i < rows.length; i++) { 
                var row = rows[i]; 
                //as data columns with relationship __r can not be displayed directly in data table, so generating dynamic columns 
                if (row.Opportunity__r) { 
                     //here you assign the related data to new variables
                    row.OpportunityName = row.Opportunity__r.Name; 
                    row.AccountName = row.Opportunity__r.Account.Name;  
                } 
            }

After this point a single record in rows collection looks something like this 
Opportunity__r.Name = 'xyz';
Opportunity__r.Account.Name = 'abc'
//and the same values are copied into the newly created variables OpportunityName & AccountName (for each record)
OpportunityName = 'xyz'; 
AccountName = 'abc'

and then assigned the modified result to component attribute which holds the data to bind with lightning data table 
component.set("v.data", rows );  

Now instead of using Opportunity__r.Name & Opportunity__r.Account.Name in column fieldName, you would use OpportunityName & AccountName respectively and this should do the trick.

Please go ahead and like the answer if it helps :) 

BR,
Deepali
Ravish Verma 15Ravish Verma 15
Hi @Deepali Gokhru 23,

Thank you so much for your prompt response. It helped a lot and I am able to get data. 

I want to add another column in my data table which is the sequence coulmn and it should display the sequence number based on the number of records. For example, if 10 records are there then in the sequnce column , it should diplay 1,2 ,3 , 4.... and so on. Can you please guide me how to achiev this ?

Thanks 
Ravish
Deepali Gokhru 23Deepali Gokhru 23
@Ravish Verma 15,

You're welcome. To achieve autonumbering you need to set showRowNumberColumn attribute of lightning:dataTable to true (which is false by default ) and it should work.

Please like the answer if it helps !!

BR,
Deepali


 
Ravish Verma 15Ravish Verma 15
Thnaks @Deepali Gokhru 23 for quick answer. It is indeed a best answer. Thank you once again. 
Do you mind helping me with another question ? I want to develop a lightning component for year end tax statement for logged in user with all donation information for a non-profit organization. Could you please guide me the steps to achieve it ?

Thanks 
Ravish 
Deepali Gokhru 23Deepali Gokhru 23
@Ravish Verma 15
I would suggest you to post a this question in a new thread to keep it relevant.

BR,
Deepali
Ashish Nigam 21Ashish Nigam 21
@Deepali

Your Solution is looking suitable , but it won't working.

records.length is undefined
I use this solution but  not getting out put here is my code

Controller :

var action1 = component.get("c.getBeneficycontact");
         var record;
          action1.setParams({
             "searchKeyword" : component.get("v.selectedRecordId")
          });
        action1.setCallback(this, function(response){
             debugger;
            let state = response.getState();
                            
                console.log(JSON.stringify(response.getReturnValue()));
            if (state === "SUCCESS") {
               var records = JSON.stringify(response.getReturnValue());
               
                console.log("OutSide Loop" + records );    //  geting message undefined
                for(var i = 0; i < records.lenght ; i++ )   /// controller is not going in  loop
                {
                    console.log(" Inside Loop");
                    record = records[i];
                    console.log("Value of records " +records[i]);
                    console.log("value of recoerd" +record);
                    
                    if (record.Benefit__r)
                    {
                        console.log("Benefit__r.Name" +record.Benefit__r.Name);
                        console.log("Benefit__r.Type_Service__c " +record.Benefit__r.Type_Service__c);
                        record.BName = record.Benefit__r.Name;
                        
                        record.TService = record.Benefit__r.Type_Service__c;
                        
                        console.log( "BName " + record.BName);
                        console.log("TService" + record.TService);
                    }
                    
                }
                
                console.log('Value of record ' + record);
                //component.set("v.BenefitList", record);
                
                helper.sortData(component, component.get("v.sortedBy"), component.get("v.sortedDirection"));
            }
            else if(state === "ERROR"){     
                console.log('Error: ' + JSON.stringify(response.error));
             
            }
            else{
                console.log('Unknown problem, state: ' + response.getState() + ', error: ' + JSON.stringify(response.error));
            }
          
        });
        $A.enqueueAction(action1);  
        

Columns set 
 component.set('v.benificeryBenColumns',[
            
            {label :'Beneficiary Name',fieldName:'Name',type:'text',sortable: true},
            {label :'Benefit Name',fieldName:'BName',type:'text',sortable: true},
            {label :'Type of Service',fieldName:'TService',type:'text',sortable: true},
            {label :'OutCome',fieldName:'Outcome__c',type:'text',sortable: true},
        ]);

here is my controller

@AuraEnabled
    public static List<BeneficiaryBenefit__c> getBeneficycontact (String searchKeyword){
       List<BeneficiaryBenefit__c> listContact = new List<BeneficiaryBenefit__c>();
         // List<Sobject> listContact = new List<Sobject>();
     //     Sobject sob1 = new Sobject();
        //List<Sobject> listObj1 = new List<Sobject>();
        //String BenificieryId ;
        try{
            
            
                listContact = [select Id, Name , Benefit__r.Name, Benefit__r.Type_Service__c , Outcome__c from BeneficiaryBenefit__c where Beneficiary__c = :searchKeyword ];
            
           // for(BeneficiaryBenefit__c lobj :listContact)
         //   {
             //   sobj1.push(listContact)
           // }
            
            //BenificieryId = (String) listContact.Id;
        }
        Catch(Exception ex){
            //LippertSD_UtilityHelper.logError(ex);
            throw new AuraHandledException('System failed to load Benefits. Please find an error: ' + ex.getMessage());
        }
      // System.debug(listContact[0].get('Name'));
      //  System.debug(listContact[0].getSObject('Benefit__r').get('Name'));
        return listContact;
    }






 
Deepali Gokhru 23Deepali Gokhru 23
@ashish 

The problem here is 'records' variable is undefined in the lightning controller as you mentioned 

   console.log("OutSide Loop" + records );    //  geting message undefined

So the rest of the logic won't work. You first need to debug why the result from call back to apex controller is printing as undefined.


Thanks,
Deepali
Russell Farmer 9Russell Farmer 9
@Deepali Gokhru 23
I am trying to use your solution to resolve a problem I posted here: https://developer.salesforce.com/forums/ForumsMain?id=9062I000000II9jQAG

I think your solution will work for my problem but I think the value I am trying to return is one level to "low". For instance I am not try to return a value from the first level of the array but a level below it: 
User-added image

Here is my code from your solution: 
var rows = response.getReturnValue();     //storing the response in a temporary variable 
            //looping through each row of the result 
            for (var i = 0; i < rows.length; i++) { 
                var row = rows[i]; 
                //as data columns with relationship __r can not be displayed directly in data table, so generating dynamic columns 
                if (row.Opportunity_Contact_Roles__r) { 
                     //here you assign the related data to new variables
                    row.RFPCoName = row.Opportunity_Contact_Roles__r.Name; 
                } 
            };
My code runs but then I get my Name varialbe undefined. How do I go one level lower in the structure?
User-added image
 
Laveen Ekka 8Laveen Ekka 8
Is the a way to use placeholder in text field or any other field in lwc datatable?
Daniel Watson 21Daniel Watson 21
@Laveen Ekka 8
I am not sure there is. (I assume you mean placeholder text for the datatable fields when "editable" is set to true for inline editing.) Looking through the datatable documentation and am not seeing anything about that at least. Can anyone else verify that there isn't a non-documented attribute?

https://developer.salesforce.com/docs/component-library/bundle/lightning:datatable/example#lightningcomponentdemo:exampleDatatableInlineEdit