ngTbale假分页实现排序、搜索、导出CSV等功能
一. ngTable功能简化
使用ngTable经常有分页,排序,过滤等功能,实现诸多功能较为麻烦。为了方便开发过程,可以抽取一些table共同点写一个公有方法。
注意:
1. 由于很多特别的需求,可能表格不一定适用于自己的项目,部分地方需要自己调试到适合自己的项目。
2. 部分功能没有使用ngTable自带的功能,只因为PM需求与之不符合
代码如下:
1. ListTableEX代码:
1 angular.module('newApp') 2 .factory('ListTableEX', ['ngTableParams', 'getStorage', function (ngTableParams, getStorage) { 3 var store = getStorage('localStorage'); 4 5 /** 6 * 创建表格 7 * @param options1 8 * @param options2 9 * @returns {ngTableParams|*} 10 */ 11 function createListTable(options1, options2) { 12 var listTable = new ngTableParams(options1, { 13 counts: options2.counts, 14 getData: function ($defer, params) { 15 // 如果不需要强制刷新数据且有缓存数据,那么直接使用缓存数据 16 // Note: 由于api可能返回空数据或者出错,因此不能通过判断 17 // 缓存数组容量是否为0来判断有无缓存,而是通过判断该数组是否为 18 // undefined 19 if (!listTable.needForceReload && params.items) { 20 // 复制items中的数据,以免shownItems的更改影响到items 21 params.shownItems = []; 22 for (var index in params.items) { 23 params.shownItems.push(params.items[index]); 24 } 25 var itemsFilter = options2.itemsFilter; 26 if (itemsFilter) { 27 params.shownItems = itemsFilter(params.shownItems); 28 } 29 sortTable(listTable); 30 listTable.calculatePages(); 31 var itemsCount = params.shownItems.length; 32 var itemCountPerPage = listTable.count(); 33 var currentPage = listTable.page(); 34 params.total(itemsCount); 35 $defer.resolve(subArray(params.shownItems, (currentPage - 1) * itemCountPerPage, itemCountPerPage)); 36 return; 37 } 38 listTable.needForceReload = false; 39 var apiParams = { 40 limit: 20000000, 41 index: 1 42 }; 43 options2.getData(apiParams).then(function (data) { 44 listTable.resetSortParams(); 45 listTable.resetCheckboxes(); 46 var resData = data.data.data; 47 var onDataResult = options2.onDataResult; 48 if(onDataResult) { 49 onDataResult(resData); 50 } 51 var items = resData.items; 52 var dataPreprocessFunc = options2.dataPreprocessFunc; 53 if (dataPreprocessFunc) { 54 dataPreprocessFunc(items); 55 } 56 // 即使api没有返回数据,也应该设置缓存数组,否则会导致无限刷新 57 params.items = items ? items : []; 58 // 由于已经获取到了数据,那么重新reload一次,复用有缓存数据时的处理逻辑 59 listTable.reload(); 60 }, function () { 61 }); 62 } 63 }); 64 listTable.tableName = options1.tableName; 65 listTable.titles = options1.titles; 66 listTable.needForceReload = false; 67 listTable.getItemId = options2.getItemId; 68 listTable.forceReload = function () { 69 listTable.needForceReload = true; 70 listTable.reload(); 71 } 72 return listTable; 73 }; 74 75 /** 76 * 初始化排序功能 77 * @param listTable 78 */ 79 function initSort(listTable) { 80 listTable.nextSortType = function () { 81 return listTable.sortParams.type == 'asc' ? 'desc' : 'asc'; 82 }; 83 listTable.isColumnSorting = function (key) { 84 return listTable.sortParams.key == key; 85 }; 86 listTable.isColumnSortingByASC = function (key) { 87 return listTable.isColumnSorting(key) && listTable.sortParams.type == 'asc'; 88 }; 89 listTable.isColumnSortingByDESC = function (key) { 90 return listTable.isColumnSorting(key) && listTable.sortParams.type == 'desc'; 91 }; 92 listTable.resetSortParams = function () { 93 listTable.sortParams = {key: '', type: ""}; 94 } 95 listTable.resetSortParams(); 96 }; 97 98 /** 99 * 初始化条目选择框 100 * @param listTable 101 * @param withCheckboxes 102 * @returns {*} 103 */ 104 function initCheckboxes(listTable, withCheckboxes) { 105 if (withCheckboxes) { 106 listTable.toggleCheckAll = function () { 107 var checkedCount = listTable.checkedIds().length; 108 if (checkedCount == listTable.shownItems.length) { 109 listTable.deselectAll(); 110 } else { 111 listTable.selectAll(); 112 } 113 }; 114 listTable.selectAll = function () { 115 listTable.resetCheckboxes(); 116 listTable.checkboxes.selectAll = true; 117 for (var index in listTable.shownItems) { 118 listTable.checkboxes.items[listTable.getItemId(listTable.shownItems[index])] = true; 119 } 120 }; 121 listTable.deselectAll = function () { 122 listTable.resetCheckboxes(); 123 listTable.checkboxes.selectAll = false; 124 for (var index in listTable.shownItems) { 125 listTable.checkboxes.items[listTable.getItemId(listTable.shownItems[index])] = false; 126 } 127 }; 128 listTable.checkedIds = function () { 129 var ids = []; 130 for (var index in listTable.shownItems) { 131 var id = listTable.getItemId(listTable.shownItems[index]); 132 if (listTable.checkboxes.items[id]) { 133 ids.push(id); 134 } 135 } 136 listTable.checkboxes.selectAll = listTable.shownItems ? (listTable.shownItems.length == ids.length) : false; 137 return ids; 138 } 139 listTable.resetCheckboxes = function () { 140 listTable.checkboxes = { 141 selectAll: false, 142 items: {} 143 }; 144 }; 145 } else { 146 listTable.resetCheckboxes = function () { 147 } 148 } 149 listTable.resetCheckboxes(); 150 return listTable; 151 }; 152 153 /** 154 * 初始话分页功能 155 * @param listTable 表格 156 * @private 157 */ 158 function initPagination(listTable) { 159 var STORAGE_PAGESIZE_KEY = getKeyForStoragePageSize(listTable); 160 161 if (store.get(STORAGE_PAGESIZE_KEY)) { 162 listTable.count(store.get(STORAGE_PAGESIZE_KEY)['value']); 163 } 164 listTable.editCount = listTable.count(); 165 166 listTable.prePage = function () { 167 var page = listTable.page(); 168 if (page > 1) { 169 listTable.page(page - 1); 170 } 171 }; 172 listTable.nextPage = function () { 173 var page = listTable.page(); 174 if (page < Math.ceil(1.0 * listTable.total() / listTable.count())) { 175 listTable.page(page + 1); 176 } 177 }; 178 listTable.calculatePages = function () { 179 var itemsCount = listTable.shownItems.length; 180 // 计算页导航条数据 181 var itemCountPerPage = listTable.count(); 182 store.set(STORAGE_PAGESIZE_KEY, { 183 'value': itemCountPerPage 184 }); 185 var totalPage = Math.ceil(1.0 * itemsCount / itemCountPerPage); 186 // 当前页最大为页面总数,最小为1 187 var currentPage = Math.max(1, Math.min(listTable.page(), totalPage)); 188 listTable.page(currentPage); 189 listTable.pages = calculateTablePages(currentPage, totalPage); 190 } 191 }; 192 193 function getKeyForStoragePageSize(listTable) { 194 return 'list_table_page_size@' + listTable.tableName; 195 }; 196 197 /** 198 * 从原始数组中分割出一段连续的数组,该操作不会影响原始数组 199 * @param array 原始数组 200 * @param startIndex 分割起始位置 201 * @param length 分割数量 202 * @returns {Array} 分割结果 203 */ 204 function subArray(array, startIndex, length) { 205 var result = []; 206 if (array && startIndex > -1 && length > 0) { 207 var maxIndex = Math.min(startIndex + length, array.length); 208 for (var i = startIndex; i < maxIndex; i++) { 209 result.push(array[i]); 210 } 211 } 212 return result; 213 }; 214 215 /** 216 * 对数据进行排序,该操作将直接操作传入的数组顺序 217 * @param items 数据数组 218 * @param key 数据对象的排序关键字 219 * @param sortType 排序类型,'asc'或者'desc' 220 */ 221 function sortItems(items, key, sortType) { 222 if (!items || items.length < 1 || !key || key.length < 1) { 223 return; 224 } 225 items.sort( 226 function compareFunction(param1, param2) { 227 var value1 = param1[key]; 228 var value2 = param2[key]; 229 if (typeof(value1) === 'number' && typeof(value2) === 'number') { 230 return sortType * (value1 - value2); 231 } 232 return sortType * ((value1 + '').localeCompare(value2 + '')); 233 } 234 ); 235 }; 236 237 function calculateTablePages(currentPage, totalPage) { 238 if (totalPage == 0) { 239 return []; 240 } 241 var end = Math.min(9, totalPage); 242 var pages = [currentPage]; 243 while (pages.length < end) { 244 var pre = pages[0] - 1; 245 var next = pages[pages.length - 1] + 1; 246 if (pre >= 1) { 247 pages.unshift(pre); 248 } 249 if (next <= totalPage) { 250 pages.push(next); 251 } 252 } 253 return pages; 254 }; 255 256 function sortTable(listTable) { 257 var sortParams = listTable.sorting(); 258 var sortKey; 259 var sortType; 260 for (var key in sortParams) { 261 sortKey = key; 262 sortType = sortParams[key]; 263 } 264 if (!sortType || sortType.length < 1) { 265 sortType = 'asc'; 266 } 267 listTable.sortParams.key = sortKey; 268 listTable.sortParams.type = sortType; 269 if (sortKey && sortKey.length > 0) { 270 sortItems(listTable.shownItems, sortKey, sortType == 'asc' ? 1 : -1); 271 } 272 }; 273 274 function init(options1, options2) { 275 var listTable = createListTable(options1, options2); 276 initSort(listTable); 277 initCheckboxes(listTable, options1.withCheckboxes); 278 initPagination(listTable); 279 return listTable; 280 }; 281 282 return { 283 init: init, 284 }; 285 }]);
2. ListTableEX分页代码:
1 <div class="ng-cloak dahuo-pagination" ng-if="params.pages.length > 0"> 2 <div class="col-lg-6"> 3 <div class="dataTables_info" role="status" aria-live="polite"> 4 {{ params.count() * (params.page() - 1) + 1 }}~ 5 {{ params.count() * (params.page() - 1) + params.data.length }}条,共{{ 6 params.total() }}条记录 7 </div> 8 </div> 9 <div class="col-lg-6 pagination2" style="margin-bottom: 40px"> 10 <div class="dataTables_paginate paging_simple_numbers"> 11 <ul class="pagination ng-table-pagination"> 12 <li><a href="" ng-click="params.prePage()">上一页</a></li> 13 <li ng-repeat="page in params.pages" 14 ng-class="{'active' : page == params.page()}"> 15 <a href="" ng-click="params.page(page)">{{ page }}</a> 16 </li> 17 <li><a href="" ng-click="params.nextPage()">下一页</a></li> 18 </ul> 19 </div> 20 <div class="col-lg-3 pull-right"> 21 {{params.testOK}} 22 <select class="select form-control" ng-model="params.editCount" ng-change="params.count(params.editCount)" 23 style="padding: 6px; margin-top: 2px;min-width: 80px;margin-right: 10px" 24 ng-options="size for size in params.settings().counts"> 25 </select> 26 </div> 27 </div> 28 </div>
由于PM的需求变更较大,使用方法可能各不相同。
基本使用方法如下:
1. HTML
本表格是一个有checkBox的表格,具有编辑,新建,上线,下线等功能。
1 <div> 2 <div class="row"> 3 <div class="col-lg-12 portlets"> 4 <div class="panel"> 5 <div class="panel-content"> 6 <div class="row dahuo-table-toolbar"> 7 <div class="col-xs-6"> 8 <button type="button" class="btn btn-default" ng-click="activityupload()" ng-disabled="checkedCount() < 1">发布</button> 9 <button type="button" class="btn btn-default" ng-click="activityoffline()" ng-disabled="checkedCount() < 1">下线</button> 10 {{ checkedCount() }}个被选中 11 </div> 12 <div class="col-xs-6"> 13 <div class="pull-right"> 14 <a href="#operation-activityadd"> 15 <button type="button" class="btn btn-primary">+ 新建活动</button> 16 </a> 17 </div> 18 </div> 19 </div> 20 <div class="table-responsive"> 21 <table ng-table="activityTable" class="table trans-table table-hover text-left dahuo-table" 22 template-pagination="layouts/pagination_v2.html" width="100%"> 23 <thead> 24 <tr> 25 <th width="5%" class="dahuo-table-header-th text-center" header="'ng-table/headers/checkbox.html'"> 26 <span class="list-checkbox"> 27 <input type="checkbox" ng-model="activityTable.checkboxes.selectAll" ng-disabled="activityTable.items.length < 1" ng-click="activityTable.toggleCheckAll()"/> 28 <span></span> 29 </span> 30 </th> 31 <th width="{{title.width}}" ng-repeat="title in activityTable.titles" ng-class="{'sort': title.sortable}" ng-click="title.sortable ? activityTable.sorting(title.key, activityTable.nextSortType()) : 0">{{ title.name }} 32 <li class="fa pull-right" ng-if="title.sortable" ng-class="{'fa-sort-asc': activityTable.isColumnSortingByASC(title.key), 'fa-sort-desc': activityTable.isColumnSortingByDESC(title.key), 'fa-unsorted': !activityTable.isColumnSorting(title.key)}" aria-hidden="true" ng-style="{'color' : (!activityTable.isColumnSorting(title.key) ? '#c9c9c9' : '')}"></li> 33 </th> 34 </tr> 35 </thead> 36 <tbody> 37 <tr ng-repeat="item in $data"> 38 <td class="text-center" header="'ng-table/headers/checkbox.html'"> 39 <span class="list-checkbox"> 40 <input type="checkbox" ng-model="activityTable.checkboxes.items[item.id]"/> 41 <span></span> 42 </span> 43 </td> 44 <td><a href="#operation-activityedit/{{item.id}}">{{自定义绑值}}</a></td> 45 <td>{{自定义绑值}}</td> 46 <td>{{自定义绑值}}</td> 47 <td>{{自定义绑值}}</td> 48 <td>{{自定义绑值}}</td> 49 </tr> 50 <tr style="height: 100px" ng-if="$data.length < 1"> 51 <td colspan="6" class="text-center">暂时没有数据</td> 52 </tr> 53 </tbody> 54 </table> 55 </div> 56 </div> 57 </div> 58 </div> 59 </div> 60 </div>
2. JS
字段解释:
(1).tableName:table名称,用于缓存数据时使用的localStorage的key
(2).titles:table表头控制项目,key是绑定数据的key,name是显示名称,sortable是否可排序
(3).withCheckboxes:表格是否包含checkboxes的列
(4).page:当前页
(5).count:每页显示条目数量
(6).sorting:默认排序的key和升降序
(7).counts:每页显示条数数量更改项
(8).getData:获取表格的数据,一般是发送API。这里有一个坑,后续会详细说明。
(9).getItemId:对应checkBoxes的itemId,复制即可。
依赖项:
DS:API请求依赖
logger:提示信息依赖
1 angular.module('newApp') 2 .controller('ActivityListCtrl', ['$scope', 'ds.activity', 'logger', 'ListTableEX', 3 function($scope, DS, logger, ListTableEX) { 4 $scope.activityTable = new ListTableEX.init( 5 { 6 tableName:'operation.activity.activity.list', 7 titles:[ 8 {key: '', name: "活动ID", sortable: true, width: '10%'}, 9 {key: '', name: "活动名称", sortable: true, width: '35%'}, 10 {key: '', name: "开始时间", sortable: true, width: '20%'}, 11 {key: '', name: "结束时间", sortable: true, width: '20%'}, 12 {key: '', name: "状态", sortable: true, width: '10%'}, 13 ], 14 withCheckboxes: true, 15 page: 1, 16 count: 25, 17 sorting: {'id': 'desc'}, 18 }, 19 { 20 counts: [10, 25, 50, 100], 21 getData: function(apiParams) { 22 return DS.listActivity(apiParams); 23 }, 24 getItemId: function(item) { 25 return item.id; 26 } 27 } 28 ); 29 30 $scope.checkedCount = function() { 31 return $scope.activityTable.checkedIds().length; 32 } 33 34 $scope.activityupload = function() { 35 var checkedIds = $scope.activityTable.checkedIds(); 36 if (checkedIds.length < 1) { 37 logger.warning('请选择操作对象'); 38 return; 39 } 40 41 DS.activityUpload(checkedIds) 42 .then(function() { 43 $scope.activityTable.forceReload(); 44 logger.success('上线成功'); 45 }, function(error) { 46 logger.error('上线失败'); 47 }); 48 }; 49 50 $scope.activityoffline = function() { 51 var checkedIds = $scope.activityTable.checkedIds(); 52 if (checkedIds.length < 1) { 53 logger.warning('请选择操作对象'); 54 return; 55 } 56 57 DS.activityOffline(checkedIds) 58 .then(function() { 59 $scope.activityTable.forceReload(); 60 logger.success('下线成功'); 61 }, function(error) { 62 logger.error('下线失败'); 63 }); 64 }; 65 } 66 ]);
二. 使用中注意的问题:
1. 需要自己更改适应服务端返回数据的处理代码:
对应代码段时listTableEX的“var resData = data.data.data”
2. 使用中可能Table数据不是来自于API:
需要使用如下代码:
1 getData: function(apiParams) { 2 return {then: function(callback){ 3 var data = { 4 data:{ 5 data:{ 6 items: [], 7 } 8 } 9 } 10 callback(data); 11 }} 12 }
然后强行加载数据:
1 $scope.ActivityTable.items = items; 2 $scope.ActivityTable.page(1); 3 $scope.ActivityTable.reload();
3. 可能数据来自API,但是触发时机不是Table new出来时:
New出来的代码更改如下:
1 getData: function(apiParams) { 2 if (满足API发送条件){ 3 var apiParams = { 4 (自定义) 5 }; 6 return DS.activityList(apiParams); 7 } 8 return {then: function(callback){ 9 var data = { 10 data:{ 11 data:{ 12 items: [], 13 } 14 } 15 } 16 callback(data); 17 }} 18 }
然后发送API获取数据:
1 $scope.ActivityTable.page(1) 2 $scope.ActivityTable.forceReload();
三. 搜索,过滤,导出CSV插件
代码都有较长的注释,这里不做过多的介绍。
导出CSV依赖插件FileSaver.js。
angular.module('newApp')
.factory('ListTableEXExtend', function (getStorage) {
/*
First method:
ListTableEXExtend.searchFilter(Array, String, Boolean);
Input:
Array: ['a', 'b', 'c', 'aa', 'ab', 'cc']
String: 'a'
Boolean: true
Output:
Array: ['a', 'aa', 'ab']
Second method:
ListTableEXExtend.searchFilter(ObjectArray, String, Boolean, Array)
Input:
ObjectArray: [
{id:111, name:'aaa', ex1:'sss', ex2: 'sss'},
{id:222, name:'11aa', ex1:'sss', ex2: 'sss'},
{id:333, name:'a1a1', ex1:'sss', ex2: 'sss'},
{id:444, name:'bbbb', ex1:'sss', ex2: 'sss'}
]
String: 1 or '1'
Boolean: true
Array: ['id', 'name']
Output:
ObjectArray: [
{id:111, name:'aaa', ex1:'sss', ex2: 'sss'},
{id:222, name:'11aa', ex1:'sss', ex2: 'sss'},
{id:333, name:'a1a1', ex1:'sss', ex2: 'sss'}
]
Others: return items
Note: if has too much data, Please use method: delay reload Table
*/
function searchFilter(items, keyword, isIgnoreUpper, regArr) {
if (!items) {
console.log("Items must be defined");
return;
}
if (typeof(items) !== "object" || items.constructor !== Array) {
console.log("Items must be a array");
return items;
}
if (items.length === 0) {
return items;
}
if (!keyword && keyword !== 0 && keyword !== '0') {
return items;
}
/* Copy Items*/
var newItems = items.concat();
if (isIgnoreUpper){
var keyword = keyword.toString().toLowerCase();
}else {
var keyword = keyword.toString();
}
if (!regArr) {
var resultItem = [];
for (var index in items) {
try {
if (isIgnoreUpper){
var newItem = newItems[index].toString().toLowerCase();
}else {
var newItem = newItems[index].toString();
}
} catch (e) {
console.log(e.name + ": " + e.message);
}
if (newItem.indexOf(keyword) !== -1) {
resultItem.push(items[index]);
}
}
return resultItem;
}
if (typeof(regArr) !== "object" || regArr.constructor !== Array) {
console.log("regArr must be a array");
return items;
}
if (regArr.length === 0) {
return items;
}
var newRegArr = regArr.concat();
for (var index in newRegArr) {
try {
newRegArr[index].toString();
} catch (e) {
console.log(e.name + ": " + e.message);
}
}
var resultItem = [];
for (var index in items) {
try {
var newItem = newItems[index];
if (typeof(newItem) !== "object" || newItem.constructor !== Object) {
console.log("Items must be a object array");
return items;
}
for (var index2 in newRegArr) {
if (!newItem[newRegArr[index2]] && newItem[newRegArr[index2]] !== 0 && newItem[newRegArr[index2]] !== '0') {
continue;
}
if (isIgnoreUpper){
var newItemToStr = newItem[newRegArr[index2]].toString().toLowerCase();
}else {
var newItemToStr = newItem[newRegArr[index2]].toString();
}
if (newItemToStr.indexOf(keyword) !== -1) {
resultItem.push(items[index]);
break;
}
}
} catch (e) {
console.log(e.name + ": " + e.message);
}
}
return resultItem;
}
/*
Method:
ListTableEXExtend.selectFilter(ObjectArray, Key, Value)
Input:
ObjectArray: [
{id:111, name:'aaa', product_id:'123', ex: 'sss'},
{id:222, name:'11aa', product_id:'113', ex: 'sss'},
{id:333, name:'a1a1', product_id:'123', ex: 'sss'},
{id:444, name:'bbbb', product_id:'113', ex: 'sss'},
]
Key: product_id
Value: 123
Output:
ObjectArray: [
{id:111, name:'aaa', product_id:'123', ex: 'sss'},
{id:333, name:'a1a1', product_id:'123', ex: 'sss'}
]
Others: return items
*/
function selectFilter(items, key, value) {
if (!items) {
console.log("Items must be defined");
return;
}
if (!key || !value) {
console.log("Key, value must be defined");
return items;
}
if (typeof(items) !== "object" || items.constructor !== Array) {
console.log("Items must be a array");
return items;
}
if (items.length === 0) {
return items;
}
key = key.toString();
var resultItem = []
for (var index in items) {
try {
var item = items[index];
if (typeof(item) !== "object" || item.constructor !== Object) {
console.log("Items must be a object array");
return items;
}
if (item[key] == undefined || item[key] == null || item[key] === '') {
continue;
}
if (value === 'defined'){
resultItem.push(items[index]);
continue;
}
if (item[key] == value) {
resultItem.push(items[index]);
}
} catch (e) {
console.log(e.name + ": " + e.message);
}
}
return resultItem;
}
function _getDateTime(date){
if (angular.isDate(date)){
return date.getTime();
}else {
return new Date(date).getTime();
}
}
function timeFilter(items, start, end, key){
if (!items) {
console.log("Items must be defined");
return;
}
if (!angular.isArray(items)) {
console.log("Items must be a array");
return items;
}
var startTime = _getDateTime(start);
var endTime = _getDateTime(end);
var newItems = items.concat();
var resultItems = [];
var newKey = key.toString()
for (var index in items){
var newItem = items[index];
if (!angular.isObject(newItem)) {
console.log("Items must be a object array");
return items;
}
if (!newItem[newKey]) {
continue;
}
var newItemDate = _getDateTime(newItem[newKey]);
if (newItemDate >= startTime && newItemDate <= endTime){
resultItems.push(newItem);
}
}
return resultItems;
}
function editItem(items, item, primaryKey, isCreated){
if (!items) {
console.log("Items must be defined");
return;
}
if (!item || !primaryKey) {
console.log("Item, primaryKey must be defined");
return items;
}
if (!angular.isArray(items)) {
console.log("Items must be a array");
return items;
}
if (!angular.isObject(item)) {
console.log("Item must be a object");
return items;
}
primaryKey = primaryKey.toString();
/* Copy Items*/
var newItems = items.concat();
for (var index in newItems) {
try {
var newItem = newItems[index];
if (!angular.isObject(newItem)) {
console.log("Items must be a object array");
return items;
}
if (!newItem[primaryKey] && newItem[primaryKey] != 0 && !item[primaryKey] && item[primaryKey] != 0) {
continue;
}else {
if (newItem[primaryKey] == item[primaryKey]){
newItems[index] = item;
isCreated = false;
}
}
} catch (e) {
console.log(e.name + ": " + e.message);
return items;
}
}
if (isCreated){
newItems.push(item);
}
return newItems;
}
/*
Usage:
var csv = new ListTableEXExtend.Csv({
--titles(Option):
ObjectArray.
just lick listtableex.titles, 'key' and 'name' is needed.
This is used for defining Csv col.
--items(Option):
ObjectArray.
just lick listtableex.items.
--filterData(Option):
FuncObject.
This is used for switch data.
For example:
{id(Must be titles'key): function(input){return input + 'Test'}}.
--defaultChar(Option):
String or Object.
String: All table's default char.
Object: One col default char(Must be titles'key).
This is used for set default char when <td></td> is like this.
--handleData(Option):
Func.
Must return Data.
Data is the fileData.
--fileName(Option):
String.
--delimiter(Option):
String.
This is used for maping <td> and another <td> with 'char'.
Default is ',' used for Csv.
});
csv has to public method:
--setItems(Option)
Note: The items maybe is undefined because API are not repond this time.
So you need to set items again.
--setTitles(Option)
Note: The titles maybe changed.
So you need to set titles again.
--exportCsv(Necessary, Main)
Others are private.
If you have to get proto, you can add public method.
*/
function Csv(options) {
var items = options.items;
var titles = options.titles;
var filterData = options.filterData;
var handleData = options.handleData;
var defaultChar = options.defaultChar;
var type = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=utf-8";
var fileName = options.fileName ? options.fileName : 'download.csv';
var delimiter = options.delimiter ? options.delimiter : ',';
var data = '';
function _download(data, fileName) {
var blob = new Blob(["\ufeff" + data], {
type: type
});
saveAs(blob, fileName);
}
function _getData(items, titles) {
if (!items || !titles || typeof(items) !== "object" || items.constructor !== Array || typeof(titles) !== "object" || titles.constructor !== Array) {
console.log("Usage:\n\titems: ObjectArray\n\ttitles: ObjectArray\n");
return false;
}
return true;
}
function _handleData(items, titles, delimiter, filterData, handleData, defaultChar) {
data = '';
if (handleData) {
data = handleData(items, titles);
} else {
var rowData = '';
for (var index in titles) {
rowData += titles[index].name + delimiter;
}
rowData = rowData.slice(0, rowData.length - 1);
data += rowData + '\n';
for (var i in items) {
var item = items[i];
var rowData = '';
for (var j in titles) {
var title = titles[j];
if (!title.key) {
return '';
} else if (item[title.key] === undefined || item[title.key] === '') {
if (defaultChar) {
if (angular.isObject(defaultChar)) {
if (defaultChar[title.key] !== undefined) {
rowData += defaultChar[title.key] + delimiter;
} else {
rowData += delimiter;
}
} else {
rowData += defaultChar + delimiter;
}
} else {
rowData += delimiter;
}
} else {
if (filterData) {
if (title.filterKey && filterData[title.filterKey]) {
var filterResultData = filterData[title.filterKey](item[title.key]);
rowData += filterResultData + delimiter;
} else if (filterData[title.key]) {
var filterResultData = filterData[title.key](item[title.key]);
rowData += filterResultData + delimiter;
} else {
rowData += item[title.key] + delimiter;
}
} else {
rowData += item[title.key] + delimiter;
}
}
}
rowData = rowData.slice(0, rowData.length - 1);
data += rowData + '\n';
}
}
return data;
}
this.setItems = function (value) {
items = value;
}
this.setTitles = function (value) {
titles = value;
}
this.exportCsv = function () {
if (!_getData(items, titles)) {
return data;
}
data = _handleData(items, titles, delimiter, filterData, handleData, defaultChar);
_download(data, fileName);
}
}
return {
searchFilter: searchFilter,
selectFilter: selectFilter,
timeFilter: timeFilter,
editItem: editItem,
Csv: Csv
}
})
以上导出CSV方法是导出所有分页的数据,当表结构并非以[{...}, {...}]这种结构返回则比较难以实现通用性。对于没有分页的表,可以解析html来导出当页的数据到CSV。
angular.module('newApp')
.directive('exportCsv', ['$parse', '$timeout', function ($parse, $timeout) {
/*
This is used for export ShownTable, Only current page.
You do not need to handle data.
For example:
if ngTable:
<a class="btn btn-primary" ng-click='csv.generate($event, "FileName", "ngTableBindname")' href=''>export</a>
<table ng-table="ngTableBindname" class="table trans-table table-hover dahuo-table" width="100%" export-csv="csv">
else:
<a class="btn btn-primary" ng-click='csv.generate($event, "FileName", "tableId")' href=''>export</a>
<table id="tableId" class="table trans-table table-hover dahuo-table" width="100%" export-csv="csv">
*/
var delimiter = ',';
var tables = {};
var type = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=utf-8";
return {
restrict: 'A',
scope: false,
link: function(scope, element, attrs) {
var data = '';
if (attrs.ngTable){
tables[attrs.ngTable] = element;
}else {
tables[attrs.id] = element;
}
function stringify(str) {
return '"' + str.replace(/^\s\s*/, '').replace(/\s*\s$/, '').replace(/"/g,'""') + '"';
}
function parseTable(table) {
data = '';
if (tables[table]){
var rows = tables[table].find('tr');
}else {
return data
}
angular.forEach(rows, function(row, i) {
var tr = angular.element(row),
tds = tr.find('th'),
rowData = '';
if (tr.hasClass('ng-table-filters')) {
return;
}
if (tds.length === 0) {
tds = tr.find('td');
}
angular.forEach(tds, function(td) {
rowData += stringify(angular.element(td).text()) + Array.apply(null, Array(td.colSpan)).map(function () { return delimiter; }).join('');
});
rowData = rowData.slice(0, rowData.length - 1);
data += rowData + '\n';
});
}
function download(data, filename) {
var blob = new Blob(["\ufeff" + data], {
type: type
});
saveAs(blob, filename);
}
var csv = {
generate: function(event, filename, table) {
if (table){
parseTable(table);
download(data, filename);
} else {
parseTable();
download(data, filename);
}
}
};
$parse(attrs.exportCsv).assign(scope.$parent, csv);
}
}
}])

浙公网安备 33010602011771号