2

Is it possible to implement the logic - when parent row is selected all its child rows are selected as well with lightning web component and tree grid?

HTML:

<lightning-tree-grid
        key-field="name"
        data={treeItems}
        columns={columns}
        onrowselection={setSelectedRows}
        expanded-rows={requestExpandRows}
        selected-rows={selectedRows}>
</lightning-tree-grid>

JS file:

 @api locationId;
 @api allPO;

 @track data;
 @track error;


 @track selectedRows = [];
 setSelectedRows(){
          var selectRows = this.template.querySelector('lightning-tree-grid').getSelectedRows();

          if(selectRows.length > 0){
               var tempList = [];
               var tempListExpanded = [];
               selectRows.forEach(function (record){
                         tempList.push(record.id);
               })
               this.dataObj.forEach(function (record){
                        if(tempList.includes(record.id)){
                            record.items.forEach(function (child){
                               //tempListExpanded.push(record.id);
                               tempList.push(child.id);
                            })
                        }
                    })
               this.selectedRows = tempList;
               //this.requestExpandRows = tempListExpanded;
               console.log(this.selectedRows);
          }
      }

 @wire(getTreeGridData, { input: '$myValue', allAccs : '$allPO'})
 wireTreeData({
     error,
     data
 }) {
     if (data) {
         //  alert(data);
         this.setExpandedRows(data);
         var res = data;
         var tempjson = JSON.parse(JSON.stringify(data).split('items').join('_children'));
         console.log(tempjson);
         this.dataObj = data;
         this.data = tempjson;

         this.setSelectedRows(data);
     } else {
         this.error = error;
     }
 }

I wanted to implement something like that. The problem is onrowselection function has no access to @track variables.. Probably there are different execution contexts. Is there any workaround how we can make it possible with lwc?

Expected behavior:

enter image description here

4
  • not sure to which track you refer-you add the listener in your component and you have also access to every track it owns? I am also not sure where you got "childRows" from - the passed selected Rows have unfortunately only the information if it has children or not. I would suggest to continue with: 1. you can use the passed event.target to call the component method, 2. write a method to find the position of added node, 3. write a method which starts from this position and recursively call it if it has children to build a list of selected ids - then update your code example if you have issues Commented Jun 27, 2019 at 11:29
  • @Renji-xD Seems it's done. Works as expected. Thank you for your time! - now the question how can I know that the row was unselected? (to unselect the child as well) Commented Jun 27, 2019 at 22:28
  • 1
    you have to manually compare the old selectedRows property against the the data returned by getSelectedRows in your event handler- find the element which is present in selectedRows but not in the new list. There is no other provided information i am aware of Commented Jun 27, 2019 at 23:45
  • HI @ValentinePotapov, I know this is an old thread, but are you able to find out how to find the unselected row? Thank you. Commented Sep 15, 2022 at 7:29

3 Answers 3

2

I thought I'd provide an additional answer to this thread, since the accepted solution cannot accommodate de-selecting child rows when the parent is de-selected. This solution allows for de-selecting parents, de-selecting children separately from the parent, auto-selecting parents when all children are selected, and auto-deselecting parents when at least one child is deselected.

HTML:

<lightning-tree-grid 
    columns={columnList}
    data={projectList}
    key-field="Id"
    onrowselection={updateSelectedRows}
    selected-rows={selectedRows}>
</lightning-tree-grid>

JS:

import { LightningElement, api, track, wire } from 'lwc';
import getProjectRecords from '@salesforce/apex/cc_CreateInvoiceController.getProjectRecords';

export default class Test12 extends LightningElement {
    @api recordId;
    @track columnList;  // set by apex wire service using field set
    @track projectListObj;  // the data as returned by the apex wire service
    @track projectList;  // wire service data required modification for correct display
    @track selectedRows = [];
    @track currentSelectedRows;  // used to track changes

    @wire(getProjectRecords, {  projectId: '$recordId'   })
    wiredProjects({ error, data }) {
        var dataObj;

        if(data) {
            dataObj = JSON.parse(data);
            this.projectListObj = dataObj;
            this.projectList = JSON.parse(JSON.stringify(dataObj).split('items').join('_children'));
            }
        }
    }

    updateSelectedRows() {
        var tempList = [];
        var selectRows = this.template.querySelector('lightning-tree-grid').getSelectedRows();
        if(selectRows.length > 0){
            selectRows.forEach(record => {
                tempList.push(record.Id);
            })

            // select and deselect child rows based on header row
            this.projectListObj.forEach(record => {
                // if header was checked and remains checked, do not add sub-rows

                // if header was not checked but is now checked, add sub-rows
                if(!this.currentSelectedRows.includes(record.Id) && tempList.includes(record.Id)) {
                    record.items.forEach(item => {
                        if(!tempList.includes(item.Id)) {
                            tempList.push(item.Id);
                        }
                    })
                }

                // if header was checked and is no longer checked, remove header and sub-rows
                if(this.currentSelectedRows.includes(record.Id) && !tempList.includes(record.Id)) {
                    record.items.forEach(item => {
                        const index = tempList.indexOf(item.Id);
                        if(index > -1) {
                            tempList.splice(index, 1);
                        }
                    })
                }

                // if all child rows for the header row are checked, add the header
                // else remove the header
                var allSelected = true;
                record.items.forEach(item => {
                    if(!tempList.includes(item.Id)) {
                        allSelected = false;
                    }
                })

                if(allSelected && !tempList.includes(record.Id)) {
                    tempList.push(record.Id);
                } else if(!allSelected && tempList.includes(record.Id)) {
                    const index = tempList.indexOf(record.Id);
                    if(index > -1) {
                        tempList.splice(index, 1);
                    }
                }

            })

            this.selectedRows = tempList;
            this.currentSelectedRows = tempList;
        }
    }
2
  • does it work for nth level hierarchy? Commented Mar 2, 2021 at 12:31
  • 1
    @womanwhodevs im facing one issue whenever expand/collapsed click again onrowselection method called and cleared the exsisting child value. How to resolve . Please help. Commented Feb 25, 2023 at 8:41
3

After a couple of hours - I implemented solution:

HTML:

<lightning-tree-grid
        key-field="name"
        data={treeItems}
        columns={columns}
        onrowselection={setSelectedRows}
        expanded-rows={requestExpandRows}
        selected-rows={selectedRows}>
</lightning-tree-grid>

JS file:

 @api locationId;
 @api allPO;

 @track data;
 @track error;


 @track selectedRows = [];
 setSelectedRows(){
          var selectRows = this.template.querySelector('lightning-tree-grid').getSelectedRows();

          if(selectRows.length > 0){
               var tempList = [];
               selectRows.forEach(function (record){
                         tempList.push(record.id);
               })
               this.dataObj.forEach(function (record){
                        if(tempList.includes(record.id)){
                            record.items.forEach(function (item){
                               tempList.push(item.id);
                            })
                        }
                    })
               this.selectedRows = tempList;
               console.log(this.selectedRows);
          }
      }

 @wire(getTreeGridData, { input: '$myValue', allAccs : '$allPO'})
 wireTreeData({
     error,
     data
 }) {
     if (data) {
         //  alert(data);
         this.setExpandedRows(data);
         var res = data;
         var tempjson = JSON.parse(JSON.stringify(data).split('items').join('_children'));
         console.log(tempjson);
         this.dataObj = data;
         this.data = tempjson;
     } else {
         this.error = error;
     }
 }

It works fine now, once you select the parent row - child rows are selected automatically. If you know how to improve an algoritm - please suggest some changes!

3
  • Nice! Does this handle multiple levels (e.g. grand children, great grand children)? Commented Nov 30, 2019 at 1:57
  • @Valentine How to select child row alone in your case ? Commented Feb 25, 2023 at 9:24
  • what does the html look like? my select-rows doesnt seem to update? (checkboxes dont check) Commented Nov 21, 2024 at 14:32
1

One possible improvement would be :

 setSelectedRows(event){
          var selectRows = event.details.selectedRows;

The if doesn't require length calculation, the following is sufficient:

if(selectedRows){

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.