在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
相关产品推荐
相关产品推荐

