I think there are are two ways to achieve what you want:
- Don't change underlying data structure but change UI markup handle data structure.
- Change underlying data structure but keep UI markup simple.
I chose option 2 and was able to get this output:
Link to playground: https://developer.salesforce.com/docs/component-library/tools/playground/KG-dO5AL/2/edit
app.html:
<template>
<table>
<thead>
<th>Name</th>
</thead>
<tbody>
<template for:each={tableData} for:item="rowData">
<tr key={rowData.Id} class={rowData.rowStyle}>
<td>
<span style={rowData.nameStyle}>
<lightning-icon icon-name={rowData.iconName}
size=x-small
onclick={showOrHideChildrenRows}
data-rowid={rowData.Id}
data-expanded="false">
</lightning-icon>
{rowData.Name}
</span>
</td>
</tr>
</template>
</tbody>
</table>
</template>
app.js:
import { LightningElement, track } from "lwc";
export default class App extends LightningElement {
@track
tableData =[
{
Name: "United States",
Id: 1,
iconName: "utility:chevronright",
parentId : null,
rowStyle : "",
nameStyle : ""
},
{
Name: "Massachusetts",
Id: 2,
iconName: "utility:chevronright",
parentId: 1,
rowStyle : "hide",
nameStyle : "margin-left:10px;"
},
{
Name: "Boston",
Id: 3,
iconName: "",
parentId: 2,
rowStyle : "hide",
nameStyle : "margin-left:20px;"
},
{
Name: "New York",
Id: 4,
iconName: "",
parentId : 1,
rowStyle : "hide",
nameStyle : "margin-left:10px;"
},
{
Name: "Vatican City",
Id : 5,
iconName: "",
parentId : null,
rowStyle : "",
nameStyle : ""
},
{
Name: "Canada",
Id : 6,
iconName: "utility:chevronright",
parentId : null,
rowStyle : "",
nameStyle : ""
},
{
Name: "Ontario",
Id : 7,
iconName: "",
parentId : 6,
rowStyle : "hide",
nameStyle : "margin-left:10px;"
},
{
Name: "Alberta",
Id : 8,
iconName: "",
parentId : 6,
rowStyle : "hide",
nameStyle : "margin-left:10px;"
}
];
showOrHideChildrenRows(event){
let rowId = event.target.dataset.rowid;
let isExpanded = event.target.dataset.expanded;
event.target.iconName = JSON.parse(isExpanded) ? "utility:chevronright": "utility:chevrondown";
event.target.dataset.expanded = JSON.stringify(!JSON.parse(isExpanded));
this.tableData = this.tableData.map((obj) => {
if(obj.parentId == rowId && !JSON.parse(isExpanded)){
obj.rowStyle = "";
}
if(obj.parentId == rowId && JSON.parse(isExpanded)){
obj.rowStyle = "hide";
}
return obj;
});
console.log(this.tableData);
}
}
Each element in the data structure has these special attributes:
parentId
- to relate each element to its parent element.rowStyle
- to hide or show children elements.nameStyle
- to apply proper indentation to parent elements.
EDIT:
As per ytiq's comment I added the logic on how to flatten the data given in the question. here is the updated playground
Note that the logic to flatten the original data structure may need to be changed based on factors like:
- how many levels deep the original data structure is going to be
- Is Id going to be unique across entire hierarchy.