How to use md-table with services in Angular 4

10,621

Here's an example of retrieving data through HTTP: https://plnkr.co/edit/mjQbufh7cUynD6qhF5Ap?p=preview


You are missing a piece where GroupDatabase does not put any data values on the dataChange stream. It is a BehaviorSubject that starts with an empty array but you do not put any more data on it. This is why the table is only receiving an empty array.

First, know that ngOnInit will not be called by Angular since GroupDatabase is not a directive and is not part of the change detection cycle.

Instead, move this.getAllGroups() to the constructor of GroupDatabase. and then subscribe to its result:

export class GroupDatabase {
  public dataChange: BehaviorSubject<Group[]> = new BehaviorSubject<Group[]>([]);
  get data(): Group[] { return this.dataChange.value }

  constructor(groupService: GroupService) {
    groupService.getAllGroups().subscribe(data => this.dataChange.next(data));
  }
}

Alternatively, get rid of GroupDatabase altogether and have your CustomDataSource directly call your GroupService.

export class CustomDataSource extends DataSource<Group> {

  constructor(
      private _groupService: GroupService, 
      private _paginator: MdPaginator) { }

  connect(): Observable<Group[]> {
    const displayDataChanges = [
      this._groupService.getAllGroups(),
      this._paginator.page
    ];

    return Observable.merge(...displayDataChanges).map((data, page) => {
      const clonedData = data.slice();

      const startIndex = this._paginator.pageIndex * this._paginator.pageSize;
      return data.splice(startIndex, this._paginator.pageSize);
    })
  }

  disconnect() {}
}
Share:
10,621
Helongh
Author by

Helongh

Updated on June 07, 2022

Comments

  • Helongh
    Helongh about 2 years

    I am quite new in the angular world and i'm trying to use the new md-table component in Angular Material 2 with Angular 4.

    I've made a service from an API which retrieves simple arrays of content. Now I'm trying to use this service as a data source for the md-table but I can't find a way to get the data from the service (it always return me an empty array).

    Please note that before using md-table, I was using already using the service and it worked normally.

    Here is the code for the component :

    import { Component, OnInit, ViewChild } from '@angular/core';
    import {DataSource} from '@angular/cdk';
    import {MdPaginator} from '@angular/material';
    import {BehaviorSubject} from 'rxjs/BehaviorSubject';
    import {Observable} from 'rxjs/Observable';
    import 'rxjs/add/operator/startWith';
    import 'rxjs/add/observable/merge';
    import 'rxjs/add/operator/map';
    
    import { GroupService } from '../shared/group.service';
    import { Group } from '../shared/group';
    
    @Component({
      selector: 'app-group-list',
      templateUrl: './group-list.component.html',
      styleUrls: ['./group-list.component.css'],
      providers: [GroupService]
    })
    export class GroupListComponent implements OnInit{
    
      public DisplayedColumns = ['name', 'email', 'directMembersCount'];
      public groupDatabase = new GroupDatabase();
      public dataSource : CustomDataSource | any;
    
      @ViewChild(MdPaginator) paginator : MdPaginator;
    
      constructor() {}
    
      ngOnInit() {
        this.dataSource = new CustomDataSource(this.groupDatabase, this.paginator);
        console.log(this.dataSource);
      }
    
    }
    
    export class GroupDatabase implements OnInit {
    
      public dataChange: BehaviorSubject<Group[]> = new BehaviorSubject<Group[]>([]);
      get data(): Group[] { return this.dataChange.value }
    
      private _groupService : GroupService
    
      private getAllGroups(){
        return this._groupService
          .getAllGroups();
      }
    
      constructor (){}
    
      ngOnInit() {
          this.getAllGroups();
          console.log(this.getAllGroups());
      }
    }
    
    export class CustomDataSource extends DataSource<any> {
    
      constructor(private _groupDatabase = new GroupDatabase(), private _paginator: MdPaginator){
        super();
      }
    
      connect(): Observable<Group[]> {
        const displayDataChanges = [
          this._groupDatabase.dataChange,
          this._paginator.page
        ];
        return Observable.merge(...displayDataChanges).map(() => {
          const data = this._groupDatabase.data.slice();
          console.log(data);
    
          const startIndex = this._paginator.pageIndex * this._paginator.pageSize;
          return data.splice(startIndex, this._paginator.pageSize);
        })
      }
    
      disconnect() {}
    }
    

    Here is the code for the HTML :

    <md-table #table [dataSource]="dataSource">
    
      <ng-container *cdkColumnDef="name">
        <md-header-cell *cdkCellDef>Nom</md-header-cell>
        <md-cell *cdkCellDef="let row"> {{row.name}} </md-cell>
      </ng-container>
    
      <ng-container *cdkColumnDef="email">
        <md-header-cell *cdkCellDef>Email</md-header-cell>
        <md-cell *cdkCellDef="let row"> {{row.email}} </md-cell>
      </ng-container>
    
      <ng-container *cdkColumnDef="directMembersCount">
        <md-header-cell *cdkCellDef>Nombre de membres</md-header-cell>
        <md-cell *cdkCellDef="let row"> {{row.directMembersCount}} </md-cell>
      </ng-container>
    
      <md-header-row *cdkHeaderRowDef="displayedColumns"></md-header-row>
      <md-row *cdkRowDef="let row; columns: DisplayedColumns;"></md-row>
    
    </md-table>
    
    <md-paginator #paginator
                  [length]="groupDatabase.data.length"
                  [pageIndex]="0"
                  [pageSize]="25"
                  [pageSizeOptions]="[5, 10, 25, 100]">
    </md-paginator>
    

    And the concerned service :

    private groupApiUrl: string;
      private groupsApiUrl: string;
      private headers: Headers;
      constructor(public http:Http, private config: Config) {
        this.groupApiUrl = config.serverWithApi + "group";
        this.groupsApiUrl = config.serverWithApi + "groups";
    
        this.headers = new Headers();
        this.headers.append('Content-Type', 'application/json');
        this.headers.append('Accept', 'application/json');
      }
    
      public getAllGroups = (): Observable<Group[]> => {
        return this.http.get(this.groupsApiUrl)
          .map((response: Response) => <Group[]>response.json())
          .catch(this.handleError)
      }
    

    I'm not sure how I should call the service using the datasource, that's why I did it as I was doing before; using the ngOnInit method.

    Thanks for you help.

  • Helongh
    Helongh almost 7 years
    Thanks for your answer. Indeed this solved my issue, but the doc really i confusing !
  • Tim
    Tim almost 7 years
    Using the first method with the GroupDatabase, how/when do you pass in the service? See my question at stackoverflow.com/questions/46456808/… for more specifics on the question and to see what I tried.
  • k.vincent
    k.vincent over 6 years
    @Andrew: I'am following the your example on Plunker which is working fine with GitHub API but when I change the URL and add mine http://localhost:4000/users, I get following error: ERROR TypeError: t.json(...).map is not a function. I assume it's affecting this part of code: return result.json().map(issue => {return {....}});. Any idea what should be fixed there? Thanks.
  • carkod
    carkod almost 6 years
    @k.vincent that basically means your t is basically not formatted properly. Have you tried logging t? What type is it?