一. ngTable功能简化

  使用ngTable经常有分页,排序,过滤等功能,实现诸多功能较为麻烦。为了方便开发过程,可以抽取一些table共同点写一个公有方法。

  注意:

    1. 由于很多特别的需求,可能表格不一定适用于自己的项目,部分地方需要自己调试到适合自己的项目。

    2. 部分功能没有使用ngTable自带的功能,只因为PM需求与之不符合

  代码如下:

  1. ListTableEX代码:

 angular.module('newApp')
.factory('ListTableEX', ['ngTableParams', 'getStorage', function (ngTableParams, getStorage) {
var store = getStorage('localStorage'); /**
* 创建表格
* @param options1
* @param options2
* @returns {ngTableParams|*}
*/
function createListTable(options1, options2) {
var listTable = new ngTableParams(options1, {
counts: options2.counts,
getData: function ($defer, params) {
// 如果不需要强制刷新数据且有缓存数据,那么直接使用缓存数据
// Note: 由于api可能返回空数据或者出错,因此不能通过判断
// 缓存数组容量是否为0来判断有无缓存,而是通过判断该数组是否为
// undefined
if (!listTable.needForceReload && params.items) {
// 复制items中的数据,以免shownItems的更改影响到items
params.shownItems = [];
for (var index in params.items) {
params.shownItems.push(params.items[index]);
}
var itemsFilter = options2.itemsFilter;
if (itemsFilter) {
params.shownItems = itemsFilter(params.shownItems);
}
sortTable(listTable);
listTable.calculatePages();
var itemsCount = params.shownItems.length;
var itemCountPerPage = listTable.count();
var currentPage = listTable.page();
params.total(itemsCount);
$defer.resolve(subArray(params.shownItems, (currentPage - 1) * itemCountPerPage, itemCountPerPage));
return;
}
listTable.needForceReload = false;
var apiParams = {
limit: 20000000,
index: 1
};
options2.getData(apiParams).then(function (data) {
listTable.resetSortParams();
listTable.resetCheckboxes();
var resData = data.data.data;
var onDataResult = options2.onDataResult;
if(onDataResult) {
onDataResult(resData);
}
var items = resData.items;
var dataPreprocessFunc = options2.dataPreprocessFunc;
if (dataPreprocessFunc) {
dataPreprocessFunc(items);
}
// 即使api没有返回数据,也应该设置缓存数组,否则会导致无限刷新
params.items = items ? items : [];
// 由于已经获取到了数据,那么重新reload一次,复用有缓存数据时的处理逻辑
listTable.reload();
}, function () {
});
}
});
listTable.tableName = options1.tableName;
listTable.titles = options1.titles;
listTable.needForceReload = false;
listTable.getItemId = options2.getItemId;
listTable.forceReload = function () {
listTable.needForceReload = true;
listTable.reload();
}
return listTable;
}; /**
* 初始化排序功能
* @param listTable
*/
function initSort(listTable) {
listTable.nextSortType = function () {
return listTable.sortParams.type == 'asc' ? 'desc' : 'asc';
};
listTable.isColumnSorting = function (key) {
return listTable.sortParams.key == key;
};
listTable.isColumnSortingByASC = function (key) {
return listTable.isColumnSorting(key) && listTable.sortParams.type == 'asc';
};
listTable.isColumnSortingByDESC = function (key) {
return listTable.isColumnSorting(key) && listTable.sortParams.type == 'desc';
};
listTable.resetSortParams = function () {
listTable.sortParams = {key: '', type: ""};
}
listTable.resetSortParams();
}; /**
* 初始化条目选择框
* @param listTable
* @param withCheckboxes
* @returns {*}
*/
function initCheckboxes(listTable, withCheckboxes) {
if (withCheckboxes) {
listTable.toggleCheckAll = function () {
var checkedCount = listTable.checkedIds().length;
if (checkedCount == listTable.shownItems.length) {
listTable.deselectAll();
} else {
listTable.selectAll();
}
};
listTable.selectAll = function () {
listTable.resetCheckboxes();
listTable.checkboxes.selectAll = true;
for (var index in listTable.shownItems) {
listTable.checkboxes.items[listTable.getItemId(listTable.shownItems[index])] = true;
}
};
listTable.deselectAll = function () {
listTable.resetCheckboxes();
listTable.checkboxes.selectAll = false;
for (var index in listTable.shownItems) {
listTable.checkboxes.items[listTable.getItemId(listTable.shownItems[index])] = false;
}
};
listTable.checkedIds = function () {
var ids = [];
for (var index in listTable.shownItems) {
var id = listTable.getItemId(listTable.shownItems[index]);
if (listTable.checkboxes.items[id]) {
ids.push(id);
}
}
listTable.checkboxes.selectAll = listTable.shownItems ? (listTable.shownItems.length == ids.length) : false;
return ids;
}
listTable.resetCheckboxes = function () {
listTable.checkboxes = {
selectAll: false,
items: {}
};
};
} else {
listTable.resetCheckboxes = function () {
}
}
listTable.resetCheckboxes();
return listTable;
}; /**
* 初始话分页功能
* @param listTable 表格
* @private
*/
function initPagination(listTable) {
var STORAGE_PAGESIZE_KEY = getKeyForStoragePageSize(listTable); if (store.get(STORAGE_PAGESIZE_KEY)) {
listTable.count(store.get(STORAGE_PAGESIZE_KEY)['value']);
}
listTable.editCount = listTable.count(); listTable.prePage = function () {
var page = listTable.page();
if (page > 1) {
listTable.page(page - 1);
}
};
listTable.nextPage = function () {
var page = listTable.page();
if (page < Math.ceil(1.0 * listTable.total() / listTable.count())) {
listTable.page(page + 1);
}
};
listTable.calculatePages = function () {
var itemsCount = listTable.shownItems.length;
// 计算页导航条数据
var itemCountPerPage = listTable.count();
store.set(STORAGE_PAGESIZE_KEY, {
'value': itemCountPerPage
});
var totalPage = Math.ceil(1.0 * itemsCount / itemCountPerPage);
// 当前页最大为页面总数,最小为1
var currentPage = Math.max(1, Math.min(listTable.page(), totalPage));
listTable.page(currentPage);
listTable.pages = calculateTablePages(currentPage, totalPage);
}
}; function getKeyForStoragePageSize(listTable) {
return 'list_table_page_size@' + listTable.tableName;
}; /**
* 从原始数组中分割出一段连续的数组,该操作不会影响原始数组
* @param array 原始数组
* @param startIndex 分割起始位置
* @param length 分割数量
* @returns {Array} 分割结果
*/
function subArray(array, startIndex, length) {
var result = [];
if (array && startIndex > -1 && length > 0) {
var maxIndex = Math.min(startIndex + length, array.length);
for (var i = startIndex; i < maxIndex; i++) {
result.push(array[i]);
}
}
return result;
}; /**
* 对数据进行排序,该操作将直接操作传入的数组顺序
* @param items 数据数组
* @param key 数据对象的排序关键字
* @param sortType 排序类型,'asc'或者'desc'
*/
function sortItems(items, key, sortType) {
if (!items || items.length < 1 || !key || key.length < 1) {
return;
}
items.sort(
function compareFunction(param1, param2) {
var value1 = param1[key];
var value2 = param2[key];
if (typeof(value1) === 'number' && typeof(value2) === 'number') {
return sortType * (value1 - value2);
}
return sortType * ((value1 + '').localeCompare(value2 + ''));
}
);
}; function calculateTablePages(currentPage, totalPage) {
if (totalPage == 0) {
return [];
}
var end = Math.min(9, totalPage);
var pages = [currentPage];
while (pages.length < end) {
var pre = pages[0] - 1;
var next = pages[pages.length - 1] + 1;
if (pre >= 1) {
pages.unshift(pre);
}
if (next <= totalPage) {
pages.push(next);
}
}
return pages;
}; function sortTable(listTable) {
var sortParams = listTable.sorting();
var sortKey;
var sortType;
for (var key in sortParams) {
sortKey = key;
sortType = sortParams[key];
}
if (!sortType || sortType.length < 1) {
sortType = 'asc';
}
listTable.sortParams.key = sortKey;
listTable.sortParams.type = sortType;
if (sortKey && sortKey.length > 0) {
sortItems(listTable.shownItems, sortKey, sortType == 'asc' ? 1 : -1);
}
}; function init(options1, options2) {
var listTable = createListTable(options1, options2);
initSort(listTable);
initCheckboxes(listTable, options1.withCheckboxes);
initPagination(listTable);
return listTable;
}; return {
init: init,
};
}]);

  2. ListTableEX分页代码:

 <div class="ng-cloak dahuo-pagination" ng-if="params.pages.length > 0">
<div class="col-lg-6">
<div class="dataTables_info" role="status" aria-live="polite">
{{ params.count() * (params.page() - 1) + 1 }}~
{{ params.count() * (params.page() - 1) + params.data.length }}条,共{{
params.total() }}条记录
</div>
</div>
<div class="col-lg-6 pagination2" style="margin-bottom: 40px">
<div class="dataTables_paginate paging_simple_numbers">
<ul class="pagination ng-table-pagination">
<li><a href="" ng-click="params.prePage()">上一页</a></li>
<li ng-repeat="page in params.pages"
ng-class="{'active' : page == params.page()}">
<a href="" ng-click="params.page(page)">{{ page }}</a>
</li>
<li><a href="" ng-click="params.nextPage()">下一页</a></li>
</ul>
</div>
<div class="col-lg-3 pull-right">
{{params.testOK}}
<select class="select form-control" ng-model="params.editCount" ng-change="params.count(params.editCount)"
style="padding: 6px; margin-top: 2px;min-width: 80px;margin-right: 10px"
ng-options="size for size in params.settings().counts">
</select>
</div>
</div>
</div>

  由于PM的需求变更较大,使用方法可能各不相同。

  基本使用方法如下:

  1. HTML

    本表格是一个有checkBox的表格,具有编辑,新建,上线,下线等功能。

 <div>
<div class="row">
<div class="col-lg-12 portlets">
<div class="panel">
<div class="panel-content">
<div class="row dahuo-table-toolbar">
<div class="col-xs-6">
<button type="button" class="btn btn-default" ng-click="activityupload()" ng-disabled="checkedCount() < 1">发布</button>
<button type="button" class="btn btn-default" ng-click="activityoffline()" ng-disabled="checkedCount() < 1">下线</button>
{{ checkedCount() }}个被选中
</div>
<div class="col-xs-6">
<div class="pull-right">
<a href="#operation-activityadd">
<button type="button" class="btn btn-primary">+ 新建活动</button>
</a>
</div>
</div>
</div>
<div class="table-responsive">
<table ng-table="activityTable" class="table trans-table table-hover text-left dahuo-table"
template-pagination="layouts/pagination_v2.html" width="100%">
<thead>
<tr>
<th width="5%" class="dahuo-table-header-th text-center" header="'ng-table/headers/checkbox.html'">
<span class="list-checkbox">
<input type="checkbox" ng-model="activityTable.checkboxes.selectAll" ng-disabled="activityTable.items.length < 1" ng-click="activityTable.toggleCheckAll()"/>
<span></span>
</span>
</th>
<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 }}
<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>
</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="item in $data">
<td class="text-center" header="'ng-table/headers/checkbox.html'">
<span class="list-checkbox">
<input type="checkbox" ng-model="activityTable.checkboxes.items[item.id]"/>
<span></span>
</span>
</td>
<td><a href="#operation-activityedit/{{item.id}}">{{自定义绑值}}</a></td>
<td>{{自定义绑值}}</td>
<td>{{自定义绑值}}</td>
<td>{{自定义绑值}}</td>
<td>{{自定义绑值}}</td>
</tr>
<tr style="height: 100px" ng-if="$data.length < 1">
<td colspan="6" class="text-center">暂时没有数据</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
</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:提示信息依赖

 angular.module('newApp')
.controller('ActivityListCtrl', ['$scope', 'ds.activity', 'logger', 'ListTableEX',
function($scope, DS, logger, ListTableEX) {
$scope.activityTable = new ListTableEX.init(
{
tableName:'operation.activity.activity.list',
titles:[
{key: '', name: "活动ID", sortable: true, width: '10%'},
{key: '', name: "活动名称", sortable: true, width: '35%'},
{key: '', name: "开始时间", sortable: true, width: '20%'},
{key: '', name: "结束时间", sortable: true, width: '20%'},
{key: '', name: "状态", sortable: true, width: '10%'},
],
withCheckboxes: true,
page: 1,
count: 25,
sorting: {'id': 'desc'},
},
{
counts: [10, 25, 50, 100],
getData: function(apiParams) {
return DS.listActivity(apiParams);
},
getItemId: function(item) {
return item.id;
}
}
); $scope.checkedCount = function() {
return $scope.activityTable.checkedIds().length;
} $scope.activityupload = function() {
var checkedIds = $scope.activityTable.checkedIds();
if (checkedIds.length < 1) {
logger.warning('请选择操作对象');
return;
} DS.activityUpload(checkedIds)
.then(function() {
$scope.activityTable.forceReload();
logger.success('上线成功');
}, function(error) {
logger.error('上线失败');
});
}; $scope.activityoffline = function() {
var checkedIds = $scope.activityTable.checkedIds();
if (checkedIds.length < 1) {
logger.warning('请选择操作对象');
return;
} DS.activityOffline(checkedIds)
.then(function() {
$scope.activityTable.forceReload();
logger.success('下线成功');
}, function(error) {
logger.error('下线失败');
});
};
}
]);

二. 使用中注意的问题:

  1. 需要自己更改适应服务端返回数据的处理代码:

    对应代码段时listTableEX的“var resData = data.data.data”

  2. 使用中可能Table数据不是来自于API:

    需要使用如下代码:

 getData: function(apiParams) {
return {then: function(callback){
var data = {
data:{
data:{
items: [],
}
}
}
callback(data);
}}
}

    然后强行加载数据:

 $scope.ActivityTable.items = items;
$scope.ActivityTable.page(1);
$scope.ActivityTable.reload();

  3. 可能数据来自API,但是触发时机不是Table new出来时:

    New出来的代码更改如下:

 getData: function(apiParams) {
if (满足API发送条件){
var apiParams = {
(自定义)
};
return DS.activityList(apiParams);
}
return {then: function(callback){
var data = {
data:{
data:{
items: [],
}
}
}
callback(data);
}}
}

    然后发送API获取数据:

 $scope.ActivityTable.page(1)
$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);
}
}
}])

ngTbale假分页实现排序、搜索、导出CSV等功能的更多相关文章

  1. SpringBoot JPA实现增删改查、分页、排序、事务操作等功能

    今天给大家介绍一下SpringBoot中JPA的一些常用操作,例如:增删改查.分页.排序.事务操作等功能.下面先来介绍一下JPA中一些常用的查询操作: //And --- 等价于 SQL 中的 and ...

  2. ngTbale真分页实现排序、搜索等功能

    一. 真分页表格基础 1. 需求:分页,排序,搜索都是需要发API到服务端. 2. JS实现代码: getStorage是localStorage一个工具方法,可以自己写这个方法. API参数如下: ...

  3. MvcPager无刷新分页,包含搜索和跳转功能

    1.MVC无刷新分页和搜索(第一版)  http://pan.baidu.com/s/1eRQ7Ml8  密码:uqf7 出现的问题: 1)程序不走判断条件一直为false, 错误原因:1)可能没有引 ...

  4. Datatable+Springmvc+mybatis(分页+排序+搜索)_Jquery

    一.简介 通过Jqury的Datatable插件,构造数据列表,并且增加或者隐藏相应的列,已达到数据显示要求.同时, jQuery Datatable 强大的功能支持:排序,分页,搜索等. 二.前台分 ...

  5. 使用插件bootstrap-table实现表格记录的查询、分页、排序等处理

    在业务系统开发中,对表格记录的查询.分页.排序等处理是非常常见的,在Web开发中,可以采用很多功能强大的插件来满足要求,且能极大的提高开发效率,本随笔介绍这个bootstrap-table是一款非常有 ...

  6. 基于Metronic的Bootstrap开发框架经验总结(16)-- 使用插件bootstrap-table实现表格记录的查询、分页、排序等处理

    在业务系统开发中,对表格记录的查询.分页.排序等处理是非常常见的,在Web开发中,可以采用很多功能强大的插件来满足要求,且能极大的提高开发效率,本随笔介绍这个bootstrap-table是一款非常有 ...

  7. Magcodes.WeiChat——通过CsvFileResult以及DataAnnotations实现导出CSV文件

    我们先来看看效果图: 从上图中可以看出,导出的文件中列名与表格名称保持一致,并且忽略了某些字段. 相关代码实现 我们来看相关代码: 页面代码: @using (Html.BeginForm(" ...

  8. PHP 读取/导出 CSV文件

    工作中经常会有遇到导入/导出的需求,下面是常用的方法.读取CSV文件,可以分页读取,设置读取行数,起始行数即可.导出CSV文件,用两种方法进行实现. /** * 读取CSV文件 * @param st ...

  9. Web 端 js 导出csv文件(使用a标签)

    前言 导出文件,使用最多的方式还是服务器端来处理.比如jsp 中使用response 的方式. 但是,有时候可能就想使用web 前端是否也可以把页面上的内容导出来呢? 比如说,导出页面的一个表格. 这 ...

随机推荐

  1. phonegap环境搭建

    最近在开发app, html5+php 采用phonegap进行打包 前端框架采用jquery mobile 这里phonegap创建安卓项目 3种方式 1.phonegap 2.cordova 3. ...

  2. 剑指offer—第三章高质量的代码(按顺序打印从1到n位十进制数)

    题目:输入一个数字n,按照顺序打印出1到最大n位十进制数,比如输入3,则打印出1,2,3直到最大的3位数999为止. 本题陷阱:没有考虑到大数的问题. 本题解题思路:将要打印的数字,看成字符串,不足位 ...

  3. WCF 扩展一:格式化Web服务请求XML

    扩展原因 有一WebService,在工程中添加Web Service引用后调用不成功,但是用SoapUI测试正常 分析 用tctTrace跟踪报文后发现wcf生成的报文命名空间与SoapUI的不一样 ...

  4. JS判断手机访问页面,根据手机访问或者PC访问跳转

    当用户访问你网站时,如果是PC端访问,则不作处理,如果是手机或者平板访问,就跳转到自己定义的手机页面去,这个在做webapp的时候经常用到,把代码分享给大家,希望对大家有所帮助. 首先,你要在页面中引 ...

  5. .net core 中的序列化和反序列化

    学习博客:http://www.voidcn.com/blog/dujingjing1230/article/p-1204454.html

  6. mysql 用户名密码登陆不上

    问题1:刚安装完mysql,设置了用户名密码root,登陆OK的,后来再连怎么也连不上了 操作步骤: 输入:mysql -uroot -proot 提示:ERROR 1045 (28000): Acc ...

  7. Pitcher Rotation

    题意: n个人m个对手给出每个人能战胜每个敌人的概率,现在有g个比赛,每个人赛完后要休息4天(可重复用),求能获得胜利的最大期望个数. 分析: 因为只有每个人5天就能用一次,所以对于每个人来说,只有得 ...

  8. 【转】linux mknod命令解析

    转自:http://www.cnblogs.com/cobbliu/archive/2011/07/05/2389014.html 个人觉得linux的软件设计思想异常强大,比如把所有的设备都当做文件 ...

  9. matlab 调用VLfeat库开篇问题

    准备工作见此网站 :http://cnyubin.com/?p=85 保存关闭startup.m文件后 重新打开matlab后 在命令行输入path或者vl_version无法检测到库已安装到matl ...

  10. echart图表控件配置入门(二)常用图表数据动态绑定

    上一节 <echart图表控件配置入门(一)>介绍了echarts图表控件的入门配置,使开发人员可以快速搭建出一个静态的图表.但是在实际开发过程这还是不够的,不可能所有的图表控件都是静态数 ...