ng-gridify
Version:
Generic grid for angularJS with paging, sorting, the ability to export CSV and configurable content.
171 lines (139 loc) • 8.51 kB
JavaScript
(function(angular, window){
'use strict';
angular.module('ngGridify', [])
.filter('offset', function () {
return function (input, start) {
if (!input || !input.length) { return; }
start = +start; //parse to int
return input.slice(start);
}
})
.component('ngGridify', {
bindings: {
config: '='
},
controllerAs: 'ctrl',
controller: function ($scope) {
var vm = this;
var columnsToConvertToFloat = [];
var columnsToConvertToDate = [];
// Properties available on the view
vm.reverse = false;
vm.pages = 0;
vm.currentpage = 0;
vm.rowcount = 0;
// Functions that are available on the view
vm.SortColumn = sortColumn;
vm.ChangePage = changePage;
vm.GetNumber = getNumber;
vm.ExportCSV = exportCSV;
// Let's watch the collection of columns and determine which needs to be converted to a number for the sorting of
// number based data
$scope.$watchCollection('ctrl.config.columns', function (columns) {
columns.map(function (column) {
if (column.type === 'number') {
columnsToConvertToFloat.push(column.column)
}
else if (column.type == 'date') {
columnsToConvertToDate.push(column.column)
}
});
})
// We setup a watch on the collection to bind the paging, convert any number based columns to floats
// to allow correct sorting and then finally set the rowcount.
$scope.$watchCollection('ctrl.config.data', function (data) {
// Convert each column that is required on numbers and dates to allow sorting
data.map(function (itemToConvert){
columnsToConvertToFloat.map(function (column) {
itemToConvert[column] = parseFloat(itemToConvert[column]);
});
columnsToConvertToDate.map(function (column) {
itemToConvert[column] = new Date(itemToConvert[column]);
});
});
if (data) {
vm.pages = calculatePages(data.length, vm.config.itemsPerPage);
vm.rowcount = data.length;
}
});
// Function that takes the columns collection, creates a CSV representation of the data, and pushes it to the browser
function exportCSV () {
// The content type needs to be set to allow it to be opened in numbers/excel/openofice
var csvContent = "data:text/csv;charset=utf-8,";
// Setup the headers
csvContent += vm.config.columns.map(function (column) {
return column.display ? column.display : column.column;
}).join(",") + '\n';
// I'm sure there is a more efficient way to do this, but we basically loop over the items, match the requested columns
// push the result into a comma delimited string, with a new line on each row.
vm.config.data.map(function (item){
csvContent += vm.config.columns.map(function (column) {
return item[column.column];
}).join(",") + '\n';
})
// Open the content in a new window
var encodedUri = encodeURI(csvContent);
window.open(encodedUri);
}
// Calculates the number of pages as we do this function more than once
function calculatePages (numberOfItems, itemsPerPage) {
return Math.ceil(numberOfItems / itemsPerPage);
}
// Function that sorts the grid by column name, opposite to the current sort
function sortColumn (column) {
vm.config.order = column;
vm.reverse = !vm.reverse;
}
// Function that allows the user to change page
function changePage (page) {
vm.currentpage = page;
}
// Creates an array of a calculated size so we can iterate over it later for the pages
function getNumber (num) {
try {
return new Array(num);
}
catch(err) {
console.error('You need to provide the itemsPerPage config property to ng-gridify')
}
}
},
template: [
'<div class="nggridify-rowcount">{{ctrl.rowcount}} records found.</div>',
'<div class="nggridify-export"><button ng-show="ctrl.config.export" ng-click="ctrl.ExportCSV()">Export to CSV</button></div>',
'<table class="{{ctrl.config.class}}" ng-show="ctrl.rowcount > 0">',
'<tr>',
// HEADER: We want to loop over the column names one by one, and sow the display names
'<th ng-repeat="column in ctrl.config.columns" width="{{column.width}}">',
'<a href="#null" ng-click="ctrl.SortColumn(column.column)">{{column.display ? column.display : column.column}}</a> ',
' <span ng-show="ctrl.config.order == column.column && ctrl.reverse" class="glyphicon glyphicon-triangle-bottom"></span>',
' <span ng-show="ctrl.config.order == column.column && !ctrl.reverse" class="glyphicon glyphicon-triangle-top"></span>',
'</th>',
// We show and hide this column if a click event has been provided for the item row
'<th ng-show="ctrl.config.itemClick"></th>',
'</tr>',
// ROW: Now we repeat over the items in the JSON, and dynamically create the columns
'<tr ng-repeat="item in ctrl.config.data| orderBy: ctrl.config.order : ctrl.reverse | offset:(ctrl.currentpage * ctrl.config.itemsPerPage) | limitTo: ctrl.config.itemsPerPage">',
'<td ng-repeat="column in ctrl.config.columns">',
// COLUMN: Handling of 'text' and 'number' types shown
'<span ng-show="column.type==\'text\' || column.type==\'number\' || !column.type">{{item[column.column]}}</span>',
// COLUMN: Handling of 'date' types shown
'<span ng-show="column.type==\'date\' && item[column.column]">{{ item[column.column] | date:(!column.format) ? "dd MMMM yyyy" : column.format}}</span>',
// COLUMN: Handling of 'email' types shown
'<a href="mailto:{{item[column.column]}}" ng-show="column.type==\'email\'">{{item[column.column]}}</a></span>',
// COLUMN: Handling of 'link' types shown
'<a href="{{item[column.column]}}" ng-show="column.type==\'link\'" target="_blank">{{item[column.column]}}</a></span>',
'</td>',
// COLUMN: Show the item click column if a function has been defined
'<td ng-show="ctrl.config.itemClick" ><button class="btn btn-info"type="button" ng-click="ctrl.config.itemClick(item)">{{ctrl.config.itemClickText}}</button></td>',
'</tr>',
'</table>',
// PAGING
'<nav ng-show="ctrl.config.paging">',
'<ul class="pagination">',
'<li ng-repeat="item in ctrl.GetNumber(ctrl.pages) track by $index" class="page-item"><a class="page-link" href="#null" ng-click="ctrl.ChangePage($index)">{{$index+1}}</a></li>',
'</ul>',
'</nav>'
].join('')
});
})(angular, window);