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

在AngularJS中处理API分块响应并实时填充DataTable

在AngularJS中实现分块响应实时填充DataTable

Absolutely, you can make this work in AngularJS—here’s a practical, step-by-step approach using a custom service to handle chunked responses, paired with DataTables for real-time row updates. Let’s break it down:

1. 自定义分块HTTP服务

AngularJS内置的$http会等待完整响应返回才触发Promise,所以我们需要用原生XMLHttpRequest来监听progress事件,实时获取分块数据。下面是一个可复用的服务:

angular.module('app').service('ChunkedHttpService', ['$rootScope', function($rootScope) {
  this.getChunkedData = function(url) {
    return {
      subscribe: function(callback) {
        const xhr = new XMLHttpRequest();
        xhr.open('GET', url, true);
        xhr.setRequestHeader('Accept', 'application/json');
        
        // 积累数据,处理可能的不完整JSON块
        let accumulatedData = '';

        xhr.addEventListener('progress', function(e) {
          if (e.lengthComputable) {
            // 可选:监听加载进度百分比
            const progress = (e.loaded / e.total) * 100;
            $rootScope.$broadcast('chunkedProgress', progress);
          }
          
          // 追加新收到的分块数据
          accumulatedData += xhr.responseText.substring(accumulatedData.length);
          
          try {
            // 尝试解析完整的JSON块(格式需和后端约定)
            const parsedChunk = JSON.parse(accumulatedData);
            $rootScope.$apply(function() {
              callback(parsedChunk);
            });
            accumulatedData = ''; // 解析成功后重置积累的内容
          } catch (err) {
            // 数据不完整,等待下一块分块补充
            console.debug('等待完整JSON块:', err.message);
          }
        });

        xhr.addEventListener('load', function() {
          // 请求完成后处理剩余的未解析数据
          if (accumulatedData) {
            try {
              const parsedChunk = JSON.parse(accumulatedData);
              $rootScope.$apply(function() {
                callback(parsedChunk);
              });
            } catch (err) {
              console.error('解析最终分块失败:', err);
            }
          }
          $rootScope.$broadcast('chunkedComplete');
        });

        xhr.addEventListener('error', function(err) {
          $rootScope.$broadcast('chunkedError', err);
        });

        xhr.send();

        return function() { xhr.abort(); }; // 返回取消订阅的方法
      }
    };
  };
}]);

2. 在控制器中整合DataTables

初始化DataTable后,订阅分块服务的回调,实时添加表格行:

angular.module('app').controller('ChunkedTableController', ['$scope', 'ChunkedHttpService', function($scope, ChunkedHttpService) {
  let dtInstance;
  
  // 初始化空的DataTable
  $scope.initTable = function() {
    dtInstance = $('#chunkedTable').DataTable({
      columns: [
        { title: 'ID', data: 'id' },
        { title: '名称', data: 'name' },
        { title: '数值', data: 'value' }
      ],
      processing: true,
      serverSide: false // 手动处理分块更新,不需要服务端分页
    });
  };

  // 开始获取分块数据
  $scope.startFetch = function() {
    const unsubscribe = ChunkedHttpService.getChunkedData('/api/chunked-data').subscribe(function(chunk) {
      // 根据后端返回的分块格式处理:如果是数组就遍历添加,单对象则直接添加
      if (Array.isArray(chunk)) {
        chunk.forEach(item => {
          dtInstance.row.add(item).draw(false); // false = 不重置分页状态
        });
      } else {
        dtInstance.row.add(chunk).draw(false);
      }
    });

    // 可选:监听进度、完成、错误事件
    $scope.$on('chunkedProgress', function(e, progress) {
      $scope.loadProgress = progress;
    });

    $scope.$on('chunkedComplete', function() {
      $scope.isComplete = true;
      unsubscribe();
    });

    $scope.$on('chunkedError', function(e, err) {
      console.error('分块请求失败:', err);
      unsubscribe();
    });
  };

  // 控制器加载时初始化表格
  $scope.initTable();
}]);

3. 对应的HTML模板

<div ng-controller="ChunkedTableController">
  <div>加载进度: {{ loadProgress | number:1 }}%</div>
  <button ng-click="startFetch()" ng-disabled="isComplete">开始加载</button>
  <table id="chunkedTable" class="display" width="100%"></table>
</div>

关键注意事项

  • 后端数据格式:服务默认假设后端返回的是完整的JSON块(单个对象或数组)。如果后端用换行分隔的JSON(NDJSON),可以把积累的数据按换行分割后逐个解析,这种格式更稳定。
  • 性能优化:使用draw(false)避免每次添加行都重新渲染整个表格,仅更新新增内容,保持分页和排序状态不变。
  • 指令复用:如果需要在多个组件中使用,可以把上述逻辑封装成自定义指令,示例如下:
angular.module('app').directive('chunkedDataTable', ['ChunkedHttpService', function(ChunkedHttpService) {
  return {
    restrict: 'E',
    scope: {
      apiUrl: '@',
      columns: '<'
    },
    template: `
      <div>加载进度: {{ loadProgress | number:1 }}%</div>
      <button ng-click="startFetch()" ng-disabled="isComplete">开始加载</button>
      <table class="display" width="100%"></table>
    `,
    link: function(scope, element) {
      let dtInstance;
      const table = element.find('table');

      // 链接阶段初始化表格
      dtInstance = table.DataTable({
        columns: scope.columns,
        processing: true,
        serverSide: false
      });

      scope.startFetch = function() {
        const unsubscribe = ChunkedHttpService.getChunkedData(scope.apiUrl).subscribe(function(chunk) {
          if (Array.isArray(chunk)) {
            chunk.forEach(item => dtInstance.row.add(item).draw(false));
          } else {
            dtInstance.row.add(chunk).draw(false);
          }
        });

        scope.$on('chunkedProgress', function(e, progress) {
          scope.loadProgress = progress;
        });

        scope.$on('chunkedComplete', function() {
          scope.isComplete = true;
          unsubscribe();
        });
      };
    }
  };
}]);

使用指令的HTML示例:

<chunked-data-table 
  api-url="/api/chunked-data"
  columns='[{"title":"ID","data":"id"},{"title":"名称","data":"name"},{"title":"数值","data":"value"}]'>
</chunked-data-table>

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.28 09:43:09