2

Im having issues with the mat-table not loading any new data after it has been initially created. I have changed from a regular html table using a ngFor to generate the rows which was working fine with the same data source. This is the mat-table html I am using:

<table mat-table [dataSource]="stocks" multiTemplateDataRows class="mat-elevation-z8">

    <ng-container matColumnDef="symbol">
      <th mat-header-cell *matHeaderCellDef> Symbol </th>
      <td mat-cell *matCellDef="let stock"> {{stock.symbol}} </td>
    </ng-container>

    <ng-container matColumnDef="price">
      <th mat-header-cell *matHeaderCellDef> Price </th>
      <td mat-cell *matCellDef="let stock"> {{stock.price}} </td>
    </ng-container>

    <ng-container matColumnDef="change">
      <th mat-header-cell *matHeaderCellDef> Change </th>
      <td mat-cell *matCellDef="let stock"> {{stock.change}} </td>
    </ng-container>

    <ng-container matColumnDef="changePercent">
      <th mat-header-cell *matHeaderCellDef> % Change </th>
      <td mat-cell *matCellDef="let stock"> {{stock.changePercent}} </td>
    </ng-container>

    <tr mat-header-row *matHeaderRowDef="columns"></tr>
    <tr mat-row *matRowDef="let row; columns:columns;" class="example-detail-row"></tr>
  </table>

This is the regular HTML table that worked:

<table>
        <tr>
          <th>Symbol</th>
          <th>Price</th>
          <th>Change</th>
          <th>% Change</th>
        </tr>
        <tr *ngFor="let stock of stocks | async" class="stock">
          <td>{{ stock.symbol }}</td>
          <td>{{ stock.price }} {{ stock.currency }}</td>
          <td>{{ stock.change }}</td>
          <td>{{ stock.changePercent }}</td>
        </tr>
    </table>

The variable stocks is of type Observable. Any suggestions as to why the table no longer updates when the value of "stock" changes?

Edit: Stocks is set using this:

constructor(private storageService: StorageService){
 this.stocks = this.storageService.getAllStocks(); 
}

The getAllStocks() function makes several requests to an api and then returns them as a Observable

Edit2:

getAllStocks(): Observable<any[]> {
return of(this.allStocks);

}

where allStocks is a list of stocks within a service

oorkyy
  • 57
  • 1
  • 1
  • 7
  • Should work but show how stocks is setup – Aluan Haddad May 07 '20 at 23:04
  • Stocks is set using this: constructor(private storageService: StorageService){ this.stocks = this.storageService.getAllStocks(); } The getAllStocks() function makes several requests to an api and then returns them as a Observable – oorkyy May 07 '20 at 23:18
  • Anyway, it seems to work. Here's an example https://stackblitz.com/edit/angular-1radrl – Aluan Haddad May 07 '20 at 23:32
  • So I tried you solution with a concatand time which did update the table. However It still doesn't update for me when the value of stocks is changed from the getAllStocks() function. I have added a setInterval to print every x seconds to check the value of stocks and it is updating. So that isnt the issue. – oorkyy May 08 '20 at 00:25
  • The point is that it has something to do with `getAllStocks`. `setInterval` sounds suspect. Please show the implementation of `getAllStocks` – Aluan Haddad May 08 '20 at 00:28
  • I was using the setinterval to print the value of stocks to allow me to see if it was updating or not. – oorkyy May 08 '20 at 00:33
  • OK, so what you are saying is that you modify the _contents_ of the `allStocks` array as new stocks come in? There is no way that `of(this.allStocks)` is going to yield multiple values over time. – Aluan Haddad May 08 '20 at 00:36
  • Yes and it looks to be successfully getting modified – oorkyy May 08 '20 at 00:39
  • 1
    Yes, but the observable created by `of(x)` only emits `x` once. See https://rxjs.dev/api/index/function/of – Aluan Haddad May 08 '20 at 00:40
  • So how come the value of stocks keeps getting updated? And why does this work when using ngFor but not when using a mat-table – oorkyy May 08 '20 at 00:47
  • 1
    Because `of(allStocks)` emits whatever `allStocks` refers to exactly once. In this case, `allStocks` refers to an array that is subsequently mutated. It's just like having `var xs = [1, 2]; var ys = xs; ys.push(3); console.log(xs) // prints [1, 3, 3]`. `ngFor` is very liberal. – Aluan Haddad May 08 '20 at 00:52
  • Ah I see. So how do I do about creating a new observable when the value of stocks changes? Cheers for the help btw – oorkyy May 08 '20 at 00:54
  • 1
    No problem, glad to be of service. You actually don't want to create a new observable when it changes you want to have a single observable, just as you do now, but one that emits whenever the underlying array changes. How you would do that will depend on the conditions that cause `allStocks` to change in the service. Ex: `allStocks$ = new BehaviorSubject(this.allStocks); getAllStocks() {return this.allStocks$;} addStock(s) {this.allStocks.push(s); this.allStocks$.next(this.allStocks);}` – Aluan Haddad May 08 '20 at 01:00
  • Subject is just one relatively crude way of doing it however. There are lots of options that may be appropriate depending on the actual scenario. – Aluan Haddad May 08 '20 at 01:01
  • 1
    Works a charm. Cheers for the explanations – oorkyy May 08 '20 at 01:10

0 Answers0