Angular Datatable首次渲染无数据,排序/搜索后恢复的问题求助
Angular Datatable首次渲染不加载数据,排序/搜索后正常的问题排查与解决
问题描述
使用Angular Datatable通过Ajax从API获取数据时,组件首次渲染无法加载数据,但点击列排序或使用搜索栏后数据显示正常。已在ngOnInit方法中初始化dtOptions,预期首次渲染即可加载数据。
控制台报错
main.ts:11 ERROR TypeError: Cannot read properties of undefined (reading 'length') at Jb (node_modules\datatables.net\js\jquery.dataTables.min.js:40:238) at Aa (node_modules\datatables.net\js\jquery.dataTables.min.js:62:175) at f (node_modules\datatables.net\js\jquery.dataTables.min.js:111:104) at HTMLTableElement.<anonymous> (node_modules\datatables.net\js\jquery.dataTables.min.js:111:157) at Function.each (node_modules\jquery\dist\jquery.js:385:1) at jQuery.fn.init.each (node_modules\jquery\dist\jquery.js:207:1) at jQuery.fn.init.u [as dataTable] (node_modules\datatables.net\js\jquery.dataTables.min.js:101:182) at l.fn.DataTable (node_modules\datatables.net\js\jquery.dataTables.min.js:191:467) at angular-datatables.directive.js:80:58 at timer (zone.js:2405:41)
环境信息
- NodeJS version: 18.0.0
- Angular version: 14.1.0
- Angular CLI version: 14.1.0
- jQuery version: 3.6.1
- DataTables version: ^14.0.0
- angular-datatables version: ^14.0.0
现有代码
ngOnInit代码
ngOnInit() { const me = this; // get language this.langServiceSupscribtion = this.sharedService.lang.subscribe((l) => { this.lang = l; }); this.dtOptions = { paging: true, pagingType: 'full_numbers', pageLength: 10, autoWidth: true, serverSide: true, searching: true, ajax: (dataTablesParameters: any, callback, settings) => { console.log(dataTablesParameters); dataTablesParameters.searchCriteria = me._searchCriteria; me.transactionService .getAllTransactionsFunctional(dataTablesParameters) .subscribe((resp) => { if (resp['succeeded']) { me.transactionsList = resp['data']; console.log(me.transactionsList); callback({ recordsTotal: resp['recordsTotal'], recordsFiltered: resp['recordsFiltered'], data: [], }); } else { console.log("resp['errors']"); } }); }, columns: [ { data: 'transactionType' }, { data: 'radionuclideName' }, { data: 'radioActivity' }, { data: 'createdOn' }, { data: 'facilityEn' }, { data: 'licenseNumber' }, { data: 'permitNumber' }, { data: 'manufacturerSerialNo' }, { data: 'sourceStatus' }, { data: null }, ], };
HTML代码
<table datatable [dtOptions]="dtOptions" id="sourcesDataTable" class="table datatable table-responsive table-striped table-bordered row-border table-hover table-sm w-100 overflow-auto" > <thead> <tr> <th class="select-filter"> {{ "landingPage.transactionType" | translate }} </th> <th class="select-filter"> {{ "landingPage.radionuclide" | translate }} </th> <th class="select-filter"> {{ "landingPage.radioactivity" | translate }} </th> <th class="select-filter"> {{ "landingPage.transactionDate" | translate }} </th> <th class="select-filter"> {{ "landingPage.facility" | translate }} </th> <th class="select-filter"> {{ "landingPage.license" | translate }} </th> <th class="select-filter"> {{ "landingPage.permit" | translate }} </th> <th class="select-filter"> {{ "landingPage.manufacturerSerialNo" | translate }} </th> <th class="select-filter"> {{ "landingPage.transactionStatus" | translate }} </th> <th>{{ "landingPage.view" | translate }}</th> </tr> </thead> <tbody *ngIf="transactionsList?.length != 0"> <tr *ngFor="let t of transactionsList"> <td>{{ t.transactionType }}</td> <td>{{ t.radionuclideName }}</td> <td>{{ t.radioActivity }}</td> <td>{{ t.createdOn }}</td> <td>{{ t.facilityEn }}</td> <td>{{ t.licenseNumber }}</td> <td>{{ t.permitNumber }}</td> <td>{{ t.manufacturerSerialNo }}</td> <td>{{ t.sourceStatus }}</td> <td class="px-2"> <button class="edit btn btn-success btn-sm mx-1" style="width: 100%" > <fa-icon [icon]="faListDots"> </fa-icon> </button> </td> </tr> </tbody> <tbody *ngIf="transactionsList?.length == 0"> <tr> <td colspan="10" class="no-data-available"> {{ "no_data" | translate }} </td> </tr> </tbody> <tfoot> <th> <input class="search-input" name="transactionType" type="text" placeholder="{{ 'landingPage.transactionTypePlaceholder' | translate }}" style="width: 100%" /> </th> <th> <input class="search-input" name="radionuclide" type="text" placeholder="{{ 'landingPage.radionuclidePlaceholder' | translate }}" style="width: 100%" /> </th> <th> <input class="search-input" name="radioactivity" type="text" placeholder="{{ 'landingPage.radioactivityPlaceholder' | translate }}" style="width: 100%" /> </th> <th> <input class="search-input" name="transactionType" type="text" placeholder="{{ 'landingPage.transactionDatePlaceholder' | translate }}" style="width: 100%" /> </th> <th> <input class="search-input" name="transactionType" type="text" placeholder="{{ 'landingPage.facilityPlaceholder' | translate }}" style="width: 100%" /> </th> <th> <input class="search-input" name="transactionType" type="text" placeholder="{{ 'landingPage.licensePlaceholder' | translate }}" style="width: 100%" /> </th> <th> <input class="search-input" name="transactionType" type="text" placeholder="{{ 'landingPage.permitPlaceholder' | translate }}" style="width: 100%" /> </th> <th> <input class="search-input" name="transactionType" type="text" placeholder="{{ 'landingPage.manufacturerSerialNoPlaceholder' | translate }}" style="width: 100%" /> </th> <th> <input class="search-input" name="transactionType" type="text" placeholder="{{ 'landingPage.transactionStatusPlaceholder' | translate }}" style="width: 100%" /> </th> <th></th> </tfoot> </table>
问题原因与解决方法
核心原因
transactionsList未初始化:组件初始化时transactionsList为undefined,导致模板中*ngIf="transactionsList?.length != 0"无法渲染tbody,Datatable初始化时读取不到数据引发length错误。- ServerSide回调数据冲突:ajax回调中传递
data: [],但手动用Angular模板渲染数据,这种配置让Datatable首次初始化时无法获取有效数据,而排序/搜索时重新触发ajax,此时transactionsList已赋值,模板渲染正常。
解决步骤
1. 初始化transactionsList为空数组
在组件类中定义时直接初始化:
transactionsList: any[] = [];
2. 修正ajax回调的data字段
将回调中的data改为获取到的transactionsList,让Datatable能识别数据:
callback({ recordsTotal: resp['recordsTotal'], recordsFiltered: resp['recordsFiltered'], data: me.transactionsList, });
3. 优化模板tbody判断
合并两个tbody的判断逻辑,避免undefined导致的渲染问题:
<tbody> <tr *ngFor="let t of transactionsList"> <td>{{ t.transactionType }}</td> <td>{{ t.radionuclideName }}</td> <td>{{ t.radioActivity }}</td> <td>{{ t.createdOn }}</td> <td>{{ t.facilityEn }}</td> <td>{{ t.licenseNumber }}</td> <td>{{ t.permitNumber }}</td> <td>{{ t.manufacturerSerialNo }}</td> <td>{{ t.sourceStatus }}</td> <td class="px-2"> <button class="edit btn btn-success btn-sm mx-1" style="width: 100%"> <fa-icon [icon]="faListDots"> </fa-icon> </button> </td> </tr> <tr *ngIf="transactionsList.length === 0"> <td colspan="10" class="no-data-available"> {{ "no_data" | translate }} </td> </tr> </tbody>
4. 可选:手动触发Datatable初始化(若上述步骤无效)
使用DtTrigger控制初始化时机,确保数据加载完成后再初始化Datatable:
- 组件中引入并定义
DtTrigger:
import { DtTrigger } from 'angular-datatables'; export class YourComponent implements OnInit, OnDestroy { dtTrigger: DtTrigger = new DtTrigger(); // ...其他代码 ngOnDestroy(): void { this.dtTrigger.unsubscribe(); } }
- 在ajax回调成功后触发:
me.transactionService.getAllTransactionsFunctional(dataTablesParameters).subscribe((resp) => { if (resp['succeeded']) { me.transactionsList = resp['data']; me.dtTrigger.next(); // 触发Datatable初始化 callback({ recordsTotal: resp['recordsTotal'], recordsFiltered: resp['recordsFiltered'], data: me.transactionsList, }); } });
- 模板中添加
[dtTrigger]="dtTrigger":
<table datatable [dtOptions]="dtOptions" [dtTrigger]="dtTrigger" id="sourcesDataTable" class="table datatable table-responsive table-striped table-bordered row-border table-hover table-sm w-100 overflow-auto" >
内容的提问来源于stack exchange,提问作者saleem99
相关产品推荐
相关产品推荐

