Angular / DataTables: TypeError: Cannot read property 'aDataSort' of undefined with 2 API calls
Angular / DataTables: TypeError: Cannot read property 'aDataSort' of undefined with 2 API calls
我花了几个小时试图解决这个问题,但我找不到适合我的情况的答案。
问题
我想在 DataTable 中显示使用两个 API 调用组成的数据。这些调用在服务中执行,然后数据作为主题发出。订阅主题的组件然后将数据显示在数据表中。
问题是数据表没有显示,我有一个错误:TypeError: Cannot read property 'aDataSort' of undefined
。当我的数据源来自单个 API 调用时并没有发生此问题,我怀疑我担心的是数据表试图在加载数据之前实例化自身。我可以通过 *ngFor
解决问题,因为我有其他类型的错误,但我不想这样做。
In short, my question is: What am I doing wrong?
代码
这是我的代码的简化版本
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
我希望我的问题不会太混乱,我真的不知道该怎么做才能解决这个问题...
非常感谢您的帮助!
编辑:修正缩进
我强烈建议您更改发出请求的方式并将它们合并到一个 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 中订阅。几乎总有一种更优雅、更高效、更易于维护的做事方式!
我花了几个小时试图解决这个问题,但我找不到适合我的情况的答案。
问题
我想在 DataTable 中显示使用两个 API 调用组成的数据。这些调用在服务中执行,然后数据作为主题发出。订阅主题的组件然后将数据显示在数据表中。
问题是数据表没有显示,我有一个错误:TypeError: Cannot read property 'aDataSort' of undefined
。当我的数据源来自单个 API 调用时并没有发生此问题,我怀疑我担心的是数据表试图在加载数据之前实例化自身。我可以通过 *ngFor
解决问题,因为我有其他类型的错误,但我不想这样做。
In short, my question is: What am I doing wrong?
代码
这是我的代码的简化版本
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
我希望我的问题不会太混乱,我真的不知道该怎么做才能解决这个问题...
非常感谢您的帮助!
编辑:修正缩进
我强烈建议您更改发出请求的方式并将它们合并到一个 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 中订阅。几乎总有一种更优雅、更高效、更易于维护的做事方式!