Mat Paginator of Mat Tabledoesn't work with api data

1.8k views Asked by At

I've seen similar questions, but nothing seems to work for me. I have a mat table where I display data from an api. But I don't know how to iterate through the 'dataSource'. Below is my code, and how I get the data when I check console log. ts file

import {AfterViewInit, Component, OnInit, ViewChild} from '@angular/core';
import {Pokemon, PokemonData} from '../../models/pokemon';
import {PokemonService} from '../../models/services/pokemon.service';
import {ActivatedRoute} from '@angular/router';
import {MatTableDataSource} from '@angular/material/table';
import {MatPaginator} from '@angular/material/paginator';

@Component({
  selector: 'app-pokemon',
  templateUrl: './pokemon.component.html',
  styleUrls: ['./pokemon.component.css']
})
export class PokemonComponent implements OnInit {
  public pokemon: Pokemon[];
  public name: string;
  public url: string;
  public type: string;
  expandedElement: PokemonData | null;
  dataSource = new MatTableDataSource();
  displayPokemonColumns: string[] = ['name', 'url', 'pokemonDetails'];
  @ViewChild(MatPaginator, {read: true}) paginator: MatPaginator;
  //
  // // tslint:disable-next-line:typedef
  // tslint:disable-next-line:typedef
  // ngAfterViewInit() {
  //   // this.dataSource.paginator = this.paginator;
  // //
  // }

  constructor(private pokemonService: PokemonService, private route: ActivatedRoute) {
  }

  // function (which is called below)issues call to API. subscribes shows to the results that are returned. adds results to shows array
  onLoadPokemonList(): void {
  this.pokemonService.getPokemonList().subscribe(
    res => {
  // this.pokemon = JSON.parse(JSON.stringify(res));
  this.pokemon = res;
  this.dataSource.data = this.pokemon;
  setTimeout(() => {
        this.dataSource.paginator = this.paginator;

      });
  console.log(this.dataSource);
    });

  }


  ngOnInit(): void {
  this.onLoadPokemonList();

  }


}

html file

<div class="table"  *ngIf="pokemon">
  <h1 matColumnDef="title">POKEDEX</h1>
  <table mat-table #table [dataSource]="dataSource" class="mat-elevation-z8" multiTemplateDataRows>
<!--    <table mat-table [dataSource]="pokemon.results" class="mat-elevation-z8" multiTemplateDataRows>-->
    <ng-container matColumnDef="name">
      <th mat-header-cell *matHeaderCellDef> Name </th>
<!--  <td mat-cell *matCellDef="let res"> {{res?.name}} </td>-->
      <td mat-cell *matCellDef="let res"> {{res.name}} </td>
    </ng-container>
    <ng-container matColumnDef="url">
      <th mat-header-cell *matHeaderCellDef> URL </th>
      <td mat-cell *matCellDef="let res"> {{res.url}} </td>
    </ng-container>
    <ng-container matColumnDef="pokemonDetails">
      <th mat-header-cell *matHeaderCellDef> Pokemon Details </th>
      <td mat-cell *matCellDef="let res">
        <button mat-raised-button color="primary" routerLink="/pokemonData/{{res.name}}">Pokemon Details</button>
      </td>
    </ng-container>
    <tr mat-header-row *matHeaderRowDef="displayPokemonColumns; sticky: true"></tr>
    <tr mat-row *matRowDef="let row; columns: displayPokemonColumns;"
        class="example-element-row"
        [class.example-expanded-row]="expandedElement === row"
        (click)="expandedElement = expandedElement === row ? null : row"></tr>
    </table>
  <mat-paginator #paginator
                 [pageSize]="10"
                 [pageSizeOptions]="[5, 10, 20]">
  </mat-paginator>
</div>

this is the data I get back:

MatTableDataSource {_renderData: BehaviorSubject, _filter: BehaviorSubject, _internalPageChanges: Subject, _renderChangesSubscription: Subscriber, sortingDataAccessor: ƒ, …}filterPredicate: (data, filter) => {…}filteredData: {count: 1050, next: "https://pokeapi.co/api/v2/pokemon?offset=150&limit=150", previous: null, results: Array(150)}sortData: (data, sort) => {…}sortingDataAccessor: (data, sortHeaderId) => {…}_data: BehaviorSubject {_isScalar: false, observers: Array(1), closed: false, isStopped: false, hasError: false, …}_filter: BehaviorSubject {_isScalar: false, observers: Array(1), closed: false, isStopped: false, hasError: false, …}_internalPageChanges: Subject {_isScalar: false, observers: Array(0), closed: false, isStopped: false, hasError: false, …}_paginator: undefined_renderChangesSubscription: Subscriber {closed: false, _parentOrParents: null, _subscriptions: Array(1), syncErrorValue: null, syncErrorThrown: false, …}_renderData: BehaviorSubject {_isScalar: false, observers: Array(1), closed: false, isStopped: false, hasError: false, …}data: (...)filter: (...)paginator: (...)sort: (...)__proto__: DataSource

The data I need is under filteredData.

When I do

<table mat-table [dataSource]="pokemon.results" class="mat-elevation-z8" multiTemplateDataRows>

instead of dataSource, I get the correct table, but the paginator (obviously) doesn't work.

2

There are 2 answers

1
Eliseo On

The problem is tha,t when you try to assing the paginator, the paginator is not in the DOM (you has the mat-paginator under a div that has a *ngIf). You need give a "change" to Angular to get it, so you need enclosed in a setTimeout inside subscribe function

this.pokemonService.getPokemonList().subscribe(res => {
  //why you use JSON.stringify? in Angular by defect a http.get return a json
  this.pokemon = JSON.parse(JSON.stringify(res));
  // this.pokemon = res;
  this.dataSource.data = this.pokemon;

   //here you indicate the paginator
   setTimeout(()=>{
    this.dataSource.paginator = this.paginator;
   })
});

Brief explain: your variable "pokemon" is null or undefined until you subscribe and get the data. (rememeber that you has *ngIf="pokemon" in html). when you get the data, the variable has value, so, "when Angular finished the instructions", refresh the app to show the table. this is the reason because you need a setTimeout

0
Heidi E On

what worked in the end, was removing the *ngIf condition on the template. I found this solution on stack overflow. Paginator now works fine.