Home > Software design >  How do I associate a MatPaginator with a MatTable taking a declarative data source exclusively in th
How do I associate a MatPaginator with a MatTable taking a declarative data source exclusively in th

Time:12-16

I am attempting to associate a MatPaginator with a MatTable who's dataSource input property is simply declared in the template, and not subscribed to from within the component itself. For example:

<table mat-table [dataSource]="allData$ | async" >

Normally, I would associate the paginator with the data source in the component like so:

export class AppComponent {
  @ViewChild(MatPaginator) set paginator(paginator: MatPaginator) {
    if (paginator) {
      this.dataSource.paginator = paginator;
    }
  }
  dataSource;
  displayedColumns = ['first_name', 'last_name', 'email', 'ip_address'];

  constructor(private appService: AppService) {}

  ngOnInit() {
    this.appService.getData().subscribe((data) => {
      this.dataSource = new MatTableDataSource(data);
      this.dataSource.paginator = this.paginator;
    });
  }
}

Unfortunately this will not work with the former method, as we are not actually creating a new datasource directly, and have no way to associate the data source and is automatically subscribed declarative via the async pipe.

Here is a StackBlitz showing the issue: https://stackblitz.com/edit/angular-ivy-ebddd6?file=src/app/app.component.html.

CodePudding user response:

I don't see why you couldn't make it MatTableDataSource though not subscribing to it in the component. We just set the data inside map and return a MatTableDataSource, that way you can attach the paginator to it. For the ViewChild matpaginator we need to set static: true so that we can access it immediately, so I would do:

@ViewChild(MatPaginator, { static: true }) paginator: MatPaginator;

allData$ = this.appService.getData().pipe(
  map((data) => {
    let dataSource = new MatTableDataSource(data);
    dataSource.paginator = this.paginator;
    return dataSource;
  })
);

STACKBLITZ

CodePudding user response:

A mat-paginator can be used "standalone" or when you has a MatTableDataSource object. The property "datasource" of a mat-table can be: an array, an observable or a MatTableDataSource.

To use standalone, you can use a template reference variable "pag" and a variable "length"

<mat-paginator #pag [length]="length"
  [pageSizeOptions]="[5, 10, 20]"
  showFirstLastButtons>
</mat-paginator>

Then use |slice in the way

<table mat-table [dataSource]="allData$|async 
           |slice:pag.pageIndex*pag.pageSize:
                  (pag.pageIndex 1)*pag.pageSize" 
    >

The last piece of the jigsaw is use pipe tap to get the length of the array

  allData$ = this.appService.getData().pipe(
    tap((res:any[])=>{
      this.length=res.length
    }))

To avoid the error "AfterChecked" you can asign to the variable allData$ in a ngOnInit enclosed in a setTimeOut

   ngOnInit(){
    setTimeout(()=>{
      this.allData$ = this.appService.getData().pipe(
        tap((res:any[])=>{
          this.length=res.length
        }))
      })
    }

See the stackblitz

  • Related