budkin
budkin

Reputation: 357

Angular Material Table won't populate with external data on initial load

I'm working on an Angular 6 project where I'm loading data in from an AWS DynamoDB table via Observable into a Material Table component. I used the angular-cli to generate the initial structure of the table, and then added my own service to fetch external data, since the example used hard coded data in an array.

Everything seems to be working (I can see the correct data returned via console.log) except for the fact that on my initial load, the data that I'm returning from the observable isn't getting populated into the table. In fact if I inspect the "this.data" variable it seems like it's immediately getting set back to "undefined." If I select and change the number of results per page on the pagination component, the data returned by the observable is inserted.

connect(): Observable<DynamoTableItem[]> {
    // Combine everything that affects the rendered data into one update
    // stream for the data-table to consume.
    const dataMutations = [
        observableOf(this.data),
        this.paginator.page,
        this.sort.sortChange
    ];

    // Set the paginators length
    this.paginator.length = this.data.length;

    return merge(...dataMutations).pipe(map(() => {
        return this.getPagedData(this.getSortedData([...this.data]));
    }));
}

I've put the project on stackblitz if someone wouldn't mind taking a look.

To reproduce the issue:

  1. Go to: https://stackblitz.com/edit/mat-table-dynamo
  2. Notice there is no data in the table.
  3. Select the "Items per page" pulldown and change to a different value.
  4. The table populates with the data returned from the Observable.

Thanks for your help!

Upvotes: 1

Views: 1302

Answers (1)

CozyAzure
CozyAzure

Reputation: 8478

The rule of thumb of writing any service in angular is that if you have a .subscribe() inside your service, you are probably(99%) do it wrong. Always return an Observable, and let your component do the .subscribe().

The reason why your code doesn't work is because you subscribe your data first inside your service, and then re-wrap it using Observable.of(). That won't work, because your http call is asynchronous. By the time your subscription inside your constructor has received emission, your connect has long established and this.data is first undefined before it can be assigned any values.

To solve your problem, simply change the of(this.data) to its original Observable source, and everything is working:

connect(): Observable<DynamoTableItem[]> {
    // Combine everything that affects the rendered data into one update
    // stream for the data-table to consume.
    const dataMutations = [
        this.dynamoService.getData(this.params),
        this.paginator.page,
        this.sort.sortChange
    ];

    // Set the paginators length 
    this.paginator.length = this.data.length;

    return merge(...dataMutations).pipe(map((received) => { 
        return this.getPagedData(this.getSortedData([...received]));
    }));
}

Here is the working working StackBlitz

Upvotes: 4

Related Questions