When I want to rebuild/extend some of the functionality for LWC I usually get styling from SLDS. You can check markup here for tree-grid.
Salesforce flattens data and uses aria-expanded, aria-level for styling. I would suggest the same. Here is the script to flatten data. I also updated playground with simple styling for Id
UPDATE:
Created separate playground to not override changes.
UPDATE
Added expand/hide.
Here is sample row markup with some code.
// rendered.js
let data = [
{ Name:'st1', Id:1, age:'2', num:'342222222', Email:'v@gmail.com' },
{ Name:'st1', age:'2', Id:2, num:'342222222', Email:'v@gmail.com', children:[
{ Name:'st1child', Id:6, age:'2', num:'342222222', Email:'v@gmail.com' },
{ Name:'st1child', Id:7, age:'2', num:'342222222', Email:'v@gmail.com' }, ]
},
{ Name:'st1', age:'2', Id:3, num:'342222222', Email:'v@gmail.com' },
{ Name:'st1', age:'2', Id:4, num:'342222222', Email:'v@gmail.com' },
{ Name:'st1', age:'2', Id:5, num:'342222222', Email:'v@gmail.com' },
];
const isExpandedDefault = false;
const flatIt = (data, level) => data.reduce((acc, row) => [...acc, Object.assign(row, {
level,
expanded: isExpandedDefault,
}), ...flatIt(row.children || [], level + 1)], []);
data = flatIt(data, 1);
<!-- parent.html -->
<template>
<table class="slds-tree">
<thead>
<th>Name</th>
<th>Age</th>
<th>Num</th>
<th>Email</th>
</thead>
<tbody>
<template for:each={datas} for:item="d">
<c-child key={d.Id} data={d}></c-child>
</template>
</tbody>
</table>
</template>
// Child.js
export default class Child extends LightningElement {
@track _data;
renderedCallback() {
}
@api
set data(value) {
this._data = value;
this.ariaExpanded = false;
this.ariaLevel = value.level;
}
get data() {
return this._data;
}
}
handleHeaderAction(event)
method should have the following code. Note the code change inside the IF block.
handleHeaderAction(event) {
const actionName = event.detail.action.name;
const colName = event.detail.columnDefinition.fieldName;
const columns = this.columns;
const activeFilter = this.activeFilter;
if(actionName !== activeFilter){
columns.map((col) => {
if(col.fieldName === colName){
var actions = col.actions;
actions.forEach((action) => {
action.checked = action.name === actionName;
});
}
});
this.activeFilter = actionName;
this.updateActivity(colName);
this.columns = columns;
}
}
updateActivity
method should be like this. Note the input parameter and the code change inside the IF block. Also, you will need to improve upon this code to handle scenarios when the user clicks on the header and filters to different values. This code will handle the filter scenario only once (i.e., If user selects the filter Call or Email, the datatable will be filtered. However, if the user switches back to All filter or any other filter without page refresh, no rows will be displayed)
updateActivity(columnName){
const rows = this.activityList;
const filter = this.activeFilter;
let filteredRows = rows;
if(filter !== 'all'){
filteredRows = rows.filter((row) => {
if(row[columnName] !== '' && row[columnName] !== undefined){
return row[columnName].toUpperCase() === filter.toUpperCase();
}
});
}
this.activityList= filteredRows;
}
Observations: Looking at the code and the JS console log, I see few things done incorrectly in your JS code.
In the handleHeaderAction(event)
method, the following line of code is not fetching the column index value. In the console log, the value shows up as -1 (implies that the column on which user took action is not found).
var idx = columns.indexOf(col);
The indexOf
method works for single dimensional arrays, but in this code we are working on a multi-dimensional array. All of the code after this line would either fail or not produce the required response.
In the updateActivity
method, the following piece of code logic doesn't make sense to me. Not sure if you just wrote this as a sample.
if(activeFilter.includes('email')){
return activeFilter === 'Email'
} else return activeFilter === 'Calls'
The variable activeFilter
is not declared in this method and instead it should have been the variable filter
. I suppose this code was written with reference from the LWC component reference. Not all the code from this reference would work as-is and is given only as a sample.
Keep a check on the following while writing LWC code. While these points seem trivial, bear in mind that the lightning platform minifies and converts your code into a slightly different structure before rendering to the browser. A missing semi-colon or a paranthesis, you change the code logic causing errors.
If you use functional javascript like map, forEach etc., do not omit ';' at the end. For example,
SomeArray.forEach((element) => { code block });
If you have IF-ELSE statement with only one like under IF or ELSE block, ensure that you use curly braces for that like and also, do not omit ';'' on that line. For example,
if(condition){
return true
} else
return false
should be written as
if(condition){
return true;
}
else{
return false;
}
Best Answer
Yes, that is possible.
The actions work pretty much the same in both Lightning Datatable and Tree Grid, in the Tree Grid Documentation its mentioned to refer to the Lightning Datatable documents for Actions.
See how row actions are set using a method
getRowActions
. In the below example, actions are rendered on the basis ofisActive
field.You can refer to this article (search for "Creating Dynamic Row-Level Actions") mentioning how to do that in detail.