Search code examples
node.jsangulartypescriptangular-materialangular-material-table

Display Data in Angular Material Table


I'm struggling with the Angular mat-table library. I got an app with a Node.js backend and an Angular frontend. The node app provides data from a MySQL database in JSON.

Now I want to display this data in a mat-table. I have logged the data in the console, which allows me to see that the data is actually retrieved but just not displayed.

Data in Console

However, the HTML table is empty:

empty html

This is my Angular component:

component.html

<table mat-table [dataSource]="dataSource" class="mat-elevation-z8 demo-table">
  <ng-container matColumnDef="id">
    <th mat-header-cell *matHeaderCellDef>ID</th>
    <td mat-cell *matCellDef="let element">{{element.id}}</td>
  </ng-container>
  <ng-container matColumnDef="name">
    <th mat-header-cell *matHeaderCellDef>Name</th>
    <td mat-cell *matCellDef="let element">{{element.name}}</td>
  </ng-container>
  <ng-container matColumnDef="pop">
    <th mat-header-cell *matHeaderCellDef>Population</th>
    <td mat-cell *matCellDef="let element">{{element.population}}</td>
  </ng-container>


  <tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
  <tr
      mat-row
      (click)="clickedRows.add(row)"
      [class.demo-row-is-clicked]="clickedRows.has(row)"
      *matRowDef="let row; columns: displayedColumns;"
  ></tr>
</table>

component.ts

import {Component, OnInit,ViewChild} from '@angular/core';
import { Player } from '../player';
import { PlayerService } from '../player.service';
import { MatTableDataSource } from '@angular/material/table';
import {MatPaginator} from '@angular/material/paginator';
import {MatSort, SortDirection} from '@angular/material/sort';

/**
 * @title Binding event handlers and properties to the table rows.
 */
@Component({
  selector: 'app-players',
  styleUrls: ['players.component.css'],
  templateUrl: 'players.component.html',
})
export class PlayersComponent implements OnInit {
  displayedColumns: string[] = ['id', 'name', 'pop'];
  dataSource = new MatTableDataSource<Player>();

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

  constructor(private playerService:PlayerService) { }

  ngOnInit(): void {
    this.getPlayers();
  }

  getPlayers() {
    this.playerService.getPlayers().subscribe(players => {
      console.log(players);
      this.dataSource.data = players;
      this.dataSource.paginator = this.paginator;
      this.dataSource.sort = this.sort;
    });
  }
  clickedRows = new Set<Player>();
}

player.service.ts

import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable, of } from 'rxjs';
import { Player } from './player';

@Injectable({
  providedIn: 'root'
})
export class PlayerService {

  constructor(private http: HttpClient) { }
  rootURL = '/api';
  getPlayers(): Observable<Player[]> {
    return this.http.get<Player[]>(this.rootURL+ '/players');
  }
}

Any ideas on this?

EDIT: Could it have something to do with how the array comes back from the API? In Node.js it is retrieved with sequelize and maybe it is the response?

// Get all Players
exports.findAll = (req, res) => {
    Player.findAll().then((players) => {
    // Send all players as response
    res.status(200).json({
      status: true,
      data: players,
    });
  });
};

Solution

  • Issue

    The data returned based on the screenshot and Node.js API is not Player array, but it is an object with status and data properties.

    {
      "status": 200,
      "data": [...]
    }
    

    This line expected that HTTP GET to receive Player array which is conflict with your data.

    this.http.get<Player[]>(this.rootURL+ '/players');
    

    Hence, it returns Observable with an empty array and your <mat-table> will not show the data.


    Solution

    Transform the data to Player array with map rxjs operator.

    import { map } from 'rxjs';
    
    getPlayers(): Observable<Player[]> {
      return this.http
        .get(this.rootURL+ '/players')
        .pipe(map((response: any) => response.data as Player[]));
    }
    

    Sample Solution on StackBlitz