60

I have data streaming from backend and i see it printing in console now i am trying to push event to dataSource its throwing error dataSource is not defined. Can someone help how to dynamically add data to materialize table ?

stream.component.html

<mat-table #table [dataSource]="dataSource"></mat-table>

stream.component.ts

import {
    Component,
    OnInit
} from '@angular/core';
import {
    StreamService
} from '../stream.service';
import {
    MatTableDataSource
} from '@angular/material';
import * as io from 'socket.io-client';

@Component({
    selector: 'app-stream',
    templateUrl: './stream.component.html',
    styleUrls: ['./stream.component.css']
})
export class StreamComponent implements OnInit {
    displayedColumns = ['ticketNum', "assetID", "severity", "riskIndex", "riskValue", "ticketOpened", "lastModifiedDate", "eventType"];
    dataSource: MatTableDataSource < Element[] > ;
    socket = io();

    constructor(private streamService: StreamService) {};

    ngOnInit() {
        this.streamService.getAllStream().subscribe(stream => {
            this.dataSource = new MatTableDataSource(stream);
        });
        this.socket.on('newMessage', function(event) {
            console.log('Datasource', event);
            this.dataSource.MatTableDataSource.filteredData.push(event);
        });
    }
}


export interface Element {
    ticketNum: number;
    ticketOpened: number;
    eventType: string;
    riskIndex: string;
    riskValue: number;
    severity: string;
    lastModifiedDate: number;
    assetID: string;
}
hussain
  • 6,587
  • 18
  • 79
  • 152

8 Answers8

54

I have found a solution for this problem, basically if you do:

this.dataSource.data.push(newElement); //Doesn't work

But if you replace the complete array then it works fine. So your final code must be :

this.socket.on('newMessage', function(event) {
    const data = this.dataSource.data;
    data.push(event);
    this.dataSource.data = data;
});

You can see the issue here -> https://github.com/angular/material2/issues/8381

Bnrdo
  • 5,325
  • 3
  • 35
  • 63
Jose Luis
  • 641
  • 5
  • 6
  • 2
    S/O Review: Please try to add some context to your link. Simply posting links does not add value to your answer. Try to look through the issue and summarize the relevant pieces in your answer. Always try to be as comprehensive as necessary. It may also benefit the asker if you explain your code. Code blocks like this, unfortunately, encourage copy/pasta behavior from future viewers. We want S/O users to gain knowledge here; so if you've got it, share it! – Jonny Asmar Jan 17 '18 at 00:52
  • 5
    cleaner and more elegant way: ``this.dataSource.data = { ...this.dataSource.data, event }`` – Pizzicato Apr 02 '19 at 17:33
  • use `this.table.renderRows()` – Eliseo Jul 09 '19 at 08:43
  • 2
    this.dataSource.data = [ ...this.dataSource.data, event ] – Sandre Mar 05 '20 at 08:44
  • that's not a bug, dataSource has to be treathed as immutable. – netalex Sep 21 '20 at 08:46
14

Here is a very simple and easy solution:

displayedColumns = ['ticketNum', 'assetID', 'severity', 'riskIndex', 'riskValue', 'ticketOpened', 'lastModifiedDate', 'eventType'];
dataSource: any[] = [];

constructor() { 

}

ngOnInit() {

}

onAdd() {  //If you want to add a new row in the dataSource
   let model = { 'ticketNum': 1, 'assetID': 2, 'severity': 3, 'riskIndex': 4, 'riskValue': 5, 'ticketOpened': true, 'lastModifiedDate': "2018-12-10", 'eventType': 'Add' };  //get the model from the form
   this.dataSource.push(model);  //add the new model object to the dataSource
   this.dataSource = [...this.dataSource];  //refresh the dataSource
}

Hope this will help :)

SKB
  • 179
  • 2
  • 8
14

The following solution worked for me:

this.socket.on('newMessage', function(event) {
    this.dataSource.data.push(event);
    this.dataSource.data = this.dataSource.data.slice();
});

Another solution would be calling the _updateChangeSubscription() method for the MatTableDataSource object:

this.socket.on('newMessage', function(event) {
    this.dataSource.data.push(event);
    this.dataSource._updateChangeSubscription();
});

This method:

/** Subscribe to changes that should trigger an update to the table's rendered rows. When the changes occur, process the current state of the filter, sort, and pagination along with the provided base data and send it to the table for rendering. */

andreivictor
  • 7,628
  • 3
  • 48
  • 75
  • 3
    `this.dataSource.data = this.dataSource.data.slice();` is unnecessary and can actually break the connection the dataSource has with the underlying layer. You can trigger it with `this.dataSource.data = this.dataSource.data;`, it doesn't have to be (and shouldn't be) via a slice / i.e. new array. – seaders Jul 12 '19 at 11:12
  • 3
    `this.dataSource._updateChangeSubscription()` works for me – Alberto Siurob May 06 '20 at 19:50
8

from the docs

Since the table optimizes for performance, it will not automatically check for changes to the data array. Instead, when objects are added, removed, or moved on the data array, you can trigger an update to the table's rendered rows by calling its renderRows() method.

So, you can use ViewChild, and refreshRow()

@ViewChild('table', { static: true }) table;
  add() {
    this.dataSource.data.push(ELEMENT_DATA[this.index++]);
    this.table.renderRows();
  }

I put together an example in stackblitz

crh225
  • 821
  • 18
  • 34
Eliseo
  • 50,109
  • 4
  • 29
  • 67
5

You could also use the ES6 spread operator or concat if you are not using ES6+ to assign the dataSource data to a new array.

In ES6+

this.socket.on('newMessage', function(event) {
    this.dataSource.data = [...this.dataSource.data, event];
});

ECMAscript version 3+

this.socket.on('newMessage', function(event) {
    this.dataSource.data = this.dataSource.data.concat(event);
});
nash11
  • 8,220
  • 3
  • 19
  • 55
0

I was stuck in same problem while creating select row and apply some action over rows data. This solution for your problem

imports..................
import { MatTableDataSource } from '@angular/material';
@component({ ...

export class StreamComponent implements OnInit {
// Initialise MatTableDataSource as empty
dataSource = new MatTableDataSource<Element[]>();

constructor() {}
...
// if you simply push data in dataSource then indexed 0 element remain undefined
// better way to do this as follows
 this.dataSource.data = val as any;

  // for your problem

   ngOnInit() {
  // ** important note .... I am giving solution assuming the subscription data is array of objects. Like in your case stream and in second event(parameter as callback)
    this.streamService.getAllStream().subscribe(stream => {
       // do it this way
        this.dataSource.data = stream as any;
     // note if you simply put it as 'this.dataSource.data = stream' then TS show you error as '[ts] Type 'string' is not assignable to type '{}[]''
    });
    this.socket.on('newMessage', (event) => {
        console.log('Datasource', event);
          // for this value
       // this.dataSource.MatTableDataSource.filteredData.push(event);   // I couldn't get what you are doing here 
     // SO try to explain what you are getting in this callback function val as event parameter ????????????????? 
     // but you can get it this ways

       this.dataSource.filteredData = event as any;
    });
  }

Hope this will help you . If you have any question just ping me.

Vikas Kumar
  • 283
  • 1
  • 3
  • 10
0

For me, nothing of these answers didn't work. I had an observable that I subscribe to get new data. the only solution that works for me was:

 this.dataService.pointsNodesObservable.subscribe((data) => {
  this.dataSource = new InsertionTableDataSource(this.paginator, this.sort, this.dataService);
  this.dataSource.data = data;
});

render like a charm!

Ezri Y
  • 407
  • 3
  • 15
0

Insert the element at the beginning or any position you want then call the change subscription method of Mat Table dataSource. Here is the code.

this.dataSource.data.unshift(newItem);
this.dataSource._updateChangeSubscription();
Zia Khan
  • 188
  • 2
  • 9