[SalesForce] Make child rows selected on selecting parent row – lwc tree grid

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

Best Answer

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;
        }
    }