Угловой 9.1.0. mat-table не загружает источник данных из GET Rest Api

Я пытаюсь загрузить данные в mat-table, но когда я console.log источник данных, который я пытаюсь загрузить, он говорит, что данные не определены.

Ссылка на изображение проблемы

Полученный мной объект вложен. Запрос API

Я следую официальному руководству с веб-сайта Angular, и это материал примеры таблиц. Также здесь доступен Таблица, получающая данные через HTTP

Репозиторий:

{"BTC":{"USD":8682.55,"EUR":7851.55},"ETH":{"USD":224.8,"EUR":203.77}}

Это моя попытка: (Вы заметите, что ngAfterViewInit () не используется, это связано с тем, что пользовательский ввод Api должен ждать, я все еще возился с этим).

HTML:

<button (click)="removeList()">Reset</button>
<button (click)="initialiseTableData()">Display Value</button>


<div class="example-container mat-elevation-z8">
  <div class="example-loading-shade" *ngIf="isLoadingResults">
    <mat-spinner *ngIf="isLoadingResults"></mat-spinner>
  </div>
  <mat-table #table [dataSource]="dataSource" class="example-table" matSort matSortActive="created" matSortDisableClear
    matSortDirection="asc">

    <ng-container matColumnDef="cryptocurrency">
      <mat-header-cell *matHeaderCellDef>Cryptocurrency</mat-header-cell>
      <mat-cell *matCellDef="let row">{{ row.items.cryptovalue }}</mat-cell>
    </ng-container>

    <ng-container matColumnDef="currency">
      <mat-header-cell *matHeaderCellDef>Currency</mat-header-cell>
      <mat-cell *matCellDef="let row">{{ row.items.currency }}</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 [length]="resultsLength" [pageSize]="30">
  </mat-paginator>
</div>

А это мой .ts:

import { Component, OnInit, AfterViewInit, ViewChild } from '@angular/core';
import { HttpClient, HttpHeaders } from '@angular/common/http';
import { merge, Observable, of as observableOf } from 'rxjs';
import { catchError, map, startWith, switchMap } from 'rxjs/operators';
import { FormControl } from '@angular/forms';
import { MatSortModule, MatSort } from '@angular/material/sort';
import { MatPaginatorModule, MatPaginator } from '@angular/material/paginator';
import { MatTableDataSource } from '@angular/material/table';


export interface TableDataApi {
  items: Tabledata[];
  total_count: number;
}

export interface Tabledata {
  cryptovalue: {
    currency: string;
  };
}

@Component({
  selector: 'app-crypto-chooser',
  templateUrl: './crypto-chooser.component.html',
  styleUrls: ['./crypto-chooser.component.css']
})

export class CryptoChooserComponent implements OnInit, AfterViewInit {

  constructor(private client: HttpClient) { }

  displayedColumns = ['cryptocurrency', 'currency'];
  dataSource = new MatTableDataSource();
  resultsLength = 0;
  isLoadingResults = false;

  @ViewChild(MatPaginator) paginator: MatPaginator;
  @ViewChild(MatSort) sort: MatSort;

  initialiseTableData() {
    this.sort.sortChange.subscribe(() => this.paginator.pageIndex = 0);

    merge(this.sort.sortChange, this.paginator.page)
      .pipe(
        startWith({}),
        switchMap(() => {
          this.isLoadingResults = true;
          return this.getCoins();
        }),
        map(data => {
          this.isLoadingResults = false;
          this.resultsLength = data.total_count;
          return data.items;
        }),
        catchError(() => {
          this.isLoadingResults = false;
          return observableOf([]);
        })
      ).subscribe(data => this.dataSource.data = data);
    console.log(this.dataSource);
  }

  ngAfterViewInit() {
  }

  getCoins(): Observable<TableDataApi> {
    this.generateUrl();
    return this.client.get<TableDataApi>(this.urlComplete, this.httpHeader);
  }

person Ivan Slavko Matić    schedule 28.02.2020    source источник
comment
Попробуйте распечатать свои данные по методу подписки. Сделайте это .subscribe(data => {console.log(data); this.dataSource.data = data;}); Можете ли вы так видеть свои данные?   -  person bjdose    schedule 29.02.2020
comment
С новым MatTableDataSource (); , это дало бы мне просто undefined.   -  person Ivan Slavko Matić    schedule 29.02.2020
comment
если вы используете фрагмент кода, который я прокомментировал до того, как console.log(data); покажет undefined? или показывает полезную нагрузку API?   -  person bjdose    schedule 29.02.2020
comment
Да, с dataSource: Tabledata [] = [] он дает мне undefined.   -  person Ivan Slavko Matić    schedule 29.02.2020
comment
Можете ли вы напечатать на карте оператора переменную данных? map(data => { console.info('what is data?', data); this.isLoadingResults = false; this.resultsLength = data.total_count; return data.items; }),   -  person bjdose    schedule 29.02.2020
comment
В console.info я получаю следующее: Что такое данные? {XRP: {…}, LTC: {…}, USDT: {…}} XRP: {EUR: 0,2145} LTC: {EUR: 54,36} USDT: {EUR: 0,9068} proto: Объект , он дает мне объект, и в разделе подписки консоли я получаю значение undefined. Я думаю, это неправильно отображает объект?   -  person Ivan Slavko Matić    schedule 29.02.2020
comment
Это правильно, вы получаете объект из своего метода getCoins, посмотрите мой ответ, попробуйте сделать то, что я сказал, чтобы исправить ваши проблемы.   -  person bjdose    schedule 29.02.2020


Ответы (1)


Я прочитал некоторую документацию об API источника данных таблицы материалов Таблицы API материалов Angular, и они скажите следующее:

Источник данных таблицы, который может быть предоставлен тремя способами (в порядке сложности):

Простой массив данных (каждый объект представляет одну строку таблицы).

Поток, который генерирует массив данных при каждом изменении массива.

Объект DataSource, реализующий интерфейс подключения / отключения.

Можно использовать первый способ (Простой массив данных).

Попробуйте сделать это, чтобы использовать простой массив данных вместо объекта matTableDataSource.

// use simple array instead of new MatTableDataSource()
// and define your interface later, for the moments we will use any
dataSource: any[] = []; 

И здесь вам нужно изменить свой метод подписки, чтобы назначить ответ переменной dataSource, которая теперь представляет собой простой массив.

  initialiseTableData() {
    this.sort.sortChange.subscribe(() => this.paginator.pageIndex = 0);

    merge(this.sort.sortChange, this.paginator.page)
      .pipe(
        startWith({}),
        switchMap(() => {
          this.isLoadingResults = true;
          return this.getCoins();
        }),
        map(data => {
          console.info('data is an object!', data);
          this.resultsLength = 0; // fix this later
          this.isLoadingResults = false;
          // here, data has total_count and items attributes?
          // this.resultsLength = data.total_count;
          // I think data is an object because you're getting
          // an object as response, you're not getting a TableDataApi
          // structure
          return data;
        }),
        catchError(() => {
          this.isLoadingResults = false;
          return observableOf([]);
        })
      ).subscribe(data => {
        // data is an object, is not an array
        // but your dataSource variable needs to be an array, so
        // you have to add data into [] in order to create an array
        // with your payload
        this.dataSource = [data]; // <- assign data to dataSource
        console.info('an array with my object', this.dataSource);
      });
  }

Теперь вы добавили интерфейс с атрибутами items и totalCount, я не знаю, почему вы это сделали, потому что ваш HTTP-запрос действительно имеет полезную нагрузку объекта, то есть: это ваша полезная нагрузка, это объект. У этого объекта нет атрибутов totalCount или items. Вам нужно исправить свой интерфейс.

Полезная нагрузка ваших данных:

{"BTC":{"USD":8682.55,"EUR":7851.55},"ETH":{"USD":224.8,"EUR":203.77}}

Выше я описал способ преобразования полезной нагрузки вашего объекта в массив, добавив только ваш объект данных в [].

Удалите интерфейс и используйте его на данный момент, но позже вам нужно будет определить интерфейс с правильной структурой.

  getCoins(): Observable<any> { // define interface with your correct structure
    this.generateUrl();
    return this.client.get<any>(this.urlComplete, this.httpHeader);
  }
person bjdose    schedule 28.02.2020
comment
Хорошо, я установил источник данных, как вы сказали, и обновил функцию, чтобы вернуть ответ в интерфейсе Tabledata следующим образом: getCoins (): Observable ‹Tabledata []› {this.generateUrl (); return this.client.get ‹Tabledata []› (this.urlComplete, this.httpHeader); } console.log для данных предоставляет мне объект, подобный предполагаемому: {LTC: {…}, USDT: {…}, BCH: {…}} LTC: {EUR: 54.24} USDT: {EUR: 0.9067} BCH : {EUR: 284.24} Но я получаю сообщение об ошибке: предоставленный источник данных не соответствует массиву, Observable или DataSource, который сообщает мне, что сопоставление не удалось? - person Ivan Slavko Matić; 29.02.2020
comment
Нет, вы должны оставить свой код таким же образом, только используйте dataSource: Tabledata[] = []; вместо dataSource = new MatTableDataSource();, потому что ваш метод getCoins правильный, не меняйте его - person bjdose; 29.02.2020
comment
Я отредактировал ответ, потому что обнаружил некоторые ошибки в вашем коде. - person bjdose; 29.02.2020
comment
Хорошо, круто, теперь я так близко. Массив с моим объектом [{…}] 0: XRP: {EUR: 0.2144} LTC: {EUR: 54.36} USDT: {EUR: 0.908} proto : Длина объекта: 1 proto: Массив (0), Массив объекта хранится в массиве для источника данных! Но он по-прежнему не отображает данные в таблице, а показывает просто пустую строку. - person Ivan Slavko Matić; 29.02.2020
comment
Да, это правильно, потому что в вашем HTML-коде вы пытаетесь получить свойства из {{row.items.cryptovalue}}, а ваша структура не имеет этих атрибутов. - person bjdose; 29.02.2020
comment
Вам нужно сопоставить свой ответ (объект) со структурой того, что вы хотите. - person bjdose; 29.02.2020