Apex – How to Correctly Map Response Data from Apex Controller to Display on DataTable

apexlightning-datatablelightning-web-components

I want to display the data that I get from apex controller to dataTable and I have trouble mapping the different columns correctly to the map.

I have a @AuraEnabled method that returns List of Object on call:

EDIT 1:

if (res.getStatusCode() == 200){ 
                Map<String,Object> results = (Map<String, Object>)JSON.deserializeUntyped(res.getBody());
                List<Object> responseData = (List<Object>) results.get('data');
                // Return data List<Object>
                return responseData;                    
            }

When I make the API call, the server always returns 3 values + x (those who you required) per row, like so — >

Apex execute anonymous system.debug of the responseJSON

((1802523701456533, 701300b97e2e1000, 2021-03-08T03:09:57, (1, Smartphone)),(15250809629146723, 5957676d3b753f69, 2021-02-18T07:19:04, (1, Smartphone)), (20487780899778413, 7a5911e7f5731400, 2021-01-26T15:54:48, (0, Desktop)), (27450806533608027, 186af40e19b1cfdf, 2021-02-27T00:55:45, (1, Smartphone)), (41195804276029081, 2ec0802a5c592800, 2021-01-22T06:33:13, (0, Desktop)), (41346057691525410, 67e89542fb518400, 2021-02-06T07:47:28, (1, Smartphone)) ....

The first one is Session ID, second Visitor ID and Timestamp

I want to display in this datatable:

Aura cmp:

<aura:component>

        <!-- ATTRIBUTES -->
        <aura:attribute name="columns" type="List"/>
        <aura:attribute name="dataTableData" type="Object" default=""/>
        
        <aura:attribute name="parentValue" type="String" default=""/>
        <!-- HANDLERS-->
        <aura:handler name="init" value="{!this}" action="{!c.init}"/>
        
        <div style="height: 1000px">
            <lightning:datatable
                                 keyField="id"
                                 data="{! v.dataTableData }"
                                 columns="{! v.columns }"
                                 hideCheckboxColumn="true"
                                 resizeColumnDisabled="true"
                                 minColumnWidth="100"
                                 maxColumnWidth="1000" />
        </div>
    </aura:component>

Controller.js

var selectedDimensions = component.get("v.columns");
        var columnsDataTable = [];
        
        if(component.get("v.parentValue") === "Sessions"){
            var columnsDataTableSession = ['Session ID','Visitor ID', 'Timestamp'];
            for (var i in columnsDataTableSession){
                columnsDataTable.push({label: columnsDataTableSession[i],
                                       fieldName: columnsDataTableSession[i],
                                       type: 'String'
                                      });
            }
        } 
 else {
                alert("Can't fetch the data right now, try later.");
        }
        
        for (var i in selectedDimensions) {
            columnsDataTable.push({label: selectedDimensions[i],
                                   fieldName: selectedDimensions[i],
                                   type: 'String'
                                  });
        }
        component.set("v.columns", columnsDataTable);
    },

With the for loop I'm declaring columns dynamically for the datatable based on previously selected picklist values.

I'm fetching data from another component and sending it to display in this one.

Fetching function is:

fetchData : function(component, event, helper) {
        
        if(component.get("v.parentValue") === "Sessions"){
            var action = component.get("c.fetchSessions");
            action.setParams({
                webSite: component.get('v.webSiteId'),
                selectedSiteName: component.get('v.selectedSite'),
                stringColumns: component.get('v.childValue'),
                date_from: component.get('v.dateFrom'),
                date_to: component.get('v.dateTo'),
                token: component.get('v.accessToken')
            });
            action.setCallback(this, function(response){
                var state = response.getState();
                if(state === "SUCCESS") {
                    var responseData = response.getReturnValue();
                    var jsonString = JSON.parse(JSON.stringify(responseData));
                    alert(responseData);
                    alert(jsonString);
                    component.set("v.dataTableData", jsonString);
                }
                else {
                    alert(response.getBody());;
                }
            });
            $A.enqueueAction(action); 

alert(responseData); –>

Alert(responseData) that I get after calling apex method in aura

DataTable after I got the response and set the data to dataTable –>
Datatable empty

So, how can I map the returnData to the datatable knowing that I will always have 3 columns returning Visitor ID, Session ID, Timestamp + those that I request?

Thank you.

Best Answer

The datatable "data" property expects an Array of objects with key/value pairs. The key should map to the "fieldName" property of the columns array.

So, if I'm reading the code correctly, your return object would need to look something like this:

[ 
  { "Session ID" : "1802523701456533",
    "Visitor ID" : "701300b97e2e1000",
    "Timestamp"  : "2021-03-08T03:09:57"
  }, ...
]

You'll need to do some post processing on your server result to get it in a similar format for use in the datatable component.

As an example, you can loop through each object in the 'jsonString' array, split the string on "," (https://www.w3schools.com/jsref/jsref_split.asp) delimiter and assign the values as needed. You may need to strip whitespace as well. This approach is very tightly coupled to the result of the HTTP callout, so I wouldn't recommend it unless you know it will never change. (I haven't tested the code below, but should work). ...

   if(state === "SUCCESS") {
     var responseData = response.getReturnValue();
     var jsonString = JSON.parse(JSON.stringify(responseData));
     var tableArr = [];
     jsonString.forEach(row => {
        var strParts = row.split(",");
        tableArr.push({
           "Session ID": strParts[0],
           "Visitor ID": strParts[1],
           "Timestamp": strParts[2],
        });
     });
     component.set("v.dataTableData", tableArr);

     ...