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
    – Renji-xD
    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) 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
    – Renji-xD
    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.
    – user2018
    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? 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. 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!

2
  • Nice! Does this handle multiple levels (e.g. grand children, great grand children)?
    – streco
    Nov 30, 2019 at 1:57
  • @Valentine How to select child row alone in your case ? Feb 25, 2023 at 9:24
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.

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