Skip to content

Instantly share code, notes, and snippets.

@mbemowski
Forked from dalcib/ngGrid.js
Created September 17, 2012 13:49
Show Gist options
  • Save mbemowski/3737358 to your computer and use it in GitHub Desktop.
Save mbemowski/3737358 to your computer and use it in GitHub Desktop.
Angular Grid
/**
* Grid directive for angularJS, based on dalcib's Angular Grid https://gist.github.com/2630138
* It's events are more angular-style and it operates on special object NavigationVector. It allows
* to select a row and to add custom rows at the bottom (for example to notify, that there are no rows found)
* When creating NavigationVector object you should pass the scope in which it will be declared in order
* to properly bind $watch expressions. Thanks to this you will be able to update index, selected or even
* items properties and other properties will be updated to match the change you made.
*
* Example:
<table ng-grid="" width="100%">
<tr ng-repeat="emp in empNavigation.items">
<td title="Surname">{{emp.surname}}</td>
<td title="Name">{{emp.name}}</td>
<td title="Second name">{{emp.secondName}}</td>
</tr>
<tr>
<td ng-show="empNavigation.items.length==0" colspan="3" style="text-align: center;">No employees found.</td>
</tr>
</table>
*/
(function(angular) {
angular.module('ui.filters').filter('skip', function() {
return function(array, skipAt) {
return array.slice(skipAt);
};
});
angular.module('ui.directives').directive('ngGrid', function($filter) {
return {
compile: function compile(tElement, tAttrs) {
//HEADER
var header = angular.element('<thead><tr></tr></thead>'),
headerRow = header.children('tr'),
tr = $(tElement.children('tbody').children('tr')[0]),
expa = tr.attr('ng-repeat');
navigationVectorName = expa.match(/^\s*(.+)\s+in\s+(.*)\.(.*)\s*$/)[2];
tr.attr('ng-repeat', expa + '| skip:ngGridSortPagination.skipAt | limitTo:ngGridSortPagination.limit');
tr.attr('ng-class',"$gridSelectedClass(emp)");
tr.attr('ng-click',"$gridSelect(emp)");
var count = 0;
angular.forEach(tr.children('td'), function(elm) {
var column = angular.element(elm),
expc = column.html(),
exp = expc.replace(/[{}\s]/g, ""),
name = exp.split(/\.(.+)?/)[1] ? exp.split(/\.(.+)?/)[1].split(/\|/)[0] : exp,
filter = exp.split(/\.(.+)?/)[1] ? exp.split(/\.(.+)?/)[1].split(/\|/)[1] : exp,
filterAttrib = (!filter) ? "" : ' filter="' + filter + '"',
title = column.attr('title') || name,
width = column.attr('width') || 100;
headerRow.append('<th 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++;
});
// 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: 0 5px; vertical-align:middle" class="ui-icon ui-icon-seek-first" ng-class="$gridPrevClass()" ng-click="ngGridSortPagination.page=1"></span><span style="cursor: pointer; display:inline-block; margin: 0 5px; vertical-align:middle" class="ui-icon ui-icon-seek-prev" ng-class="$gridPrevClass()" ng-click="ngGridSortPagination.page=ngGridSortPagination.page-1"></span><span style="display:inline-block; margin:0 5px;" class="ui-state-disabled">|</span><span>Page <input style="margin-bottom: 5px" 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; margin:0 5px;" class="ui-state-disabled">|</span><span style="cursor: pointer; display:inline-block; vertical-align:middle; margin: 0 5px;" class="ui-icon ui-icon-seek-next" ng-class="$gridNextClass()" ng-click="ngGridSortPagination.page=ngGridSortPagination.page+1"></span><span style="cursor: pointer; display:inline-block; vertical-align:middle; margin: 0 5px;" class="ui-icon ui-icon-seek-end" ng-class="$gridNextClass()" ng-click="ngGridSortPagination.page=ngGridSortPagination.lastPage"></span><span style="display:inline-block; margin-left:10px;" ><select ng-model="ngGridSortPagination.limit" style="position:relative;top:5px"><option value="5">5</option><option value="10">10</option><option value="20">20</option><option value="30">30</option></select></span><span style="margin:0 5px">Znaleziono {{'+navigationVectorName+'.items.length}} obiektów</span></td></tr></tfoot>';
tElement.append(footer);
// tElement.find('.ui-state-default, .ui-widget-content').css('font-size', '0.91em');
return {
pre: function($scope, linkElement) {
$scope.ngGridSortPagination = {};
var grid = $scope.ngGridSortPagination;
grid.limit = 5;
grid.page = 1;
// grid.lastPage = Math.ceil(count / grid.limit);
// grid.skipAt = ((grid.page - 1) * grid.limit);
//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');
}
$scope[navigationVectorName].items = $filter('orderBy')($scope[navigationVectorName].items,grid.predicate,grid.reverse);
// 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);
});
},
post: function postLink($scope, element, attrs, controller) {
//colResizable - by Alvaro Prieto Lauroba - MIT & GPL
//http://quocity.com/colresizable/
(function(a){function h(b){var c=a(this).data(q),d=m[c.t],e=d.g[c.i];e.ox=b.pageX;e.l=e[I]()[H];i[D](E+q,f)[D](F+q,g);P[z](x+"*{cursor:"+d.opt.dragCursor+K+J);e[B](d.opt.draggingClass);l=e;if(d.c[c.i].l)for(b=0;b<d.ln;b++)c=d.c[b],c.l=j,c.w=c[u]();return j}function g(b){i.unbind(E+q).unbind(F+q);a("head :last-child").remove();if(l){l[A](l.t.opt.draggingClass);var f=l.t,g=f.opt.onResize;l.x&&(e(f,l.i,1),d(f),g&&(b[G]=f[0],g(b)));f.p&&O&&c(f);l=k}}function f(a){if(l){var b=l.t,c=a.pageX-l.ox+l.l,f=b.opt.minWidth,g=l.i,h=1.5*b.cs+f+b.b,i=g==b.ln-1?b.w-h:b.g[g+1][I]()[H]-b.cs-f,f=g?b.g[g-1][I]()[H]+b.cs+f:h,c=s.max(f,s.min(i,c));l.x=c;l.css(H,c+p);if(b.opt.liveDrag&&(e(b,g),d(b),c=b.opt.onDrag))a[G]=b[0],c(a)}return j}function e(a,b,c){var d=l.x-l.l,e=a.c[b],f=a.c[b+1],g=e.w+d,d=f.w-d;e[u](g+p);f[u](d+p);a.cg.eq(b)[u](g+p);a.cg.eq(b+1)[u](d+p);if(c)e.w=g,f.w=d}function d(a){a.gc[u](a.w);for(var b=0;b<a.ln;b++){var c=a.c[b];a.g[b].css({left:c.offset().left-a.offset()[H]+c.outerWidth()+a.cs/2+p,height:a.opt.headerOnly?a.c[0].outerHeight():a.outerHeight()})}}function c(a,b){var c,d=0,e=0,f=[];if(b)if(a.cg[C](u),a.opt.flush)O[a.id]="";else{for(c=O[a.id].split(";");e<a.ln;e++)f[y](100*c[e]/c[a.ln]+"%"),b.eq(e).css(u,f[e]);for(e=0;e<a.ln;e++)a.cg.eq(e).css(u,f[e])}else{O[a.id]="";for(e in a.c)c=a.c[e][u](),O[a.id]+=c+";",d+=c;O[a.id]+=d}}function b(b){var e=">thead>tr>",f='"></div>',g=">tbody>tr:first>",i=">tr:first>",j="td",k="th",l=b.find(e+k+","+e+j);l.length||(l=b.find(g+k+","+i+k+","+g+j+","+i+j));b.cg=b.find("col");b.ln=l.length;b.p&&O&&O[b.id]&&c(b,l);l.each(function(c){var d=a(this),e=a(b.gc[z](w+"CRG"+f)[0].lastChild);e.t=b;e.i=c;e.c=d;d.w=d[u]();b.g[y](e);b.c[y](d);d[u](d.w)[C](u);if(c<b.ln-1)e.mousedown(h)[z](b.opt.gripInnerHtml)[z](w+q+'" style="cursor:'+b.opt.hoverCursor+f);else e[B]("CRL")[A]("CRG");e.data(q,{i:c,t:b[v](o)})});b.cg[C](u);d(b);b.find("td, th").not(l).not(N+"th, table td").each(function(){a(this)[C](u)})}var i=a(document),j=!1,k=null,l=k,m=[],n=0,o="id",p="px",q="CRZ",r=parseInt,s=Math,t=a.browser.msie,u="width",v="attr",w='<div class="',x="<style type='text/css'>",y="push",z="append",A="removeClass",B="addClass",C="removeAttr",D="bind",E="mousemove.",F="mouseup.",G="currentTarget",H="left",I="position",J="}</style>",K="!important;",L=":0px"+K,M="resize",N="table",O,P=a("head")[z](x+".CRZ{table-layout:fixed;}.CRZ td,.CRZ th{padding-"+H+L+"padding-right"+L+"overflow:hidden}.CRC{height:0px;"+I+":relative;}.CRG{margin-left:-5px;"+I+":absolute;z-index:5;}.CRG .CRZ{"+I+":absolute;background-color:red;filter:alpha(opacity=1);opacity:0;width:10px;height:100%;top:0px}.CRL{"+I+":absolute;width:1px}.CRD{ border-left:1px dotted black"+J);try{O=sessionStorage}catch(Q){}a(window)[D](M+"."+q,function(){for(a in m){var a=m[a],b,c=0;a[A](q);if(a.w!=a[u]()){a.w=a[u]();for(b=0;b<a.ln;b++)c+=a.c[b].w;for(b=0;b<a.ln;b++)a.c[b].css(u,s.round(1e3*a.c[b].w/c)/10+"%").l=1}d(a[B](q))}});a.fn.extend({colResizable:function(c){c=a.extend({draggingClass:"CRD",gripInnerHtml:"",liveDrag:j,minWidth:15,headerOnly:j,hoverCursor:"e-"+M,dragCursor:"e-"+M,postbackSafe:j,flush:j,marginLeft:k,marginRight:k,disable:j,onDrag:k,onResize:k},c);return this.each(function(){var d=c,e=a(this);if(d.disable){if(e=e[v](o),(d=m[e])&&d.is(N))d[A](q).gc.remove(),delete m[e]}else{var f=e.id=e[v](o)||q+n++;e.p=d.postbackSafe;if(e.is(N)&&!m[f])e[B](q)[v](o,f).before(w+'CRC"/>'),e.opt=d,e.g=[],e.c=[],e.w=e[u](),e.gc=e.prev(),d.marginLeft&&e.gc.css("marginLeft",d.marginLeft),d.marginRight&&e.gc.css("marginRight",d.marginRight),e.cs=r(t?this.cellSpacing||this.currentStyle.borderSpacing:e.css("border-spacing"))||2,e.b=r(t?this.border||this.currentStyle.borderLeftWidth:e.css("border-"+H+"-"+u))||1,m[f]=e,b(e)}})}})})(jQuery)
$(document).ready(function(){element.colResizable({liveDrag:true,minWidth:100});});
$scope.$watch(navigationVectorName+'.items',function(newVal, oldVal){
// console.log(attrs.ngGrid + ".items changed");
// console.log($scope[attrs.ngGrid].items);
if(newVal == oldVal)
return;
$scope.ngGridSortPagination.lastPage = Math.ceil(newVal.length / $scope.ngGridSortPagination.limit);
});
$scope.$watch(navigationVectorName+'.items.length',function(newVal, oldVal){
if(newVal == oldVal)
return;
// console.log("length changed to " + newVal)
$scope.ngGridSortPagination.lastPage = Math.ceil(newVal / $scope.ngGridSortPagination.limit);
});
$scope.$watch(navigationVectorName+'.index',function(newVal, oldVal){
if(newVal == oldVal)
return;
// console.log("Page change by index " + newVal + " to " + Math.ceil((newVal+1) / $scope.ngGridSortPagination.limit))
$scope.ngGridSortPagination.page = Math.ceil((newVal+1) / $scope.ngGridSortPagination.limit);
});
$scope.$watch('ngGridSortPagination.page',function(newVal, oldVal){
// console.log("page changed from " + oldVal + " to " + newVal)
if(newVal == oldVal || newVal==="")
return;
if(newVal <= $scope.ngGridSortPagination.lastPage && (newVal >=1))
$scope.ngGridSortPagination.skipAt = (($scope.ngGridSortPagination.page - 1) * $scope.ngGridSortPagination.limit);
else
if($scope.ngGridSortPagination.lastPage == 0)
$scope.ngGridSortPagination.page = 0;
else
$scope.ngGridSortPagination.page = oldVal==="" ? 1 : oldVal;
});
$scope.$watch('ngGridSortPagination.limit',function(newVal, oldVal){
// console.log("limit changed to " + newVal);
$scope.ngGridSortPagination.lastPage = Math.ceil($scope[navigationVectorName].items.length / newVal);
$scope.ngGridSortPagination.page = 1;
});
$scope.$gridSelect = function(obj){
$scope[navigationVectorName].selected = obj;
};
$scope.$gridSelectedClass = function(obj){
if(obj == $scope[navigationVectorName].selected)
return "ng-grid-selected";
else
return "";
};
$scope.$gridPrevClass = function(){
if($scope.ngGridSortPagination.page <= 1)
return "ui-state-disabled";
else
return "";
};
$scope.$gridNextClass = function(){
if($scope.ngGridSortPagination.page >= $scope.ngGridSortPagination.lastPage || $scope.ngGridSortPagination.page == "")
return "ui-state-disabled";
else
return "";
};
}
}
}
};
});
})(window.angular);
/**
* Object used by ngGrid
* items - array of items displayed in ngGrid
* selected - currently selected object
* index - index in the array of the currently selected object
*
* @param $scope - scope in which the NavigationVector is defined
*/
function NavigationVector($scope){
var self = this;
this.$scope = $scope;
this.items = [];
this.selected = null;
this.index = null;
$scope.$watch(function(){return self.selected;},function(newVal, oldVal){
if(newVal == oldVal)
return;
self.index = self.items.indexOf(newVal);
if(self.index == -1){
self.index = null;
}
});
$scope.$watch(function(){return self.index;},function(newVal, oldVal){
if(newVal == oldVal)
return;
if(self.items)
self.selected = self.items[newVal];
else
self.selected = null;
});
$scope.$watch(function(){return self.items;},function(newVal, oldVal){
if(newVal == oldVal)
return;
if(self.items){
self.index = self.items.indexOf(self.selected);
if(self.index == -1)
if(self.items.length == 0){
self.index = null;
}else{
self.index = 0;
self.selected = self.items[0];
}
}else{
self.items = [];
self.index = null;
self.selected = null;
}
});
$scope.$watch(function(){return self.items.length;},function(newVal, oldVal){
if(newVal == oldVal)
return;
self.index = self.items.indexOf(self.selected);
if(self.index == -1){
if(self.items.length == 0){
self.index = null;
}else{
self.index = 0;
self.selected = self.items[0];
}
}
});
};
tr.ng-grid-selected td{
background: #BCBFE6;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment