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.
    – Damecek
    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.
    – AjFmO
    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. Aug 8, 2021 at 4:41
  • @CasparHarmer even doing that, then the data will not render, I've tried that also.
    – AjFmO
    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.
    – AjFmO
    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. 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.
    – AjFmO
    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.

Not the answer you're looking for? Browse other questions tagged .