0

我花了几个小时试图解决这个问题,但我找不到适合我的情况的答案。

问题

我想在使用两个 API 调用组成的 DataTable 中显示数据。这些调用在服务中执行,然后数据作为主题发出。订阅主题的组件然后将数据显示在数据表中。

问题是数据表没有出现,我有一个错误:TypeError: Cannot read property 'aDataSort' of undefined. 当我的数据源来自单个 API 调用时,这个问题没有发生,我怀疑我担心的是数据表试图在加载数据之前实例化自己。我可以通过我有其他类型的错误来解决这个问题*ngFor,我宁愿不这样做。


简而言之,我的问题是:我做错了什么?


代码

这是我的代码的简化版本

api.service.ts

import {Subject, Subscription} from 'rxjs';
import {HttpClient} from '@angular/common/http';
...

export class APIService implements OnDestroy {
  // first data to be retrieved
  private data1: data1Structure;
  data1Subject = new Subject<data1Structure>();
  data1Subscription: Subscription;

  // second data to be retrieved
  private data2: data2Structure[];
  data2Subject = new Subject<data2Structure[]>();

  constructor(private http: HttpClient) {}

  emitData1Subject(){
    this.data1Subject.next(this.data1);
  }

  getData1() {
    this.http
      .get(`${this.baseURL}data1.json`)
      .subscribe(
        (response) => {
          this.data1Source = response;
          this.emitData1Subject();
        },
        (error) => { console.log(error); }
      );
  }

  emitData2Subject(){
    this.data2Subject.next(this.data2.slice());
  }

  getData2(){
    this.data1Subject.subscribe((data) => {
      this.data1 = data;
      this.http
        .get(`${this.baseURL}data2.json`)
        .subscribe(
          (response) => {
            // Combine the two datasets
            this.data2 = this.formatData2(response);
            this.emitData2Subject();
          },
          (error) => { console.log(error); }
        );
    });
  }
  ...
}

datatable.component.ts

import {ApiService} from '../../../services/api.service';
...

export class DatatableComponent implements OnInit, OnDestroy {

  data2Subscription: Subscription;
  colSettings = [
    {title: '#', data: 'Hid', width: '5%', visible: false},
    {title: 'Date', data: 'date', width: '45%'},
    {title: 'Title', data: 'title', width: '50%'}
  ];
  dtTrigger: Subject<boolean> = new Subject();
  dtOptions: DataTables.Settings;

  constructor(private apiService: ApiService) {
    // first API call
    this.apiService.getData1();
  }

  ngOnInit(): void {
    // second API call
    this.apiService.getData2();
    this.data2Subscription = this.apiService.data2Subject.subscribe(
      (data2) => {
        this.dtOptions = this.dataTable.getTableSettings(data2, this.colSettings);
        this.dtTrigger.next();
      },
      (error) => { console.log(error); }
    );
  }

  getTableSettings(records, colSettings: DataTables.ColumnSettings[]): DataTables.Settings {
    return {
      data: records,
      columns: colSettings,
      pagingType: 'full_numbers',
      pageLength: 50,
      deferRender: true,
      dom: 'lBfrtip'
    };
  }
}

datatable.component.html

<table id="data2" datatable [dtOptions]="dtOptions" [dtTrigger]="dtWTrigger" class="row-border hover"></table>

环境

Angular version: 9.1.1
Datatables version: 9.0.2
Chrome / Mac OS Catalina 10.15.5

我希望我的问题不会太令人困惑,我真的不知道该怎么做才能解决这个问题......

非常感谢您的帮助!

编辑:修复缩进

4

1 回答 1

1

我强烈建议你改变你提出请求的方式,并将它们合并到一个 Observable 中。当您在模板中使用 HTTP 请求的结果时,最好的做法是使用async带有最终合并的 Observable 的管道:

compiledData$: Observable<CombinedDataObjectType>; // create a custom type here

ngOnInit() {
  this.compiledData$ = this.http.get('firstUrl').pipe(
    switchMap(firstData => this.http.get('secondUrl').pipe(
      map(secondData => ({ firstData, secondData }))
    )),
  );
}

您的 Observable 现在会发出一个包含两组数据的对象,因此在模板中:

<div *ngIf="compiledData$ | async as data">
  <!-- Consume data here -->
</div>

基本上,避免将逻辑放在subscribe回调中,事实上,尽可能避免在 TS 中订阅。几乎总是有一种更优雅、更高效、更易于维护的方式来做事!

于 2020-07-30T16:38:09.807 回答