Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I update my Angular Table dataSource with data from an Observable

Im trying to display data in my Angular Material Table, that I receive from my backend.

Everything works fine as long as my _timelogService.getAllTimelogs() function returns an Observable of dummy data which I hard coded into the function. As soon as I make an actualy HTTP POST request to my backend - and return an Observable of that data - the html table doesnt display any rows eventhough my dataSource contains the required data.

When I use logData() it display the correct data even if i use the actual Observable.

I suspect that my Angular Table does not get updated when the dataSource gets updated.

Did anyone else ever encounter such an issue?

export class OverviewComponent implements OnInit {

  displayedColumns: string[] = ['roomId', 'startTime', 'endTime', 
  'duration'];
  dataSource = new MatTableDataSource<TimeLogTable>();

  @ViewChild(MatPaginator, { static: true }) paginator: MatPaginator;
  @ViewChild(MatSort, { static: true }) sort: MatSort;  

  constructor(private _timelogService: TimelogService) {
    //In the Docs example the dummy data gets created here
  }

  ngOnInit(): void {
    this._timelogService.getAllTimelogs().subscribe(
      dataObs=>{
        console.log("Display received data");
        console.log(dataObs);
        this.dataSource.data = dataObs;
        this.dataSource.paginator = this.paginator;
        this.dataSource.sort = this.sort;
      }
    )
  }

  logData(){
    console.log(this.dataSource);
  }
}

My Service

getAllTimelogs(): Observable<TimeLogTable[]>{
  var timelogsArr: TimeLogTable[] = [];
  var timelogsArrDummy: TimeLogTable[] = [];

  //Call to backend
  this._http.get<any>('http://localhost:3000/gettimelogs?token=' + 
  localStorage.getItem('token'), this.httpOptions)
  .subscribe(
    res=>{
      //Create timeLogTable from json with backend response
      for (let i = 0; i < res.timelogs.length; i++) {
        timelogsArr.push({
          roomId: res.timelogs[i].roomId,
          startTime: new Date(res.timelogs[i].startTime),
          endTime: new Date (res.timelogs[i].endTime), 
          duration: "test"
        });
      }
    },
    err=>{
      //Error handling
    }
  );
  //Return the Observable - DOES NOT WORK
  return of (timelogsArr);

  /*
  //Return the dummy data as an Observable - DOES WORK
  //Dummy data
  timelogsArrDummy = [
  {roomId: 'kit_A', startTime: new Date(1631185600000), endTime: new 
  Date (1631185661311), duration: "test"},
  {roomId: 'din_A', startTime: new Date(1631185600000), endTime: new 
  Date (1631195661311), duration: "test"},
  {roomId: 'off_A', startTime: new Date(1631185600000), endTime: new 
  Date (1632185661311), duration: "test"},
  {roomId: 'kit_B', startTime: new Date(1631185600000), endTime: new 
  Date (1631885661311), duration: "test"},
  ]
  return of (timelogsArrDummy);
  */
}

Here a screenshot from the console.log(dataObs): enter image description here

like image 243
Jonas Avatar asked Feb 11 '26 01:02

Jonas


2 Answers

I found the issue that was causing my problem. My getAllTimelogs() function returned an empty Object first and then filled it up with data later. The MatTableDataSource somehow could not handle that. Here is my working code:

export class OverviewComponent implements OnInit, AfterViewInit {

  displayedColumns: string[] = ['roomId', 'startTime', 'endTime', 'duration'];
  dataSource = new MatTableDataSource<TimeLogTable>();

  @ViewChild(MatPaginator, { static: true }) paginator: MatPaginator;
  @ViewChild(MatSort, { static: true }) sort: MatSort;  
  
  constructor(private _timelogService: TimelogService) { }

  timelogsArr: TimeLogTable[] = [];

  ngOnInit(): void {
    this._timelogService.getAllTimelogs().subscribe(
      res=>{
        //Create timeLogTable from json with backend response
        console.log("alert");
        for (let i = 0; i < res.timelogs.length; i++) {
          //Change the format of the data
          this.timelogsArr.push({
            roomId: res.timelogs[i].roomId,
            startTime: new Date(res.timelogs[i].startTime),
            endTime: new Date (res.timelogs[i].endTime), 
            duration: this.msToTime(res.timelogs[i].endTime - res.timelogs[i].startTime)
          });
        }
        //Update table datasource
        this.dataSource.data = this.timelogsArr;
      }
    );
  }

  ngAfterViewInit(): void {
    this.dataSource.paginator = this.paginator;
    this.dataSource.sort = this.sort;
  }

And here the getAlltimelogs() function:

getAllTimelogs(): Observable<any>{
    return this._http.get<any>('http://localhost:3000/gettimelogs?token=' + localStorage.getItem('token'), this.httpOptions)
  }

Thanks to everyone who kindly helped me.

like image 127
Jonas Avatar answered Feb 13 '26 18:02

Jonas


You should trigger change detection just after the new data has been received.

export class OverviewComponent implements AfterViewInit {
    
      displayedColumns: string[] = ['roomId', 'startTime', 'endTime', 'duration'];
      tableData: any = [];

      dataSource = new MatTableDataSource<TimeLogTable>(this.tableData);
    
      @ViewChild(MatPaginator) paginator: MatPaginator;
      @ViewChild(MatSort) sort: MatSort;  
      
      constructor(private _timelogService: TimelogService, private _cd: ChangeDetectorRef) {  }
    
      ngAfterViewInit() {
        this._timelogService.getAllTimelogs().subscribe(
        (dataObs: any)=> {
          this.dataSource.data = dataObs;
          this.dataSource.paginator = this.paginator;
          this.dataSource.sort = this.sort;

          this._cd.detectChanges(); 
        });
        
      }
}

Please note, if the data source is being altered in the client-side, it is advisable to call renderRows() method instead of triggering change detection.

like image 39
thisdotutkarsh Avatar answered Feb 13 '26 19:02

thisdotutkarsh