Angular DataTable更新后重渲染出现重复行,刷新页面恢复正常
Angular DataTable 更新数据后行重复问题
我为Angular DataTable编写了数据生成函数generateSlotData,首次加载时数据展示正常;但更新数据后调用该函数重渲染表格时,会出现行重复的问题,刷新页面后数据显示恢复正常。
代码实现:
generateSlotData(provider: any){ this.allSlotData = []; let tempSlotData: any = []; let groups: any = []; let lanes: any = []; let customers: any = []; this.db.fetchAllData(provider).subscribe((res: any)=>{ res.rows.forEach((element: any) => { this.tempSlotData.push(Object.assign({}, element.value)); }); this.db.fetchGroups(provider).subscribe((res: any) => { res.rows.forEach((element: any) => { groups.push(Object.assign({}, element.value)); this.groupsData.push(Object.assign({}, element.value)); }); this.tempSlotData.forEach((slot: any) => { const temp = this.groupsData.find((group: any) => group._id === slot.group_id); if(temp){ slot.group_name = temp.name; } else { slot.group_name = 'Default Group'; } }); this.db.fetchLanes(provider).subscribe((res: any) => { res.rows.forEach((element: any) => { lanes.push(Object.assign({}, element.value)); this.lanesData.push(Object.assign({}, element.value)); }); this.tempSlotData.forEach((slot: any) => { const temp = this.lanesData.find((lane: any) => lane._id === slot.lane_id); if(temp){ slot.lane_name = temp.name; } else { slot.lane_name = 'Default Lane'; } }); this.db.fetchCustomers(this.provider).subscribe((res: any)=>{ res.rows.forEach((element: any) => { customers.push(Object.assign({}, element.value)); this.customersData.push(Object.assign({}, element.value)); }); this.tempSlotData.forEach((slot: any) => { const temp = this.customersData.find((customer: any) => customer.customerid === slot.user_id); if(temp){ slot.user_name = temp.fname + ' ' + temp.lname; slot.user_phone = temp.phone; slot.user_email = temp.email; } }); this.tempSlotData.forEach((slot: any) => { const d = new Date(slot.date); const day = d.toISOString().split('T')[0]; const data = { slot_id: slot._id, rev: slot._rev, date: day, token: slot.token_number, fullname: slot.user_name, phone: slot.user_phone, email: slot.user_email, time: slot.slot, lane: slot.lane_name, lane_id: slot.lane_id, group: slot.group_name, group_id: slot.group_id, status: slot.booking_status, additional_details: slot.additional_details }; this.allSlotData.push(data); }); if (this.isDtInitialized) { this.dtElement.dtInstance.then((dtInstance: DataTables.Api) => { dtInstance.destroy(); this.dtTrigger.next(this.allSlotData); }); } else { this.isDtInitialized = true; this.dtTrigger.next(this.allSlotData); } }); }); }); }); }
问题现象:
更新数据后调用该函数重渲染表格,原本4行的数据会出现多次重复:
- 更新前:表格显示4条正常数据
- 更新后:表格中原有4条数据重复出现,行数显著增加
问题原因及解决方法
1. 全局数组未清空
每次调用generateSlotData时,仅清空了allSlotData,但tempSlotData、groupsData、lanesData、customersData这些全局数组未做清空处理,每次调用都会追加新数据,导致后续关联数据时重复匹配,最终allSlotData被重复填充。
解决: 在函数开头清空所有全局数组:
generateSlotData(provider: any){ this.allSlotData = []; this.tempSlotData = []; // 清空全局tempSlotData this.groupsData = []; // 清空全局groupsData this.lanesData = []; // 清空全局lanesData this.customersData = [];// 清空全局customersData // ... 后续代码不变 }
2. 嵌套订阅导致异步逻辑混乱
多层嵌套订阅可能导致数据处理顺序不可控,且重复调用函数时,旧的订阅可能仍在执行,新订阅又启动,造成数据重复。建议用RxJS操作符扁平化订阅流,避免嵌套。
优化后的代码示例:
import { forkJoin } from 'rxjs'; import { switchMap } from 'rxjs/operators'; generateSlotData(provider: any){ this.allSlotData = []; this.tempSlotData = []; this.groupsData = []; this.lanesData = []; this.customersData = []; this.db.fetchAllData(provider).pipe( switchMap(slotRes => { slotRes.rows.forEach((element: any) => { this.tempSlotData.push(Object.assign({}, element.value)); }); // 并行获取groups、lanes、customers数据 return forkJoin([ this.db.fetchGroups(provider), this.db.fetchLanes(provider), this.db.fetchCustomers(provider) ]); }) ).subscribe(([groupRes, laneRes, customerRes]) => { // 处理groups数据 groupRes.rows.forEach((element: any) => { this.groupsData.push(Object.assign({}, element.value)); }); this.tempSlotData.forEach((slot: any) => { const temp = this.groupsData.find((group: any) => group._id === slot.group_id); slot.group_name = temp ? temp.name : 'Default Group'; }); // 处理lanes数据 laneRes.rows.forEach((element: any) => { this.lanesData.push(Object.assign({}, element.value)); }); this.tempSlotData.forEach((slot: any) => { const temp = this.lanesData.find((lane: any) => lane._id === slot.lane_id); slot.lane_name = temp ? temp.name : 'Default Lane'; }); // 处理customers数据 customerRes.rows.forEach((element: any) => { this.customersData.push(Object.assign({}, element.value)); }); this.tempSlotData.forEach((slot: any) => { const temp = this.customersData.find((customer: any) => customer.customerid === slot.user_id); if(temp){ slot.user_name = `${temp.fname} ${temp.lname}`; slot.user_phone = temp.phone; slot.user_email = temp.email; } }); // 组装最终数据 this.tempSlotData.forEach((slot: any) => { const day = new Date(slot.date).toISOString().split('T')[0]; this.allSlotData.push({ slot_id: slot._id, rev: slot._rev, date: day, token: slot.token_number, fullname: slot.user_name, phone: slot.user_phone, email: slot.user_email, time: slot.slot, lane: slot.lane_name, lane_id: slot.lane_id, group: slot.group_name, group_id: slot.group_id, status: slot.booking_status, additional_details: slot.additional_details }); }); // 更新DataTable if (this.isDtInitialized) { this.dtElement.dtInstance.then((dtInstance: DataTables.Api) => { dtInstance.destroy(); this.dtTrigger.next(this.allSlotData); }); } else { this.isDtInitialized = true; this.dtTrigger.next(this.allSlotData); } }); }
3. 优化DataTable更新逻辑
确保DataTable实例完全销毁后再触发新数据更新,也可以在dtOptions中配置destroy: true,简化更新时的实例销毁操作。
内容的提问来源于stack exchange,提问作者Nizal Sha
相关产品推荐
相关产品推荐

