0

I am trying to implement a Material Design table in Angular 4 that receives data from a service call. No matter what I try, I cannot get the table to populate even though my service is returning data properly. I do not receive any errors in the console. It appears that the connect() function in the datasource never gets called (as per the traces I placed everywhere) hence my datasource is always empty. I am running material in version 2.0.0-beta.12. I did read that there was a bug in previous versions of material and that's why I included the call to "this._changeDetector.detectChanges();" in my component ngOnInit but it did not help. I also read the question How to use md-table with services in Angular 4 and implemented the recommendations.

After looking through the official doc and lots of threads, here is the code I came up with:

role.component.ts

import { Component, OnInit, ViewChild, ChangeDetectorRef } from '@angular/core';
import { MatPaginator } from '@angular/material';    
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
import { DataSource } from '@angular/cdk/table';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/startWith';
import 'rxjs/add/observable/merge';
import 'rxjs/add/operator/map';
import { RoleService } from '../shared/services/role/role.service';
import { Role } from '../shared/classes/role';

@Component({
  selector: 'app-roles',
  templateUrl: './role.component.html',
  styleUrls: ['./role.component.css']
})
export class RoleComponent implements OnInit {
  public displayedColumns = ['role_name', 'tech_stack'];
  public roleDatabase = new RoleDatabase(this._roleService);
  public roleDatasource: RoleDatasource | null;

  @ViewChild(MatPaginator) paginator: MatPaginator;

  constructor(private _roleService: RoleService, private _changeDetector: ChangeDetectorRef) { }

  ngOnInit() {
    this.roleDatasource = new RoleDatasource(this.roleDatabase, this.paginator);
    this._changeDetector.detectChanges();
  }

}

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

  constructor(private _roleService: RoleService) {
      this.getAllRoles();
  }

  getAllRoles() {
      this._roleService.getRoles().subscribe( response => {
          this.dataChange.next(response);
      });
  }
}

export class RoleDatasource extends DataSource<any> {

  constructor(private _roleDatabase: RoleDatabase, private _paginator: MatPaginator) {
    super();
  }

  connect(): Observable<Role[]> {

    const displayDataChanges = [
        this._roleDatabase.dataChange,
        this._paginator.page
    ];

    return Observable.merge(...displayDataChanges).map(() => {
        const data = this._roleDatabase.data.slice();
        const startIndex = this._paginator.pageIndex * this._paginator.pageSize;
        return data.splice(startIndex, this._paginator.pageSize);
    })
  }

  disconnect() { }
}

role.service.ts

import { Injectable } from '@angular/core';
import { Http, Response, URLSearchParams, Headers } from '@angular/http';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/map';
import 'rxjs/add/observable/of';
import 'rxjs/add/operator/catch';

import { Role } from '../../classes/role';

@Injectable()
export class RoleService {

  private rolesHeaderUrl = '/local/roles';

  constructor(private http: Http) { }

  getRoles(): Observable<Role[]> {
    return this.http.get(this.rolesHeaderUrl)
      .map(response => response.json() as Role[])
      .catch(this.getError);
  }

  private getError(error: Response): Observable<any>{
    return Observable.throw(error.json() || 'Server Issue');
  }

}

and finally the role.component.html:

<mat-card class="title-card">
  <mat-card-title class="page-title">Roles</mat-card-title>
</mat-card>

<mat-card>

  <mat-table #roletable [dataSource]="roleDataSource">

    <ng-container matColumnDef="role_name">
      <mat-header-cell *matHeaderCellDef> Role Name </mat-header-cell>
      <mat-cell *matCellDef="let role"> {{role.role_name}} </mat-cell>
    </ng-container>

    <ng-container matColumnDef="tech_stack">
      <mat-header-cell *matHeaderCellDef> Technical Stack </mat-header-cell>
      <mat-cell *matCellDef="let role"> {{role.tech_stack}} </mat-cell>
    </ng-container>

    <mat-header-row *matHeaderRowDef="displayedColumns"></mat-header-row>
    <mat-row *matRowDef="let row; columns: displayedColumns;"></mat-row>

  </mat-table>

  <mat-paginator #paginator
    [length]="roleDatabase.data.length"
    [pageIndex]="0"
    [pageSize]="25"
    [pageSizeOptions]="[5, 10, 25, 100]">
  </mat-paginator>

</mat-card>

I must be missing something obvious (hopefully!) at this point... if someone could just find it!!

Thanks for the help!

dda

dda
  • 240
  • 1
  • 3
  • this bit here: `private rolesHeaderUrl = '/local/roles';` ... is it actually going to that url when it is requesting the data? double check in your networks tab of the dev console. I had a code that had that style of api endpoint and it didn't pull the data properly. after much checking I found that it was doing something strange and trying to pull data from /my/component/path/api/endpoint instead of /api/endpoint. Might be something similar. – Iskandar Reza Oct 20 '17 at 23:57
  • I actually have a proxy.config.json file rerouting the request to localhost:3000 since my service is local. I did test the service (using both postman and through the app) and the data is returned correctly. What it looks like is even though the service returns data, the connect() function is not called. – dda Oct 21 '17 at 00:04

1 Answers1

0

I resolved my issue and it was indeed an obvious mistake... my datasource variable was named "roleDatasource" but I misspelled it in the html "roleDataSource". Oh well...

dda
  • 240
  • 1
  • 3