PrimeNG Treetable - Set Frozen Columns Row Height to Same as Scrollable Columns Row Height

138 Views Asked by At

I am populating a PrimeNG TreeTable with data of variable content. The rows which display the scrollable columns can therefore have a variable height, depending on the data content of each column and the width of the viewport (since the content wraps when the viewport shrinks, thus increasing the scrollable column row height).

How can I set the row height of the frozen columns to match the row height of the scrollable columns? (I would have thought this would be the default behaviour and can't imagine why we would need any other behaviour). Surely there is a simple way to do this?

I have tried iterating through the rows of scrollable columns in ngAfterViewInit and then updating the row height of the frozen columns but this is too late in the lifecycle and results in the frozen columns disappearing entirely. And even if it worked it would be a nasty workaround.

1

There are 1 best solutions below

0
On

Answering my own question here:

IMHO, the PrimeNg TreeTable element itself ought to support matching of variable height body rows with their respective frozen columns, but below is a workaround which solves the problem:

HTML:

<p-treeTable [value]="files" [columns]="scrollableCols" [frozenColumns]="frozenCols" [scrollable]="true"
             frozenWidth="200px" [tableStyle]="{'min-width':'50rem'}" (onNodeExpand)="setRowHeight($event)"
             (onNodeCollapse)="setRowHeight($event)" >

    <ng-template pTemplate="header" let-columns>
        <tr>
            <th *ngFor="let col of columns">
                {{ col.header }}
            </th>
        </tr>
    </ng-template>

    <ng-template pTemplate="body" let-rowData="rowData" let-columns="columns">
        <tr #bodyRow [ngStyle]="{height: '65px'}">
            <td *ngFor="let col of columns;">
                <ng-container *ngIf="col.field != 'typeList'">{{ rowData[col.field] }}</ng-container>
                <ng-container *ngIf="col.field == 'typeList'">
                    <ng-container *ngFor="let value of rowData.typeList">
                        <tr>
                            <td>{{value}}</td>
                        </tr>
                    </ng-container>
                </ng-container>
            </td>
        </tr>
    </ng-template>

    <ng-template pTemplate="frozenbody" let-rowNode let-rowData="rowData">
        <tr #frozenBodyRow>
            <td>
                <p-treeTableToggler [rowNode]="rowNode"></p-treeTableToggler>
                {{ rowData.name }}
            </td>
        </tr>
    </ng-template>
</p-treeTable>

Typescript

import {
    AfterViewChecked,
    Component,
    ElementRef,
    HostListener,
    OnInit,
    QueryList,
    Renderer2,
    ViewChildren
} from "@angular/core";
import {TreeNode} from "primeng/api";
import {NodeService} from "../../service/nodeservice";

interface Column {
    field: string;
    header: string;
}

@Component({
    selector: "app-junk",
    templateUrl: "junk.component.html"
})

export class JunkComponent implements OnInit, AfterViewChecked {
    @ViewChildren("bodyRow") bodyRows!: QueryList<ElementRef>;
    @ViewChildren("frozenBodyRow") frozenBodyRows!: QueryList<ElementRef>;

    files: TreeNode[] = [];
    cols!: Column[];
    frozenCols!: Column[];
    scrollableCols!: Column[];

    constructor(private nodeService: NodeService, private renderer: Renderer2) {
    }

    setRowHeight(event: any) {
        this.setFrozenRowsHeight();
    }

    @HostListener("window:resize", ["$event"])
    onResize(event: any): void {
        this.setFrozenRowsHeight();
    }

    ngOnInit(): void {
        this.nodeService.getFileSystemNodesData().subscribe((files) => {
            this.files = files;
        });

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

        this.scrollableCols = [
            {field: "size", header: "Size"},
            {field: "typeList", header: "Type"},
            {field: "size", header: "Size"},
            {field: "typeList", header: "Type"},
            {field: "size", header: "Size"},
            {field: "typeList", header: "Type"}
        ];

        this.frozenCols = [{field: "name", header: "Name"}];
    }

    ngAfterViewChecked(): void {
        this.setFrozenRowsHeight();
    }

    private setFrozenRowsHeight(): void {
        let i: number = 0;

        this.bodyRows.forEach((rowElement) => {
            const height = rowElement.nativeElement.offsetHeight;
            const frozenRow = this.frozenBodyRows.get(i++);
            this.renderer.setAttribute(frozenRow!.nativeElement, "height", height + "px");
        });
    }
}

Service:

import {Injectable} from "@angular/core";
import {of} from "rxjs";

@Injectable({
  providedIn: "root"
})
export class NodeService {

  getFileSystemNodesData() {
    return of([
      {
        "data": {
          "name": "Applications",
          "size": "200mb",
          "typeList": [
            "Folder1",
            "Folder2",
            "Folder3 ----------- blblb blblb  blblb blblb blblb blblb blblb blblb blblb blblb blblb blblb blblb blblb blblb blblb blblb blblb blblb blblb blblb blblb blblb "
          ]
        },
        "children": [
          {
            "data": {
              "name": "Angular",
              "size": "25mb",
              "typeList": [
                "FolderA",
                "FolderB",
                "FolderC"
              ]
            },
            "children": [
              {
                "data": {
                  "name": "angular.app",
                  "size": "10mb",
                  "typeList": [
                    "Application1",
                    "Application2",
                    "Application3",
                    "Application4"
                  ]
                }
              },
              {
                "data": {
                  "name": "cli.app",
                  "size": "10mb",
                  "typeList": [
                    "Application1",
                    "Application2"
                  ]
                }
              },
              {
                "data": {
                  "name": "mobile.app",
                  "size": "5mb",
                  "typeList": [
                    "Application1",
                    "Application2"
                  ]
                }
              }
            ]
          },
          {
            "data": {
              "name": "editor.app",
              "size": "25mb",
              "typeList": [
                "Application1",
                "Application2"
              ]
            }
          },
          {
            "data": {
              "name": "settings.app",
              "size": "50mb",
              "typeList": [
                "Application1",
                "Application2"
              ]
            }
          }
        ]
      },
      {
        "data": {
          "name": "Cloud",
          "size": "20mb",
          "typeList": [
            "Folder1",
            "Folder2"
          ]
        },
        "children": [
          {
            "data": {
              "name": "backup-1.zip",
              "size": "10mb",
              "typeList": [
                "Zip1",
                "Zip2"
              ]
            }
          },
          {
            "data": {
              "name": "backup-2.zip",
              "size": "10mb",
              "typeList": [
                "Zip1",
                "Zip2"
              ]
            }
          }
        ]
      },
      {
        "data": {
          "name": "Desktop",
          "size": "150kb",
          "typeList": [
            "Folder1",
            "Folder2"
          ]
        },
        "children": [
          {
            "data": {
              "name": "note-meeting.txt",
              "size": "50kb",
              "type": "Text"
            }
          },
          {
            "data": {
              "name": "note-todo.txt",
              "size": "100kb",
              "typeList": [
                "Text1",
                "Text2"
              ]
            }
          }
        ]
      },
      {
        "data": {
          "name": "Documents",
          "size": "75kb",
          "typeList": [
            "Folder1",
            "Folder2"
          ]
        },
        "children": [
          {
            "data": {
              "name": "Work",
              "size": "55kb",
              "typeList": [
                "Folder1",
                "Folder2"
              ]
            },
            "children": [
              {
                "data": {
                  "name": "Expenses.doc",
                  "size": "30kb",
                  "typeList": [
                    "Document1",
                    "Document2"
                  ]
                }
              },
              {
                "data": {
                  "name": "Resume.doc",
                  "size": "25kb",
                  "typeList": [
                    "Resume1",
                    "Resume2"
                  ]
                }
              }
            ]
          },
          {
            "data": {
              "name": "Home",
              "size": "20kb",
              "typeList": [
                "Folder1",
                "Folder2"
              ]
            },
            "children": [
              {
                "data": {
                  "name": "Invoices",
                  "size": "20kb",
                  "typeList": [
                    "Text1",
                    "Text2"
                  ]
                }
              }
            ]
          }
        ]
      }

    ]);
  }
}