9

I am trying to create a custom Tree Grid component in that based on my business use case I'm in need to create every row as a component, and every column as a component. Below is the structure of my component

table container component

<template>
   <table>
    <thead>
    </thead>
    <tbody>
     <template for:each={datas} for:item="d">
            <c-child key={d.Id} data={d}></c-child>
       </template>
    </tbody>
  </table>

</template>

Child component

<template>
  <td>{data.Name}</td>
  <td>{data.age}</td>
  <td>{data.num}</td>
  <td>{data.Email}</td>
          <template for:each={data.childrens} for:item="d">
                <c-child key={d.Id} data={d}></c-child>
           </template>
</template>

Child.css

:host {
  display: table-row;
}

Here My Question is when I try to build a row component recursively for the tree structure . Since My row component doesn't contain <tr> I cannot able to render recursively because every custom element(LWC component) includes a tag with the component name in HTML Lightning Web Component for table rows and cellsbased on this link every row component behaves like a row when applying display: table-row;

So My nested tree structure looks like when I try to build the row component recursively, So my host component behaves like single tr

<c-row>
   <td></td>
   <td></td>
   <c-row>
   <c-row>
</c-row>

Here is a Playground link that I described my issue with a simple example.

UPDATE :

Below is the screenshot of UI I am trying to build

Example Tree Grid

7
  • Any reason not using lightning-tree or lightning-tree-grid?
    – Atlas Can
    Commented Dec 27, 2019 at 6:54
  • We need inline edit feature in tree grid and some other business use cases which cannot be covered in tree grid so we planned to build custom grid component
    – vignesh
    Commented Dec 27, 2019 at 9:06
  • can you explain exactly what UI you are trying to render? any screenshot of that? Commented Dec 27, 2019 at 10:15
  • I just want to render like slds tree grid lightningdesignsystem.com/components/tree-grid and I provided the playground URL above it describes the issue which I am facing'
    – vignesh
    Commented Dec 27, 2019 at 12:26
  • @rahulgawale Here is the screenshot of UI Iam trying to build drive.google.com/file/d/1r7Wh_vADwBbD11BI4NaZwz1N95DQ0tMI/…
    – vignesh
    Commented Dec 27, 2019 at 13:17

2 Answers 2

5
+50

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:'[email protected]' },
    { Name:'st1', age:'2', Id:2, num:'342222222', Email:'[email protected]', children:[
        { Name:'st1child', Id:6, age:'2', num:'342222222', Email:'[email protected]' },
        { Name:'st1child', Id:7, age:'2', num:'342222222', Email:'[email protected]' }, ]
    },
    { Name:'st1', age:'2', Id:3, num:'342222222', Email:'[email protected]' },
    { Name:'st1', age:'2', Id:4, num:'342222222', Email:'[email protected]' },
    { Name:'st1', age:'2', Id:5, num:'342222222', Email:'[email protected]' },
];
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;
    }
}
2
  • 1
    I think this is not really solving the issue since it is forcing to change the data structure from a tree to a list of elements. Is there a way to maintain the tree structure and still render the children data? Thanks! Commented May 13, 2020 at 14:02
  • @IsmaelSlimane you can add this transformation on the getter, so you both store you tree structure and list display
    – ytiq
    Commented May 13, 2020 at 15:54
1

I think there are are two ways to achieve what you want:

  1. Don't change underlying data structure but change UI markup handle data structure.
  2. 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

enter image description here

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:

  1. how many levels deep the original data structure is going to be
  2. Is Id going to be unique across entire hierarchy.
3
  • You hard-coded styling, so you didn't solve the issue for properly displaying the underlying data
    – ytiq
    Commented Dec 29, 2019 at 17:41
  • @ytiq we can solve that while flattening the data. My point was to show that we don’t need to work with the same data structure that we are getting from server. I will try to update logic on how to flatten the data structure.
    – javanoob
    Commented Dec 29, 2019 at 18:12
  • @ytiq check my upddated playground. I think this is what you referring to in your previous comment.
    – javanoob
    Commented Dec 29, 2019 at 22:33

You must log in to answer this question.

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