Reputation: 7215
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
Upvotes: 1
Views: 2057
Reputation: 26075
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.
Upvotes: 1