1
votes

I am facing an odd issue with a PrimeNG 9 TreeTable in an application I am working on which I have configured virtual scrolling and a nested node structure with toggle\expand behaviour.

I am finding after I make a selection, the highlighted node is lost when either scrolling or expanding another node. The highlighted selection jumps to another random node.

Minimal reproducible demo can be found here: https://stackblitz.com/edit/primeng9-treetablescroll-selections

I was wondering if this may be to do with the this.cdr.detectChanges(); which I added in the ngAfterViewInit lifecycle hook, which I added to bring in this bug fix in a v10.0.3 release but removing this makes no difference and brings back an ExpressionChangedAfterItHasBeenCheckedError which the bug fix addresses.

The application cannot be migrated to PrimeNG 10 yet either, so I am specifically looking for a v9 fix\workaround if possible.

Anyone got any ideas what may be going on here?

SOLVED

Kudos to @DipenShah for pointing me in the right direction to find a suitable workaround. Based on his answer I enhanced his approach a little further to handle the case that in v9 the tree table fires the expand\collapse events AND the node select event when toggling the parent nodes. This does not happen in v10.

My final workaround is here https://stackblitz.com/edit/primeng9-treetablescroll-selections-utsj2p?file=src/app/app.component.ts

1
In your example, I can't see any code.StPaulis
@StPaulis - my bad, demo link updated, thanksmindparse
Not sure whats going on with that version you're using but it seems buggy.. the link you provide also links to primeng9-treetablescroll-selections.stackblitz.io for a full page demo. I notice the last element is supposed to be 999 (and it is initially) but when I expand/collapse a row or two row 994 ends up at the end somehow. My immediate thoughts would be this is buggy, don't use it.Rob Evans
@RobEvans - if this wasnt buggy, I wouldnt be asking this question in the first place!mindparse
fair enough :) personally I'd find another library to use that isn't apparently broken to begin withRob Evans

1 Answers

1
votes

There seems to be an issue with the library itself and with recycling views before reusing them during scrolling.

Fortunately, if you want to take things in to your hands, by manually adding highlighted class you can fix the issue. Take a look at updated stackblitz.

app.component.ts

import {AfterViewInit, ChangeDetectorRef,  Component,OnDestroy,OnInit, ViewChild} from '@angular/core';
import {NodeService} from './nodeservice';
import {TreeNode} from 'primeng/api';
// import { PrimeNGConfig } from 'primeng/api';
import { TreeTable } from 'primeng/treetable';
import { Subscription } from 'rxjs';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.scss']
})
export class AppComponent implements AfterViewInit, OnDestroy { 
    
    @ViewChild(TreeTable)
    private virtualTable: TreeTable;

    virtualFiles: TreeNode[];
    selectedNode: TreeNode;

    cols: any[];
  
    private virtualTableSub: Subscription;

    constructor(private cdr: ChangeDetectorRef) { }

    ngOnInit() {
        this.virtualFiles = Array.from({length: 1000}).map((_,i) => this.createNode(i, 100));

        this.cols = [
            { field: 'name', header: 'Name' },
            { field: 'size', header: 'Size' },
            { field: 'type', header: 'Type' }
        ];
    }

    expanded(e) {
      this.selectedNode = null;
      setTimeout(() => {
        this.selectedNode = e.node;
        this.cdr.detectChanges();
      });
    }

    collapsed(e) {
      this.selectedNode = null;
      setTimeout(() => {
        this.selectedNode = e.node;
        this.cdr.detectChanges();
      });
    }

    ngAfterViewInit() {
      this.virtualTableSub = this.virtualTable.tableService.uiUpdateSource$.subscribe(() => {
        if (this.virtualTable.virtualScroll) {
          this.cdr.detectChanges();
        }
      });
    }

    ngOnDestroy() {
      this.virtualTableSub?.unsubscribe();
    }

    createNode(i: number, children: number): TreeNode {
        let node: TreeNode = {
            data: {name: 'Node ' + i, type: 'virtual node', size: Math.ceil(Math.random() * 10000) + 'kb'},
            children: Array.from({length: children}).map((_,j) => {
                return { 
                    data: {name: 'Node ' + i + '.' + j, type: 'virtual child node', size: Math.ceil(Math.random() * 10000) + 'kb'}
                }
            })
        };

        return node;
    }
}

app.component.html

<div class="card">
    <h5>Full Page Scroll</h5>
    <p>FlexScroll can also be used for cases where scrollable viewport should be responsive with respect to the window
        size. See the <a [routerLink]="['/treetable/flexscroll']">Full Page</a> demo for an example.</p>

    <h5>Virtual Scroll with 100000 Nodes</h5>
    <p-treeTable #treeTable [value]="virtualFiles" [columns]="cols" [scrollable]="true" [rows]="100"
        selectionMode="single" [(selection)]="selectedNode" (onNodeExpand)="expanded($event)"
        (onNodeCollapse)="collapsed($event)" scrollHeight="200px" [virtualScroll]="true" [virtualRowHeight]="34"
        dataKey="name">
        <ng-template pTemplate="header" let-columns>
            <tr>
                <th *ngFor="let col of columns">
                    {{col.header}}
                </th>
            </tr>
        </ng-template>
        <ng-template pTemplate="body" let-rowNode let-rowData="rowData" let-columns="columns">
            <tr [ttRow]="rowNode" [ttSelectableRow]="rowNode" style="height:34px"
                [ngClass]="{ 'highlighted' : selectedNode?.data === rowData }">
                <td *ngFor="let col of columns; let i = index">
                    <p-treeTableToggler [rowNode]="rowNode" *ngIf="i == 0"></p-treeTableToggler>
                    {{rowData[col.field]}}
                </td>
            </tr>
        </ng-template>
    </p-treeTable>
</div>

app.component.scss

::ng-deep.ui-treetable .ui-treetable-tbody > tr.highlighted {
  background-color: #57a0d7;
  color: #fff;
}

I would encourage you to open an issue on the library's github repository.