You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.30 02:49:58