diprokon / ng-table-virtual-scroll

Virtual Scroll for Angular Material Table
https://diprokon.github.io/ng-table-virtual-scroll
MIT License
133 stars 41 forks source link

mat-table can not be used inside ngTemplateOutlet #98

Open Grochni opened 2 years ago

Grochni commented 2 years ago

I have a fairly complex table to which I added virtual scroll using this excellent project.

However, as I still want to be able to use my table component without virtual scroll and I do not want to duplicate the whole table component, I tried putting it into an ng-template like this:

<cdk-virtual-scroll-viewport *ngIf="virtualScroll; else tableTemplate" [tvsItemSize]="rowHeight">
    <ng-container [ngTemplateOutlet]="tableTemplate"></ng-container>
</cdk-virtual-scroll-viewport>
<ng-template #tableTemplate>
   <mat-table>...</mat-table>
</ng-template>

There is an open issue in angular material explaining this behavior. It is stated in the comments that there's a workaround using ContentChildren instead of ContentChild. I tried around and was not able to get it to work.

What I ended up doing as a workaround is to subclass your TableItemSizeDirective and add an input to provide the table manually (because I can easily pick it up as a ViewChild in my component). This comes with some hacks like manually calling the ngAfterContentInit again when the input is set and overriding the ngAfterContentInit to null-check the table. Also, I had to copy your entire Directive decorator, as this is not inherited. The whole setup will make it hard to update my dependency to your library in the future and I therefor consider it a undesirable solution.

I can't say if there's an easy way to make Angular automatically pick up content children from inside an ngTemplateOutlet, but an other option would be to accept a reference to a MatTable as an input to your TableItemSizeDirective. This would be a small change that allows my usecase to work.

Kind regards and thanks for your work on this great project.

For others struggling with this, my workaround looks like this:

import { VIRTUAL_SCROLL_STRATEGY } from '@angular/cdk/scrolling';
import { AfterContentInit, Directive, Input, forwardRef } from '@angular/core';
import { MatTable } from '@angular/material/table';

import { TableItemSizeDirective, _tableVirtualScrollDirectiveStrategyFactory } from 'ng-table-virtual-scroll';

@Directive({
  selector: 'cdk-virtual-scroll-viewport[appItemSize]',
  providers: [{
    provide: VIRTUAL_SCROLL_STRATEGY,
    useFactory: _tableVirtualScrollDirectiveStrategyFactory,
    deps: [forwardRef(() => AppTableItemSizeDirective)]
  }]
})
export class AppTableItemSizeDirective extends TableItemSizeDirective implements AfterContentInit {

  @Input()
  set appItemSize(itemSize: string | number) {
    this.rowHeight = itemSize;
  }

  @Input()
  set tableComponent(table: MatTable<any>) {
    this.table = table;
    this.ngAfterContentInit();
  }

  override ngAfterContentInit() {
    if (!!this.table) {
      super.ngAfterContentInit();
    }
  }
}
wS2Zth commented 1 year ago

You saved me thx! Thanks.