1

I'm fetching data from an API; a Http get request. The returned data that I'm having is Object composed of 02 Strings and 03 Integer. I do receive the data in the console (from the "AuthenticationService"), but I failed displaying them in the material table; mat-table.

This is what I get

So I went back to the backend code to change the returned data as an Array of String ( List ) --> in this case I receive this result Even the same row duplicated 05 times !!

Here is my ts file:

import { HttpClient } from '@angular/common/http';
import { Component, OnInit, ViewChild } from '@angular/core';
import { MatPaginator } from '@angular/material/paginator';
import { MatSort } from '@angular/material/sort';
import { MatTableDataSource } from '@angular/material/table';
import { Device } from '../models/Device';
import { DeviceMi } from '../models/DeviceMi';
import { AuthenticationService } from '../service/authentication.service';

@Component({
  selector: 'app-mi',
  templateUrl: './mi.component.html',
  styleUrls: ['./mi.component.css']
})
export class MiComponent implements OnInit {
  miToDisplay: any;
  displayedColumns: string[] = ['fabrNumberModel', 'deviceNameModel', 'deviceStateModel', 'deviceTypeModel', 'deviceTemperatureModel', 'actions'];
  data3: any;
  dataSource!: MatTableDataSource<DeviceMi>;
  itemsPerPage: number = 10;
  row: any;

  @ViewChild('paginationMiDevicesListing') paginator!: MatPaginator;
  @ViewChild('sortDevicesListing') sort!: MatSort;


  constructor(
    private responseService: AuthenticationService,
  ) { }

  ngOnInit() {
    this.dataSource = new MatTableDataSource(this.miToDisplay);
    this.dataSource.paginator = this.paginator;
    this.displayMiDevices();
  }

  displayMiDevices() {
    this.data3 = localStorage.getItem('accessToken');
    console.log(this.data3);
    this.responseService.getV1Devices().subscribe( data => {
      console.log(data);
        this.miToDisplay = data;
        console.warn(this.miToDisplay);
      }
    );
    console.log(this.miToDisplay);
    
  } 

}

Here is the html file:

<div class="container" class="mat-elevation-z8">
    <mat-table [dataSource]="miToDisplay" matSort>
    
      <!-- Mi Device Id Column -->
      <ng-container matColumnDef="fabrNumberModel">
        <mat-header-cell *matHeaderCellDef mat-sort-header> Mi Device ID </mat-header-cell>
        <mat-cell *matCellDef="let element"> {{element.fabrNumberModel}} </mat-cell>
      </ng-container>
    
      <!-- Device Name Column -->
      <ng-container matColumnDef="deviceNameModel">
        <mat-header-cell *matHeaderCellDef mat-sort-header> Device Name </mat-header-cell>
        <mat-cell *matCellDef="let element"> {{element.deviceNameModel}} </mat-cell>
      </ng-container>
    
      <!-- Device State Column -->
      <ng-container matColumnDef="deviceStateModel">
        <mat-header-cell *matHeaderCellDef mat-sort-header> Device State </mat-header-cell>
        <mat-cell *matCellDef="let element"> {{element.deviceStateModel}} </mat-cell>
      </ng-container>
    
      <!-- Device Type Column -->
      <ng-container matColumnDef="deviceTypeModel">
        <mat-header-cell *matHeaderCellDef mat-sort-header> Device Type </mat-header-cell>
        <mat-cell *matCellDef="let element"> {{element.deviceTypeModel}} </mat-cell>
      </ng-container>
    
      <!-- Device Temperature Column -->
      <ng-container matColumnDef="deviceTemperatureModel">
        <mat-header-cell *matHeaderCellDef mat-sort-header> Device Temperature </mat-header-cell>
        <mat-cell *matCellDef="let element"> {{element.deviceTemperatureModel}} </mat-cell>
      </ng-container>
    
      <!-- Button Column -->
      <ng-container matColumnDef="actions">
        <th class="actionName" mat-header-cell *matHeaderCellDef><mat-icon>power_settings_new</mat-icon></th>
        <mat-cell *matCellDef="let row">
            <mat-slide-toggle class="powerOnButton" (click)="powerOnAction(row)"></mat-slide-toggle>
            <mat-icon>power_on</mat-icon>
        </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 #paginationMiDevicesListing [length]="50" [pageSizeOptions]="[5, 10, 20]"
                    showFirstLastButtons 
                    aria-label="Select page of periodic elements"
                    showFirstLastButtons=>
    </mat-paginator>
  </div>

Here is the AuthenticationService that has the Http get request:

getV1Devices(): Observable<any> {
      return this.httpServiceAuthentication.get<any>(this.url2);
    }

How can I resolve this and display the data in the table? iIn my case I should see one row in the table. Thanks.

Bilel-NEJI
  • 61
  • 9

2 Answers2

1

You are assigning this.dataSource = new MatTableDataSource(this.miToDisplay); before this.miToDisplay is filled with data coming from the API.

To overcome this problem, you have to reinit this.dataSource inside your .subscribe() block.

displayMiDevices() {
  this.data3 = localStorage.getItem('accessToken');
  console.log(this.data3);
  this.responseService.getV1Devices().subscribe((data) => {
    console.log(data);
    this.miToDisplay = data;
    console.warn(this.miToDisplay);
    this.dataSource = new MatTableDataSource(this.miToDisplay); // HERE
  });
  console.log(this.miToDisplay);
}

Also in your .html you don't bind your mat-table dataSource attribute with the variable itself. <mat-table [dataSource]="miToDisplay" matSort> << This is wrong. You need to instead bind it with the dataSource instead.

Like this <mat-table [dataSource]="dataSource" matSort>

Mohamed Karkotly
  • 1,364
  • 3
  • 13
  • 26
  • This didn't work out, in both cases; returning an object from the api or returning a List – Bilel-NEJI Jul 17 '22 at 01:31
  • Now I see the problem, when you changed the data coming from API, you had to keep it as the original one (With the key, value pairs) but send it as array, even if it would contain only one object. – Mohamed Karkotly Jul 17 '22 at 01:51
  • The contract between the columns and the sent objects is the key of your JSON, that's why you defined something called `displyedColumns` and in your html the `matColumnDef` defines the key of your JSON, that's how `mat-table` understands your data. – Mohamed Karkotly Jul 17 '22 at 01:52
  • Now `mat-table` also expect an array of this defined JSON object containing the keys we're talking about, it doesn't expect a single object, it rather understands an array containing 0 or at least 1 JSON object. – Mohamed Karkotly Jul 17 '22 at 01:54
  • So what should I do? – Bilel-NEJI Jul 17 '22 at 02:25
  • Send the data from API like this: `[{deviceNameModel: "Washing Machine", deviceStateModel: 1, ...}]` and do the adjustments mentioned in my answer. – Mohamed Karkotly Jul 17 '22 at 02:34
1

I maintained the Json response as a json (in the backend):

public Response fetchV1Devices(.....) {
// some code here
retrun Response.ok(js).build();
}

then I converted it to an Array like this this.dataSource = new Array(data); and of course in the html file <mat-table [dataSource]=dataSource matSort>

so it becomes like this:

    this.responseService.getV1Devices().subscribe( data => {
        this.miToDisplay = data;
        this.dataSource = new Array(data);
      }
    );

This topic is similar to the issue: Provided data source did not match an array, Observable, or DataSource at getTableUnknownDataSourceError

Bilel-NEJI
  • 61
  • 9