2

Edit 4

I'm using a lightning-datatable implementation where I'm populating the data and columns from a wrapper Apex object properties.

Parent component call server-side method, that returns a List of my wrapper object:

Apex Wrapper Object:

public class ColumnAndDataWrapper { @AuraEnabled public List<SObject> recordList; @AuraEnabled public List<Column> columns; @AuraEnabled public String objectPluralLabel; @AuraEnabled public String iconName; public ColumnAndDataWrapper(List<SObject> recordList, List<Column> columns, String objectPluralLabel, String iconName) { this.recordList = recordList; this.columns = columns; this.objectPluralLabel = objectPluralLabel; this.iconName = iconName; } } 

Apex object Column:

public class Column { @AuraEnabled public String label; @AuraEnabled public String fieldName; @AuraEnabled public String fieldType; public Column(String label, String fieldName, String fieldType) { this.label = label; this.fieldName = fieldName; this.fieldType = fieldType; } } 

Lightning Datatable implementation

<template> <template for:each={records} for:item="record"> <div class="slds-var-m-top_medium" key={record.key}> <lightning-card icon-name={record.iconName} title={record.objectPluralLabel}> <div class="slds-card__body slds-card__body_inner"> <c-icrm-rich-datatable key-field={record.key} data={record.recordList} columns={record.columns} onrowaction={handleRowAction} show-row-number-column hide-checkbox-column enable-infinite-loading > </c-icrm-rich-datatable> </div> </lightning-card> </div> </template> </template> 

Client-side controller

From Jake's answer, if do record.columns.push(newCol), I get an error because the record.columns is a Javascript Object and not an Array/Map.

I have tried to parse the object to an array by implementing the Object.entries() method like this:

export default class RetireContactDatatable extends LightningElement { @api objectRecordsMap = []; @track records = []; connectedCallback() { let _map = this.objectRecordsMap; let newCol = { type: 'action', typeAttributes: { rowActions: actions } }; for (let _key in _map) { if (_map.hasOwnProperty(_key)) { let record = { key: _key, iconName: _map[_key].iconName, objectPluralLabel: _map[_key].objectPluralLabel, recordList: Object.entries(_map[_key].recordList), columns: Object.entries(_map[_key].columns) }; record.columns.push(newCol); this.records.push(record); } } } } 

This is how it looks:

enter image description here

From the debug console:

 [ [ "0", { "fieldName":"Name", "fieldType":"text", "label":"Grant Report Name" } ], [ "1", { "fieldName":"Award_Amount__c", "fieldType":"currency", "label":"Award Amount" } ], [ "2", { "fieldName":"Docket_Record__c", "fieldType":"reference", "label":"Docket Record" } ], { "type":"action", "typeAttributes":{ "rowActions":[ { "label":"Edit", "name":"edit_record", "iconName":"utility:edit" }, { "label":"Delete", "name":"delete_record", "iconName":"utility:delete" } ] } } ] 

I'd say I'm close, but as you see the data is not rendering.

At this point I've tried several approaches and non of them seems to work as I expect.

I took another approach by using Object.assign({}, data) method instead of parsing, this way I should be able to update the column value.

connectedCallback() { let _map = this.objectRecordsMap; let newCol = { type: 'action', typeAttributes: { rowActions: actions } }; for (let _key in _map) { if (_map.hasOwnProperty(_key)) { let record = { key: _key, iconName: _map[_key].iconName, objectPluralLabel: _map[_key].objectPluralLabel, recordList: _map[_key].recordList, columns: _map[_key].columns }; record.columns = Object.assign(newCol, record.columns); this.records.push(record); } } } 

But this is not working either, just shows the card header with title and icon.

4
  • 1
    Did you try to update the this.keyValue.value.columns instead? Probably the keyValue will need to be decorated with @track. Commented Aug 4, 2021 at 6:15
  • I don't think that could work, I tried tho, and can confirm didn't work, keyValue is the current item for the for:each directive, is not a property on the LWC, like objectRecordsMap for exmaple. Commented Aug 4, 2021 at 18:26
  • Nah, you have to just assign a totally new columns array (with your added values). It's not going to detect any other method of addition. Commented Aug 8, 2021 at 4:41
  • @CasparHarmer even doing that, then the data will not render, I've tried that also. Commented Aug 9, 2021 at 18:58

2 Answers 2

3
+25

IIUC, you have an apex object that has a columns property, which you pass to your lwc. In your lwc, you want to add an action column for edit and delete.

Assuming your c-icrm-rich-datatable is extending the lightning-datatable component, the columns and actions property needs to look like this:

const actions = [ { label: 'Edit Record', name: 'edit_record' }, { label: 'Delete', name: 'delete_record' } ]; const columns = [ { type: 'action', typeAttributes: { rowActions: actions, menuAlignment: 'left' } } ]; 

Note that columns and actions are arrays of objects. Each column on your table is an object which is an element in the columns array.

In your loop, you can do this:

 let newCol = { type: 'action', typeAttributes: { rowActions: actions } }; for (let _key in _map) { if (_map.hasOwnProperty(_key)) { let record = { key: _key, iconName: _map[_key].iconName, objectPluralLabel: _map[_key].objectPluralLabel, recordList: _map[_key].recordList, columns: _map[_key].columns }; record.columns.push(newCol); } this.records.push(record); } 

Check the "Creating Static Row-Level Actions" section here for more details.

Edit: Assumptions:

Your Apex object Column looks like this:

public class Column { @AuraEnabled public String fieldName {get; set;} @AuraEnabled public String fieldType {get; set;} @AuraEnabled public String label {get; set;} } 

So when you do _map[_key].columns in your loop, it looks like this

[ { "fieldName": "Name", "fieldType": "text", "label": "Grant Report Name" }, { "fieldName": "Award_Amount__c", "fieldType": "currency", "label": "Award Amount" }, { "fieldName": "Docket_Record__c", "fieldType": "reference", "label": "Docket Record" } ] 

And after you do record.columns.push(newCol);, if you ran a console.log(record.columns) It would look like this:

[ { "fieldName": "Name", "fieldType": "text", "label": "Grant Report Name" }, { "fieldName": "Award_Amount__c", "fieldType": "currency", "label": "Award Amount" }, { "fieldName": "Docket_Record__c", "fieldType": "reference", "label": "Docket Record" }, { "type": "action", "typeAttributes": { "rowActions": [ { "label": "Edit", "name": "edit_record", "iconName": "utility:edit" }, { "label": "Delete", "name": "delete_record", "iconName": "utility:delete" } ] } } ] 
3
  • you got it right, that's what I'm trying to achieve. to add a column to the datatable, tho I'm passing a List of the Apex object, instead of just one. I've tried your method before and did again just in case and it errors Uncaught (in promise) TypeError: 'set' on proxy: trap returned falsish for property '3'. I'll update the question with new insights about my blocker. Commented Aug 9, 2021 at 18:57
  • 1
    Based on your post, the columns property in your apex object is a list. According to your post update, it's a Javascript object. For it to work with the lightning-datatable, it needs to be a list of objects like I showed in my answer. I've added some sample json based on what you've shown. Commented Aug 9, 2021 at 20:12
  • I get this error: Uncaught (in promise) TypeError: 'set' on proxy: trap returned falsish for property '3' because I'm trying to push a value to an Object, for the case of the column. Commented Aug 10, 2021 at 1:57
1

Since the Wrapped Object has a property that is a List (of Apex object Column), I realized that I had to loop through that List and push each element to my LWC tmp record column property.

@api objectRecordsMap = []; @track records = []; connectedCallback() { let _map = this.objectRecordsMap; let newCol = { type: 'action', typeAttributes: { rowActions: actions } }; for (let _key in _map) { if (_map.hasOwnProperty(_key)) { let record = { key: _key, iconName: _map[_key].iconName, objectPluralLabel: _map[_key].objectPluralLabel, recordList: _map[_key].recordList }; let cols = []; _map[_key].columns.forEach(element => { cols.push(element); }); cols.push(newCol); record.columns = cols; this.records.push(record); } } } 

Since I'm pushing values each time we loop through the list of records, the record.column would get all the columns for all the objects records (1 loop: 3 cols, 2 loop: 1 Loop. Cols + 2 Loop. Cols and so on), to avoid this, I created a temp var to hold the columns, and then set the record.columns to this array, and clean it before the next assignment.

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.