Skip to content

Instantly share code, notes, and snippets.

@dalcib
Created May 7, 2012 20:18
Show Gist options
  • Save dalcib/2630138 to your computer and use it in GitHub Desktop.
Save dalcib/2630138 to your computer and use it in GitHub Desktop.
Angular Grid
//////////////////////////////////////
/// Grid Directive to Angular 1.0.0rc7
//////////////////////////////////////
// To use:
// <script>
// var app = angular.module('myapp', ['ngGrid']);
// var Ctrl = function($scope) { $scope.todo = [...] }
// </script>
// <div ng-app="myapp">
// <div ng-controller="Ctrl">
// <table ng-grid >
// <tr ng-repeat="todo in todos">
// <td >{{todo.name}}</td>
// <td >{{todo.estimate | number}}</td>
// <td width="50">{{todo.done }}</td>
// <td title="Create At">{{todo.created_at | date}}</td>
// </tr>
// </table>
// </div>
// </div>​
//// ngGrid Module
(function(angular) {
angular.module('ngSkip', []).filter('skip', function() {
return function(array, skipAt) {
return array.slice(skipAt);
};
});
angular.module('ngGrid', ['ngSkip']).directive('ngGrid', function() {
var direc = {
compile: function compile(tElement, tAttrs) {
//HEADER
var header = angular.element('<thead><tr></tr></thead>'),
headerRow = header.children('tr'),
count = 0,
tr = tElement.children('tbody').children('tr'),
expa = tr.attr('ng-repeat');
tr.attr('ng-repeat', expa + '| skip:ngGridSortPagination.skipAt | orderBy:ngGridSortPagination.predicate:ngGridSortPagination.reverse | limitTo:ngGridSortPagination.limit');
angular.forEach(tr.children('td'), function(elm) {
var column = angular.element(elm),
expc = column.html(),
exp = expc.replace(/[{}\s]/g, ""),
name = exp.split(/\.(.+)?/)[1].split(/\|/)[0],
filter = exp.split(/\.(.+)?/)[1].split(/\|/)[1],
filterAttrib = (!filter) ? "" : ' filter="' + filter + '"',
title = column.attr('title') || name,
width = column.attr('width') || 100;
headerRow.append('<th class="ui-state-default" name="' + name + '"' + filterAttrib + '" style="cursor: pointer;width: ' + width + 'px; font-weight:bold"><span style="display:inline-block"> </span>' + title + '</th>');
column.addClass('ui-widget-content');
column.attr('title', null);
count += 1;
});
tElement.addClass('ui-widget');
tElement.prepend(header);
//PAGINATION
var footer = '<tfoot class="ui-state-default"><tr><td align="center" colspan="' + count + '">' + '<span style="cursor: pointer; display:inline-block; margin-left:10px;; vertical-align:middle" class="ui-icon ui-icon-seek-first ui-state-disabled"></span>' + '<span style="cursor: pointer; display:inline-block; margin-left:10px;; vertical-align:middle" class="ui-icon ui-icon-seek-prev ui-state-disabled"></span>' + '<span style="display:inline-block; margin-left:10px;" class="ui-state-disabled">|</span>' + '<span >Page<input id="ui-pg-input" ng:model="ngGridSortPagination.page" class="ui-pg-input" type="text" size="2" maxlength="4" value="0"> of <span >{{ngGridSortPagination.lastPage}}</span></span>' + '<span style="display:inline-block" class="ui-state-disabled" style="margin-left:5px;">|</span>' + '<span style="cursor: pointer; display:inline-block; vertical-align:middle; margin-left:10px;" class="ui-icon ui-icon-seek-next"></span>' + '<span style="cursor: pointer; display:inline-block; vertical-align:middle; margin-left:10px;" class="ui-icon ui-icon-seek-end"></span>' + '<span style="display:inline-block; margin-left:10px;" ><select ng:model="ngGridSortPagination.limit" class="ui-pg-selbox">' + '<option value="10" selected="selected">10</option><option value="20">20</option><option value="30">30</option></select></span>' + '</td></tr></tfoot>';
tElement.append(footer);
tElement.find('.ui-state-default, .ui-widget-content').css('padding', ' 0.2em 0.4em');
tElement.find('.ui-state-default, .ui-widget-content').css('font-size', '70%');
return function(scope, linkElement) {
scope.ngGridSortPagination = {};
var grid = scope.ngGridSortPagination;
//PAGINATION
var pager = linkElement.children('tfoot').children('tr').children('td'),
rhs = expa.match(/^\s*(.+)\s+in\s+(.*)\s*$/)[2],
collection = scope.$eval(rhs),
count = collection.length;
scope.$watch(rhs, function() {
count = scope.$eval(rhs).length;
grid.lastPage = Math.round(count / grid.limit) + 1;
});
grid.limit = 10;
grid.lastPage = Math.round(count / grid.limit) + 1;
grid.page = 1;
grid.skipAt = ((grid.page - 1) * grid.limit);
pager.children('.ui-icon-seek-first').bind('click', function() {
grid.page = 1;
grid.skipAt = 1;
angular.element(this).addClass('ui-state-disabled');
pager.children('.ui-icon-seek-prev').addClass('ui-state-disabled');
pager.children('.ui-icon-seek-next, .ui-icon-seek-end').removeClass('ui-state-disabled');
grid.skipAt = ((grid.page - 1) * grid.limit);
scope.$digest();
//console.log('fisrt');
});
pager.children('.ui-icon-seek-prev').bind('click', function() {
pager.children('.ui-icon-seek-next, .ui-icon-seek-end').removeClass('ui-state-disabled');
if (grid.page > 1) {
grid.page -= 1;
grid.skipAt = ((grid.page - 1) * grid.limit);
scope.$digest();
}
if (grid.page === 1) {
angular.element(this).addClass('ui-state-disabled');
pager.children('.ui-icon-seek-first').addClass('ui-state-disabled');
}
//console.log('prev')
});
pager.children('.ui-icon-seek-next').bind('click', function() {
pager.children('.ui-icon-seek-first, .ui-icon-seek-prev').removeClass('ui-state-disabled');
if (grid.page < grid.lastPage) {
grid.page += 1;
grid.skipAt = ((grid.page - 1) * grid.limit);
scope.$digest();
}
if (grid.page === grid.lastPage) {
angular.element(this).addClass('ui-state-disabled');
pager.children('.ui-icon-seek-end').addClass('ui-state-disabled');
}
//console.log('next')
});
pager.children('.ui-icon-seek-end').bind('click', function() {
grid.page = grid.lastPage;
pager.children('.ui-icon-seek-prev, .ui-icon-seek-first').removeClass('ui-state-disabled');
angular.element(this).addClass('ui-state-disabled');
pager.children('.ui-icon-seek-next').addClass('ui-state-disabled');
grid.skipAt = ((grid.page - 1) * grid.limit);
scope.$digest();
//console.log('end')
});
pager.children('span').children('.ui-pg-selbox').bind('change', function(ev) {
grid.lastPage = Math.round(count / ev.target.value) + 1;
grid.page = 1;
grid.skipAt = ((grid.page - 1) * ev.target.value);
scope.$digest();
});
//ORDER
var listenerOrder = function(ev) {
var sort = angular.element(this).children('span');
grid.predicate = angular.element(this).attr('name');
grid.reverse = false;
if (!sort.hasClass('ui-icon-triangle-1-n') && !sort.hasClass('ui-icon-triangle-1-s')) {
headerRow.children('th').children('span').removeClass('ui-icon ui-icon-triangle-1-n ui-icon-triangle-1-s');
sort.addClass('ui-icon ui-icon-triangle-1-n');
grid.reverse = false;
} else {
if (sort.hasClass('ui-icon-triangle-1-n')) {
grid.reverse = true;
} else {
grid.reverse = false;
}
sort.toggleClass('ui-icon-triangle-1-n');
sort.toggleClass('ui-icon-triangle-1-s');
}
console.log('orderBy :' + grid.predicate + ' order: ' + grid.reverse);
scope.$digest();
};
angular.forEach(linkElement.children('thead').children('tr').children('th'), function(elm) {
elm.addEventListener('click', listenerOrder);
});
}
}
};
return direc;
});
})(window.angular);
@mbemowski
Copy link

You should change the order of the filters: first should be orderBy, then skip and finally limitTo, because otherwise sorting doesn't work properly. You can see the wrong behaviour in your fiddle here: http://jsfiddle.net/dalcib/J3fjc/ . Just change the sort order to name and navigate through pages: value "asdfasdf" keeps showing through pages 1-4.
Here is my fork of your gist with mentioned problem fixed: https://gist.github.com/3737358

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment