I would enjoy using the easy to set up lightning-datatable, but it just lacks any ability to really customize it. It would work, but I need text-wrapping in cells instead of truncation. And it lets the user do that with the drop down, but we have these tables in visualforce pages inside apex:forms, which causes the entire page to reload if you click the little arrow in the header to change from Clip text to Wrap text....

So, I am going for a custom html table with slds styling. This is what I am attempting to do:

<table class="slds-table slds-table_bordered slds-table_striped">
        <tr class="slds-line-height_reset">
            <template for:each={tableColumns} for:item="column"><!--Need the LWC html repeat-->
                <th key={column.fieldName} class="slds-text-title_caps slds-cell-wrap" scope="col">{criterion.label}</th>
        <template for:each={tableData} for:item="criterion" for:index="index">
            <tr key={criterion.id}>
                <template for:each={tableColumns} for:item="column">
                    <td key={}>{criterion[column.fieldName]}</td>

My question comes with the <td key={}>{criterion[column.fieldName]}</td>.

In order to prevent any kind of hard coding of table columns or cells, doing the double iteration would be nice, but I'll need to dynamically access the value of criterion. Is such a thing possible? Is there any way to do this? It doesn't seem to like how I have it at the moment (I know key is missing still).

  • I would try something like <td data-column-field={column.fieldName} data-criterion={criterion}>{render}</td> and then in js render(el){ return el.dataset.criterion[el.dataset.columnField]; } although i am not 100% if the reference to element will be passed this way :/
    – zaitsman
    Feb 22, 2019 at 1:24

1 Answer 1


I have assumed, that data in criterion will be in this format.

    field1: "value"

As we can't use expressions in LWC HTML, you need to preprocess the table data into an array. Basically a two-dimensional array. Instead of having each row as an object, I changed it to an array of key, fieldApiName, and value because we need generic field identifiers.

let criterionDataArray = [];
this.criterionArray.forEach((criterionRow, index) => {
    let newRow = Object.keys(criterionRow).map((currentValue, key) => {
        return {
            fieldApiName: key,
            value: currentValue,
            key: `${index}${key}`
    criterionDataArray.push({ Id: index, data: newRow });
this.tableData = criterionDataArray;

This is how you can iterate that.


<template if:true={tableData} for:each={tableData} for:item="criterion" for:index="index">
    <tr key={criterion.Id}>
        <template if:true={criterion.data} for:each={criterion.data} for:item="row">
            <td data-field-api-name={row.fieldApiName} key={row.key}>{row.value}</td>

When you need to save the data back, you need to convert this into an original format. You can do the same using array.map method.

You must log in to answer this question.

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