title: How can I fix the URL fields in my Dynamic Lightning:Datatable so that the field labels display the name of each related record?
body: <p>I am working on a lightning component that will display a custom, dynamic, list of 'related' records that can be used to display related records in a list on an object that is ALSO a child of the same parent. So, for example, If the Account is the parent record, and custom object 1 and custom object 2 are both children of the account, I want to display the custom object 1 records that are associated with the same account as custom object 2 on the custom object 2 lightning record page. I am using user inputs in the LC canvas and field sets to locate the records and display the columns, respectively.</p>
  <p>The component I created works in that it does query and display the records, columns, and field values dynamically. I wanted to also include a clickable link to the custom object 1 records using the 'Name' field. However, I can't seem to dynamically set the typeAttribute of the Name field to display the label of the record. Because I am assigning it as an attribute of the column header, it is only taking the last value in the array.</p>
  <p>Is there a way to assign the typeAttribute to the records response, and NOT the column header response?</p>
  <p>Here is my code:</p>
  <p>Apex Class:</p>
  <p>public class DynamicRelatedListController {</p>
  <pre><code>@AuraEnabled
      public static String fetchParent(String recId, String parentLookup, String objPageName) {
          
         
          String strSOQL = 'SELECT Id, ' + parentLookup + ' FROM ' + objPageName + ' WHERE Id = \'' + recId + '\' LIMIT 1 ' ;  
  
         List &lt;SObject&gt; currentRecord = new List &lt;SObject&gt;(Database.query(strSOQL)); 
          
          system.debug('Database Result for fetch parent ' + strSOQL);
          
          List &lt;String&gt; parentIds = new List&lt;String&gt;();
          
          for(SObject cr : currentRecord) {
             
             parentIds.add(String.valueOf(cr.get(parentLookup)));
  
          }
  
         
           String parentId = parentIds[0];
          
          return parentId;
  
      }
  
  
      @AuraEnabled
      public static List &lt; SObject &gt; fetchChildren(String objectName, String criteria, String parentFieldAPIName, String recId, String parentLookup, String objPageName) {
  
          String parentId = fetchParent(recId, parentLookup, objPageName);
  
          // String prefix ='%' + GM_Util.findObjectNameFromRecordIdPrefix(parentId) + '%';
          
          List &lt; SObject &gt; childRecordList = new List &lt; SObject &gt; ();
          String strSOQL = 'SELECT Id, ' + parentFieldAPIName + ' FROM ' + objectName + ' WHERE ' + parentFieldAPIName + ' = \'' + parentId  + '\'';  
  
          if ( String.isNotBlank( criteria ) )  
              strSOQL += ' ' + criteria;  
       
  
          childRecordList = Database.query(strSOQL);
          
          system.debug('Database Result for fetch children ' + strSOQL);
  
          return childRecordList;
  
      }
  
     
  
      @AuraEnabled
      public static DataTableDetails fetchRelatedRecs(String recId, String fieldSetName, String criteria, String objectName, String parentFieldAPIName, String objPageName, String parentLookup)
          {  
              
        
              DataTableDetails dataTableDtls = new DataTableDetails();
              List&lt;GM_Util_Aura.FieldSetMemberWrapperClass&gt; listOfFieldSetMembers = new List&lt;GM_Util_Aura.FieldSetMemberWrapperClass&gt;();
              if(fieldSetName != NULL &amp;&amp; fieldSetName != ''){
                  listOfFieldSetMembers = GM_Util_Aura.getFieldSetMemberJSON(objectName, fieldSetName);                
              }
              // List&lt;GM_Util_Aura.FieldSetMemberWrapperClass&gt; listOfFieldSetMembers = GM_Util_Aura.getFieldSetMemberJSON(objectName, fieldSetName);
              List&lt;String&gt; WHERE_IN_LIST = new List &lt;String&gt;();
  
              for (SObject crl: fetchChildren(objectName, criteria, parentFieldAPIName, recId, parentLookup, objPageName)) {
  
                  WHERE_IN_LIST.add(crl.Id);
              }   
              
              String SQL = 'SELECT Id ';
              if(listOfFieldSetMembers != null &amp;&amp; listOfFieldSetMembers.size() &gt; 0){
                  for(GM_Util_Aura.FieldSetMemberWrapperClass fsmwc : listOfFieldSetMembers){
                      dataTableDtls.fieldLabelsList.add(fsmwc);
                      
                      SQL = SQL + ',' +  fsmwc.fieldName;
                  }
              }
              SQL += ' FROM ' + objectName +' WHERE Id IN : WHERE_IN_LIST' ;
              System.debug('final SQL statement (WHERE_IN_LIST): ' + SQL);
              
              dataTableDtls.returnedParentRecords = Database.query(SQL);
              System.debug('returned Parent Records are ' + Database.Query(SQL));
  
              
              
              // filteredResults = Database.query(SQL); 
              
              System.debug('final filtered results are: ' + dataTableDtls);
              
              System.debug('returned field labels are ' + listOfFieldSetMembers);
  
              return dataTableDtls;        
          }
  
  
          
          public class DataTableDetails{
              @AuraEnabled
              public List&lt;sObject&gt; returnedParentRecords = new List&lt;sObject&gt;();
              @AuraEnabled
              public List&lt;GM_Util_Aura.FieldSetMemberWrapperClass&gt; fieldLabelsList = new List&lt;GM_Util_Aura.FieldSetMemberWrapperClass&gt;();
          }
      }
  </code></pre>
  <p>My component helper &amp; cmp code:</p>
  <p><div class="snippet" data-lang="js" data-hide="false" data-console="true" data-babel="false">
  <div class="snippet-code">
  <pre class="snippet-code-js lang-js prettyprint-override"><code>({
  
      getDataTable: function(component, event) {
      
          console.log("entered Get Data Table");
      
          var action = component.get("c.fetchRelatedRecs");
          action.setParams({
              recId: component.get("v.recordId"),
              objectName: component.get("v.ObjectAPIName"),
              fieldSetName: component.get("v.FieldSetAPIName"),
              objPageName: component.get("v.sObjectName"), 
              parentFieldAPIName: component.get("v.ParentFieldAPIName"),
              parentLookup: component.get("v.ParentFieldLookupName"),
              criteria: component.get("v.criteria")
          });
          action.setCallback(this, function(response){
              //console.log("this is the fieldSetName " + fieldSetName)
              var state = response.getState();
              if(state === 'SUCCESS'){
      
                  var fieldSetResponse = response.getReturnValue().fieldLabelsList;
                  var parentRecordsResponse = response.getReturnValue().returnedParentRecords;
      
                  console.log("Parent records length " + parentRecordsResponse.length);
                  console.log("Field Set Length " + fieldSetResponse.length);
                  console.log("here are the field labels before data transformation " + JSON.stringify(fieldSetResponse));
                  console.log("Here are all the related records before transformation " + JSON.stringify(parentRecordsResponse));
      
                  
                  //this for loop changes all lookup fields to type 'url' for clickable links
                  for(var i = 0; i &lt; parentRecordsResponse.length; i++ ) {
   
                      
                      if(fieldSetResponse[i].fieldName.includes('__r.Name')) {
                          fieldSetResponse[i].type ='url';
  
                          //drop the .Name from the lookup field so that the lightning:datatable can find the field value in the parentRecordsResponse object 
                          fieldSetResponse[i].fieldName = fieldSetResponse[i].fieldName.replace('.Name', '') ;
      
                              //this for loop locates the key in the parentRecordsResponse object that matches the lookup field in the fieldSetResponse object. 
                             for(var j = 0; j &lt; parentRecordsResponse.length; j++) {
                              var arraykeys = Object.keys(parentRecordsResponse[j]);
                              var arrayvalues = Object.values(parentRecordsResponse[j]);
                                 
                                 console.log("Array Keys in iteration "+ arraykeys[j]);
                                 console.log("Array Values " + JSON.stringify(arrayvalues));
  
      
                              //this for loop locates the key in the parentRecordsResponse object that matches the lookup field in the fieldSetResponse object. 
                               for(var h = 0; h &lt;arraykeys.length; h++) {
                                   
                                   console.log("Array Keys in iteration h "+ arraykeys[h]);
                                   
                                   //fieldSetResponse[i]['typeAttributes'] = {label: arrayvalues[h].Name };
                                   
                                   if(fieldSetResponse[i].fieldName === arraykeys[h]){
                                       
                                       
                                  
                                       //assign the'type attributes' to the url field with the Name value of the lookup field
                                       fieldSetResponse[i]['typeAttributes'] = {label: arrayvalues[h]['Name'] };
                                         
                                          //transform the nested lookup field object to /recordId for the url
                                       parentRecordsResponse[j][arraykeys[h]] = "/" + arrayvalues[h].Id ;
  
      
                                   }
                               }
      
                          }
      
                      } 
                  }
      
                      console.log("here are the field labels after data transformation " + JSON.stringify(fieldSetResponse));
                      console.log("Here are all the related records " + JSON.stringify(parentRecordsResponse));
      
                      //send the responses back to the controller and set them using the 'v.' keys
                      component.set("v.columnsHeader", fieldSetResponse);
                      component.set("v.listofRelatedRecords", parentRecordsResponse); 
              }
      
                  else if (state === 'ERROR'){
                  console.log('::::::::::::: ERROR :::::::::::::');
              }
          });
          $A.enqueueAction(action);
      }
      
      
      })</code></pre>
  <pre class="snippet-code-html lang-html prettyprint-override"><code>&lt;aura:component controller="DynamicRelatedListController" implements="flexipage:availableForRecordHome,force:hasRecordId,force:lightningQuickAction,force:hasSObjectName" access="global"&gt;
  
      &lt;aura:handler name="init" value="{!this}" action="{!c.doInit}" /&gt;
      &lt;aura:attribute name="ObjectAPIName" type="String"/&gt;
      &lt;aura:attribute name="FieldSetAPIName" type="String"/&gt;
      &lt;aura:attribute name="ParentFieldAPIName" type="String"/&gt;
      &lt;aura:attribute name="ParentFieldLookupName" type="String"/&gt;
      &lt;aura:attribute name="Criteria" type="String"/&gt;
      &lt;aura:attribute name="TableName" type="String"/&gt;
      &lt;!-- &lt;aura:attribute name="recordId" type="String" /&gt; --&gt;
      &lt;aura:attribute name="columnsHeader" type="List"/&gt;
      &lt;aura:attribute name="listofRelatedRecords" type="Object"/&gt;
  
  
      &lt;lightning:card aura:id="lightCard" class="slds-card_boundary" title="{!v.TableName}" iconName="standard:file"&gt;    
          &lt;div style="overflow-x: auto;"&gt;
              &lt;lightning:datatable data="{!v.listofRelatedRecords}"
              columns="{!v.columnsHeader}"
              keyField="Id"
              hideCheckboxColumn="true"/&gt;  
          &lt;/div&gt;
      &lt;/lightning:card&gt; 
  &lt;/aura:component&gt;</code></pre>
  </div>
  </div>
  </p>
  <p>This is what the end result looks like - note that all of the values ARE different, except for the label of the Name field. The link itself works fine, and navigates to the appropriate record:</p>
  <p><a href="https://i.stack.imgur.com/chOtt.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/chOtt.png" alt="Lightning Component UI" /></a></p>