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

AngularJS DataTables(i-lin) Ajax请求后渲染完整表格问题求助

解决AngularJS DataTables先显示静态列再加载动态列的用户体验问题

你遇到的核心问题是表格初始化时只有静态列,后续通过异步请求获取动态列后再更新,导致表格重新渲染,让用户看到两次界面变化。下面是几个针对性的解决方法,帮你优化用户体验:

思路一:先获取动态列数据,再初始化DataTables

这是最直接的方案——先拿到所有需要的列信息(静态+动态),再构建完整的dtColumns和dtOptions,最后让表格一次性渲染完成,用户不会看到任何中间状态。

修改代码如下:

// 先调用接口获取payment_methods,拿到列信息后再初始化表格
Module.fetchSalesData({
  startdate: $filter('date')($scope.item.startdate, 'yyyy-MM-dd'),
  enddate : $filter('date')($scope.item.enddate, 'yyyy-MM-dd'),
  // 如果接口支持,可以传length:0只拿列信息,减少数据传输量
  length: 0
}).then(function(res) {
  $scope.payment_methods = res.data;
  
  // 构建完整的列集合:静态列 + 动态列
  $scope.dtColumns = [
    DTColumnBuilder.newColumn('invoice_date')
      .withTitle('Date')
      .withOption('sWidth', '10%'),
    DTColumnBuilder.newColumn('ref_num').withTitle('Description')
  ];
  
  // 追加动态列
  angular.forEach($scope.payment_methods, function (value) {
    $scope.dtColumns.push(
      DTColumnBuilder.newColumn(value).withTitle(value)
        .notSortable().withClass('text-right')
        .withOption('stateSave', true)
        .renderWith(function (data, type, full) {
          data = typeof data === 'undefined' ? '0.00' : data;
          return $filter('currency')(data, '');
        })
    );
  });
  
  // 现在初始化DataTables的配置项
  $scope.dtOptions = DTOptionsBuilder
    .newOptions()
    .withOption('ajax', function (data, callback, settings) {
      settings.sAjaxDataProp = 'trans';
      Module.fetchSalesData({
        draw: data.draw,
        length: data.length,
        start: data.start,
        search : data.search.value,
        count_again : $scope.countAgain,
        startdate: $filter('date')($scope.item.startdate, 'yyyy-MM-dd'),
        enddate : $filter('date')($scope.item.enddate, 'yyyy-MM-dd'),
        payee_id: $scope.item.payee_id,
        staff_id: $scope.item.staff_id,
        order: data.order
      }).then(function(res) {
        callback(res);
        $scope.countAgain = false;
      })
    })
    .withOption('serverSide', true);
});

思路二:用Angular指令控制表格的显示时机

通过ng-if或ng-show隐藏表格,直到所有列都准备完毕后再显示。这样用户只会看到最终的完整表格,完全不会感知到静态列的中间状态。

修改HTML:

<!-- 只有当dtColumns包含动态列时,才渲染表格 -->
<table id="saleReport" class="report-table table-striped table table-bordered table-condensed table-hover data-table" 
       datatable="" dt-options="dtOptions" dt-columns="dtColumns" dt-instance="dtInstance"
       ng-if="dtColumns && dtColumns.length > 2">

修改AngularJS代码(去掉不可靠的setTimeout):

$scope.dtOptions = DTOptionsBuilder
  .newOptions()
  .withOption('ajax', function (data, callback, settings) {
    settings.sAjaxDataProp = 'trans';
    Module.fetchSalesData({
      draw: data.draw,
      length: data.length,
      start: data.start,
      search : data.search.value,
      count_again : $scope.countAgain,
      startdate: $filter('date')($scope.item.startdate, 'yyyy-MM-dd'),
      enddate : $filter('date')($scope.item.enddate, 'yyyy-MM-dd'),
      payee_id: $scope.item.payee_id,
      staff_id: $scope.item.staff_id,
      order: data.order
    }).then(function(res) {
      $scope.payment_methods = res.data;
      
      // 直接追加动态列,Angular会自动触发digest更新视图
      angular.forEach($scope.payment_methods, function (value) {
        $scope.dtColumns.push(
          DTColumnBuilder.newColumn(value).withTitle(value)
            .notSortable().withClass('text-right')
            .withOption('stateSave', true)
            .renderWith(function (data, type, full) {
              data = typeof data === 'undefined' ? '0.00' : data;
              return $filter('currency')(data, '');
            })
        );
      });
      
      callback(res);
      $scope.countAgain = false;
    })
  })
  .withOption('serverSide', true);

// 初始化静态列
$scope.dtColumns = [
  DTColumnBuilder.newColumn('invoice_date')
    .withTitle('Date')
    .withOption('sWidth', '10%'),
  DTColumnBuilder.newColumn('ref_num').withTitle('Description')
];

思路三:正确销毁并重新初始化表格

如果之前销毁表格失败,大概率是操作时机不对或没有在Angular上下文里处理。可以利用dtInstance管理表格实例,拿到动态列后先销毁旧表格,再更新列并重新渲染。

修改代码:

// 初始化dtInstance用于管理表格实例
$scope.dtInstance = {};

$scope.dtOptions = DTOptionsBuilder
  .newOptions()
  .withOption('ajax', function (data, callback, settings) {
    settings.sAjaxDataProp = 'trans';
    Module.fetchSalesData({
      draw: data.draw,
      length: data.length,
      start: data.start,
      search : data.search.value,
      count_again : $scope.countAgain,
      startdate: $filter('date')($scope.item.startdate, 'yyyy-MM-dd'),
      enddate : $filter('date')($scope.item.enddate, 'yyyy-MM-dd'),
      payee_id: $scope.item.payee_id,
      staff_id: $scope.item.staff_id,
      order: data.order
    }).then(function(res) {
      $scope.payment_methods = res.data;
      
      // 销毁已存在的表格实例
      if ($scope.dtInstance.DataTable) {
        $scope.dtInstance.DataTable.destroy();
      }
      
      // 重置并更新列集合
      $scope.dtColumns = [
        DTColumnBuilder.newColumn('invoice_date')
          .withTitle('Date')
          .withOption('sWidth', '10%'),
        DTColumnBuilder.newColumn('ref_num').withTitle('Description')
      ];
      
      angular.forEach($scope.payment_methods, function (value) {
        $scope.dtColumns.push(
          DTColumnBuilder.newColumn(value).withTitle(value)
            .notSortable().withClass('text-right')
            .withOption('stateSave', true)
            .renderWith(function (data, type, full) {
              data = typeof data === 'undefined' ? '0.00' : data;
              return $filter('currency')(data, '');
            })
        );
      });
      
      callback(res);
      $scope.countAgain = false;
    })
  })
  .withOption('serverSide', true);

// 初始静态列
$scope.dtColumns = [
  DTColumnBuilder.newColumn('invoice_date')
    .withTitle('Date')
    .withOption('sWidth', '10%'),
  DTColumnBuilder.newColumn('ref_num').withTitle('Description')
];

内容的提问来源于stack exchange,提问作者fife

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.06 22:49:08