From d9bbb244fc74d00a4353d4cd7e25e783f408aada Mon Sep 17 00:00:00 2001 From: Brian Hann Date: Thu, 18 Apr 2013 13:51:20 -0500 Subject: [PATCH 01/17] Added descriptions to karma file conf Conflicts: test/karma.conf.js From 83ddcfb58b06b90030d92bed266c96001620c43d Mon Sep 17 00:00:00 2001 From: Brian Hann Date: Fri, 19 Apr 2013 15:29:01 -0500 Subject: [PATCH 02/17] Fetch templates with $http, run init() after getTemplate() now return a promise. It is immediately resolved if the template is local. If it is remote, the promise is resolved after the template is fetched and put into the cache. Also the $http request is cached in $templateCache as well, so the same template should only be fetched once no matter now many grids use it. New function self.initTemplates(), calls getTemplate() for each of the template types. Returns a promise via $q.all() that bundles all the getTemplate() promises. self.init() now returns a promise and defers execution until after initTemplates() completes. --- src/classes/grid.js | 124 +++++++++++++++++++++++++++++--------------- 1 file changed, 81 insertions(+), 43 deletions(-) diff --git a/src/classes/grid.js b/src/classes/grid.js index 216bf7cb89..bcbe47094a 100644 --- a/src/classes/grid.js +++ b/src/classes/grid.js @@ -1,7 +1,7 @@ /// /// /// -var ngGrid = function ($scope, options, sortService, domUtilityService, $filter, $templateCache, $utils, $timeout, $parse) { +var ngGrid = function ($scope, options, sortService, domUtilityService, $filter, $templateCache, $utils, $timeout, $parse, $http, $q) { var defaults = { //Define an aggregate template to customize the rows when grouped. See github wiki for more details. aggregateTemplate: undefined, @@ -215,32 +215,59 @@ var ngGrid = function ($scope, options, sortService, domUtilityService, $filter, self.data = []; self.lateBindColumns = false; self.filteredRows = []; + + self.initTemplates = function() { + var templates = ['rowTemplate', 'aggregateTemplate', 'headerRowTemplate', 'checkboxCellTemplate', 'checkboxHeaderTemplate', 'menuTemplate', 'footerTemplate']; + + var promises = []; + templates.forEach(function(template) { + promises.push( self.getTemplate(template) ); + }); + + // getTemplate('rowTemplate'); + // getTemplate('aggregateTemplate'); + // getTemplate('headerRowTemplate'); + // getTemplate('checkboxCellTemplate'); + // getTemplate('checkboxHeaderTemplate'); + // getTemplate('menuTemplate'); + // getTemplate('footerTemplate'); + + return $q.all(promises); + }; //Templates // test templates for urls and get the tempaltes via synchronous ajax calls - var getTemplate = function (key) { + self.getTemplate = function (key) { var t = self.config[key]; var uKey = self.gridId + key + ".html"; + var p = $q.defer(); if (t && !TEMPLATE_REGEXP.test(t)) { - $templateCache.put(uKey, $.ajax({ - type: "GET", - url: t, - async: false - }).responseText); + // $templateCache.put(uKey, $.ajax({ + // type: "GET", + // url: t, + // async: false + // }).responseText); + $http.get(t, { + cache: $templateCache + }) + .success(function(data){ + $templateCache.put(uKey, data); + p.resolve(); + }) + .error(function(err){ + p.reject("Could not load template: " + t); + }); } else if (t) { $templateCache.put(uKey, t); + p.resolve(); } else { var dKey = key + ".html"; $templateCache.put(uKey, $templateCache.get(dKey)); + p.resolve(); } + + return p.promise; }; - getTemplate('rowTemplate'); - getTemplate('aggregateTemplate'); - getTemplate('headerRowTemplate'); - getTemplate('checkboxCellTemplate'); - getTemplate('checkboxHeaderTemplate'); - getTemplate('menuTemplate'); - getTemplate('footerTemplate'); if (typeof self.config.data == "object") { self.data = self.config.data; // we cannot watch for updates if you don't pass the string name @@ -442,35 +469,41 @@ var ngGrid = function ($scope, options, sortService, domUtilityService, $filter, } }; self.init = function() { - //factories and services - $scope.selectionProvider = new ngSelectionProvider(self, $scope, $parse); - $scope.domAccessProvider = new ngDomAccessProvider(self); - self.rowFactory = new ngRowFactory(self, $scope, domUtilityService, $templateCache, $utils); - self.searchProvider = new ngSearchProvider($scope, self, $filter); - self.styleProvider = new ngStyleProvider($scope, self); - $scope.$watch('configGroups', function(a) { - var tempArr = []; - angular.forEach(a, function(item) { - tempArr.push(item.field || item); - }); - self.config.groups = tempArr; - self.rowFactory.filteredRowsChanged(); - $scope.$emit('ngGridEventGroups', a); - }, true); - $scope.$watch('columns', function (a) { - domUtilityService.BuildStyles($scope, self, true); - $scope.$emit('ngGridEventColumns', a); - }, true); - $scope.$watch(function() { - return options.i18n; - }, function(newLang) { - $utils.seti18n($scope, newLang); + return self.initTemplates().then(function(){ + //factories and services + $scope.selectionProvider = new ngSelectionProvider(self, $scope, $parse); + $scope.domAccessProvider = new ngDomAccessProvider(self); + self.rowFactory = new ngRowFactory(self, $scope, domUtilityService, $templateCache, $utils); + self.searchProvider = new ngSearchProvider($scope, self, $filter); + self.styleProvider = new ngStyleProvider($scope, self); + $scope.$watch('configGroups', function(a) { + var tempArr = []; + angular.forEach(a, function(item) { + tempArr.push(item.field || item); + }); + self.config.groups = tempArr; + self.rowFactory.filteredRowsChanged(); + $scope.$emit('ngGridEventGroups', a); + }, true); + $scope.$watch('columns', function (a) { + domUtilityService.BuildStyles($scope, self, true); + $scope.$emit('ngGridEventColumns', a); + }, true); + $scope.$watch(function() { + return options.i18n; + }, function(newLang) { + $utils.seti18n($scope, newLang); + }); + self.maxCanvasHt = self.calcMaxCanvasHeight(); + if (self.config.sortInfo.fields && self.config.sortInfo.fields.length > 0) { + self.getColsFromFields(); + self.sortActual(); + } }); - self.maxCanvasHt = self.calcMaxCanvasHeight(); - if (self.config.sortInfo.fields && self.config.sortInfo.fields.length > 0) { - self.getColsFromFields(); - self.sortActual(); - } + + // var p = $q.defer(); + // p.resolve(); + // return p.promise; }; self.resizeOnData = function(col) { @@ -813,6 +846,11 @@ var ngGrid = function ($scope, options, sortService, domUtilityService, $filter, } return newDim; }; + //call init - self.init(); + // self.initTemplates(); + // self.init(); + + /** Main execution **/ + //self.initTemplates().then(self.init); }; From 6f6c52d4d21b0ed387e73bc725ab3ab32d3189ae Mon Sep 17 00:00:00 2001 From: Brian Hann Date: Fri, 19 Apr 2013 15:32:40 -0500 Subject: [PATCH 03/17] Wait for grid.init() to complete, then continue grid.init() is now called here, not in src/classes/grid.js. It returns a promise which delays the execution until everything is ready (templates, and whatever else init() does). --- src/directives/ng-grid.js | 250 +++++++++++++++++++------------------- 1 file changed, 126 insertions(+), 124 deletions(-) diff --git a/src/directives/ng-grid.js b/src/directives/ng-grid.js index 9d4ac365b5..8d6b91dc77 100644 --- a/src/directives/ng-grid.js +++ b/src/directives/ng-grid.js @@ -1,4 +1,4 @@ -ngGridDirectives.directive('ngGrid', ['$compile', '$filter', '$templateCache', '$sortService', '$domUtilityService', '$utilityService', '$timeout', '$parse', function ($compile, $filter, $templateCache, sortService, domUtilityService, $utils, $timeout, $parse) { +ngGridDirectives.directive('ngGrid', ['$compile', '$filter', '$templateCache', '$sortService', '$domUtilityService', '$utilityService', '$timeout', '$parse', '$http', '$q', function ($compile, $filter, $templateCache, sortService, domUtilityService, $utils, $timeout, $parse, $http, $q) { var ngGridDirective = { scope: true, compile: function() { @@ -7,138 +7,140 @@ var $element = $(iElement); var options = $scope.$eval(iAttrs.ngGrid); options.gridDim = new ngDimension({ outerHeight: $($element).height(), outerWidth: $($element).width() }); - var grid = new ngGrid($scope, options, sortService, domUtilityService, $filter, $templateCache, $utils, $timeout, $parse); - // if columndefs are a string of a property ont he scope watch for changes and rebuild columns. - if (typeof options.columnDefs == "string") { - $scope.$parent.$watch(options.columnDefs, function (a) { - if (!a) { - grid.refreshDomSizes(); + var grid = new ngGrid($scope, options, sortService, domUtilityService, $filter, $templateCache, $utils, $timeout, $parse, $http, $q); + return grid.init().then(function() { + // if columndefs are a string of a property ont he scope watch for changes and rebuild columns. + if (typeof options.columnDefs == "string") { + $scope.$parent.$watch(options.columnDefs, function (a) { + if (!a) { + grid.refreshDomSizes(); + grid.buildColumns(); + return; + } + // we have to set this to false in case we want to autogenerate columns with no initial data. + grid.lateBoundColumns = false; + $scope.columns = []; + grid.config.columnDefs = a; grid.buildColumns(); - return; - } - // we have to set this to false in case we want to autogenerate columns with no initial data. - grid.lateBoundColumns = false; - $scope.columns = []; - grid.config.columnDefs = a; - grid.buildColumns(); - grid.configureColumnWidths(); - grid.eventProvider.assignEvents(); - domUtilityService.RebuildGrid($scope, grid); - }, true); - } else { - grid.buildColumns(); - } - - // if it is a string we can watch for data changes. otherwise you won't be able to update the grid data - if (typeof options.data == "string") { - var dataWatcher = function (a) { - // make a temporary copy of the data - grid.data = $.extend([], a); - grid.rowFactory.fixRowCache(); - angular.forEach(grid.data, function (item, j) { - var indx = grid.rowMap[j] || j; - if (grid.rowCache[indx]) { - grid.rowCache[indx].ensureEntity(item); + grid.configureColumnWidths(); + grid.eventProvider.assignEvents(); + domUtilityService.RebuildGrid($scope, grid); + }, true); + } else { + grid.buildColumns(); + } + + // if it is a string we can watch for data changes. otherwise you won't be able to update the grid data + if (typeof options.data == "string") { + var dataWatcher = function (a) { + // make a temporary copy of the data + grid.data = $.extend([], a); + grid.rowFactory.fixRowCache(); + angular.forEach(grid.data, function (item, j) { + var indx = grid.rowMap[j] || j; + if (grid.rowCache[indx]) { + grid.rowCache[indx].ensureEntity(item); + } + grid.rowMap[indx] = j; + }); + grid.searchProvider.evalFilter(); + grid.configureColumnWidths(); + grid.refreshDomSizes(); + if (grid.config.sortInfo.fields.length > 0) { + grid.getColsFromFields(); + grid.sortActual(); + grid.searchProvider.evalFilter(); + $scope.$emit('ngGridEventSorted', grid.config.sortInfo); } - grid.rowMap[indx] = j; + $scope.$emit("ngGridEventData", grid.gridId); + }; + $scope.$parent.$watch(options.data, dataWatcher); + $scope.$parent.$watch(options.data + '.length', function() { + dataWatcher($scope.$eval(options.data)); }); - grid.searchProvider.evalFilter(); - grid.configureColumnWidths(); - grid.refreshDomSizes(); - if (grid.config.sortInfo.fields.length > 0) { - grid.getColsFromFields(); - grid.sortActual(); - grid.searchProvider.evalFilter(); - $scope.$emit('ngGridEventSorted', grid.config.sortInfo); + } + + grid.footerController = new ngFooter($scope, grid); + //set the right styling on the container + iElement.addClass("ngGrid").addClass(grid.gridId.toString()); + if (!options.enableHighlighting) { + iElement.addClass("unselectable"); + } + if (options.jqueryUITheme) { + iElement.addClass('ui-widget'); + } + iElement.append($compile($templateCache.get('gridTemplate.html'))($scope)); // make sure that if any of these change, we re-fire the calc logic + //walk the element's graph and the correct properties on the grid + domUtilityService.AssignGridContainers($scope, iElement, grid); + //now use the manager to assign the event handlers + grid.eventProvider = new ngEventProvider(grid, $scope, domUtilityService, $timeout); + + // method for user to select a specific row programatically + options.selectRow = function (rowIndex, state) { + if (grid.rowCache[rowIndex]) { + if (grid.rowCache[rowIndex].clone) { + grid.rowCache[rowIndex].clone.setSelection(state ? true : false); + } + grid.rowCache[rowIndex].setSelection(state ? true : false); } - $scope.$emit("ngGridEventData", grid.gridId); }; - $scope.$parent.$watch(options.data, dataWatcher); - $scope.$parent.$watch(options.data + '.length', function() { - dataWatcher($scope.$eval(options.data)); - }); - } - - grid.footerController = new ngFooter($scope, grid); - //set the right styling on the container - iElement.addClass("ngGrid").addClass(grid.gridId.toString()); - if (!options.enableHighlighting) { - iElement.addClass("unselectable"); - } - if (options.jqueryUITheme) { - iElement.addClass('ui-widget'); - } - iElement.append($compile($templateCache.get('gridTemplate.html'))($scope)); // make sure that if any of these change, we re-fire the calc logic - //walk the element's graph and the correct properties on the grid - domUtilityService.AssignGridContainers($scope, iElement, grid); - //now use the manager to assign the event handlers - grid.eventProvider = new ngEventProvider(grid, $scope, domUtilityService, $timeout); - - // method for user to select a specific row programatically - options.selectRow = function (rowIndex, state) { - if (grid.rowCache[rowIndex]) { - if (grid.rowCache[rowIndex].clone) { - grid.rowCache[rowIndex].clone.setSelection(state ? true : false); - } - grid.rowCache[rowIndex].setSelection(state ? true : false); - } - }; - // method for user to select the row by data item programatically - options.selectItem = function (itemIndex, state) { - options.selectRow(grid.rowMap[itemIndex], state); - }; - // method for user to set the select all state. - options.selectAll = function (state) { - $scope.toggleSelectAll(state); - }; - // method for user to set the groups programatically - options.groupBy = function (field) { - if (field) { - $scope.groupBy($scope.columns.filter(function(c) { + // method for user to select the row by data item programatically + options.selectItem = function (itemIndex, state) { + options.selectRow(grid.rowMap[itemIndex], state); + }; + // method for user to set the select all state. + options.selectAll = function (state) { + $scope.toggleSelectAll(state); + }; + // method for user to set the groups programatically + options.groupBy = function (field) { + if (field) { + $scope.groupBy($scope.columns.filter(function(c) { + return c.field == field; + })[0]); + } else { + var arr = $.extend(true, [], $scope.configGroups); + angular.forEach(arr, $scope.groupBy); + } + }; + // method for user to set the sort field programatically + options.sortBy = function (field) { + var col = $scope.columns.filter(function (c) { return c.field == field; - })[0]); - } else { - var arr = $.extend(true, [], $scope.configGroups); - angular.forEach(arr, $scope.groupBy); - } - }; - // method for user to set the sort field programatically - options.sortBy = function (field) { - var col = $scope.columns.filter(function (c) { - return c.field == field; - })[0]; - if (col) col.sort(); - }; - // the grid Id, entity, scope for convenience - options.gridId = grid.gridId; - options.ngGrid = grid; - options.$gridScope = $scope; - options.$gridServices = { SortService: sortService, DomUtilityService: domUtilityService }; - $scope.$on('ngGridEventDigestGrid', function(){ - domUtilityService.digest($scope.$parent); - }); - - $scope.$on('ngGridEventDigestGridParent', function(){ - domUtilityService.digest($scope.$parent); - }); - // set up the columns - $scope.$evalAsync(function() { - $scope.adjustScrollLeft(0); - }); - //initialize plugins. - angular.forEach(options.plugins, function (p) { - if (typeof p === 'function') { - p = p.call(this); + })[0]; + if (col) col.sort(); + }; + // the grid Id, entity, scope for convenience + options.gridId = grid.gridId; + options.ngGrid = grid; + options.$gridScope = $scope; + options.$gridServices = { SortService: sortService, DomUtilityService: domUtilityService }; + $scope.$on('ngGridEventDigestGrid', function(){ + domUtilityService.digest($scope.$parent); + }); + + $scope.$on('ngGridEventDigestGridParent', function(){ + domUtilityService.digest($scope.$parent); + }); + // set up the columns + $scope.$evalAsync(function() { + $scope.adjustScrollLeft(0); + }); + //initialize plugins. + angular.forEach(options.plugins, function (p) { + if (typeof p === 'function') { + p = p.call(this); + } + p.init($scope.$new(), grid, options.$gridServices); + options.plugins[$utils.getInstanceType(p)] = p; + }); + //send initi finalize notification. + if (options.init == "function") { + options.init(grid, $scope); } - p.init($scope.$new(), grid, options.$gridServices); - options.plugins[$utils.getInstanceType(p)] = p; + return null; }); - //send initi finalize notification. - if (options.init == "function") { - options.init(grid, $scope); - } - return null; } }; } From 7ce548fab0b8b766bcef88e211ea3680439339a9 Mon Sep 17 00:00:00 2001 From: Brian Hann Date: Fri, 19 Apr 2013 15:35:55 -0500 Subject: [PATCH 04/17] Adding express webserver for testing templates A bug with external templating occurs when the template comes back AFTER the data is ready to be drawn in the grid. This script uses express to delay serving the grid template for a couple hundred milliseconds. From e6aebb48beb8d0b5fb41c3399448c740eb3079e5 Mon Sep 17 00:00:00 2001 From: Brian Hann Date: Fri, 19 Apr 2013 15:37:09 -0500 Subject: [PATCH 05/17] Adding express to devDependencies --- package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/package.json b/package.json index ecdff64b12..15ec84b200 100644 --- a/package.json +++ b/package.json @@ -25,6 +25,7 @@ "author": "ng-grid Team", "license": "MIT", "devDependencies": { + "express": "~3.2.0", "grunt": "~0.4.1", "grunt-jsdoc": "~0.2.4", "grunt-karma": "~0.4.3", From 3d2b2903a668052e33a54fd80f86cac6023ddb3c Mon Sep 17 00:00:00 2001 From: Brian Hann Date: Fri, 19 Apr 2013 15:37:33 -0500 Subject: [PATCH 06/17] Adding files for testing external templates There is a bug with external templating where if the template comes back AFTER asynchronously loaded data is ready, an "$apply/$digest is already executing" error will occur. This test will demonstrate that in tag 2.0.4 if you access it via scripts/server.js Conflicts: workbench/templating/external.html From 6ae7f8b314a943d8f6e3ca85bd777594aa03d021 Mon Sep 17 00:00:00 2001 From: Brian Hann Date: Fri, 19 Apr 2013 15:41:36 -0500 Subject: [PATCH 07/17] Removing commented out old code --- src/classes/grid.js | 20 -------------------- 1 file changed, 20 deletions(-) diff --git a/src/classes/grid.js b/src/classes/grid.js index bcbe47094a..9cdead4469 100644 --- a/src/classes/grid.js +++ b/src/classes/grid.js @@ -224,14 +224,6 @@ var ngGrid = function ($scope, options, sortService, domUtilityService, $filter, promises.push( self.getTemplate(template) ); }); - // getTemplate('rowTemplate'); - // getTemplate('aggregateTemplate'); - // getTemplate('headerRowTemplate'); - // getTemplate('checkboxCellTemplate'); - // getTemplate('checkboxHeaderTemplate'); - // getTemplate('menuTemplate'); - // getTemplate('footerTemplate'); - return $q.all(promises); }; @@ -242,11 +234,6 @@ var ngGrid = function ($scope, options, sortService, domUtilityService, $filter, var uKey = self.gridId + key + ".html"; var p = $q.defer(); if (t && !TEMPLATE_REGEXP.test(t)) { - // $templateCache.put(uKey, $.ajax({ - // type: "GET", - // url: t, - // async: false - // }).responseText); $http.get(t, { cache: $templateCache }) @@ -846,11 +833,4 @@ var ngGrid = function ($scope, options, sortService, domUtilityService, $filter, } return newDim; }; - - //call init - // self.initTemplates(); - // self.init(); - - /** Main execution **/ - //self.initTemplates().then(self.init); }; From bd428d223c0c0d352bb7c6c7201a64149f504a5f Mon Sep 17 00:00:00 2001 From: Brian Hann Date: Tue, 23 Apr 2013 11:14:38 -0500 Subject: [PATCH 08/17] Removing express from devDependencies --- package.json | 1 - 1 file changed, 1 deletion(-) diff --git a/package.json b/package.json index 1560851ee1..3d6a21a6aa 100644 --- a/package.json +++ b/package.json @@ -25,7 +25,6 @@ "author": "ng-grid Team", "license": "MIT", "devDependencies": { - "express": "~3.2.0", "grunt": "~0.4.1", "grunt-jsdoc": "~0.2.4", "grunt-karma": "~0.4.4", From 5115b9b46f986e4fdab26ea543b2c52f02ee7947 Mon Sep 17 00:00:00 2001 From: Brian Hann Date: Tue, 23 Apr 2013 11:24:00 -0500 Subject: [PATCH 09/17] Adding v2.0.5 change log notes --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index aa8c0cbb88..aae09f1f6e 100644 --- a/README.md +++ b/README.md @@ -110,6 +110,7 @@ There is a task for CI testing with PhantomJS ## Change Log +* __2013-04-??__ - Version 2.0.5 - Moving to $http for external template fetching. Should fix issues with grid rendering before templates are retrieved, as well as fetching the same template multiple times. * __2013-03-29__ - Version 2.0.3 - fixing some more minor bugs. * __2013-03-29__ - Version 2.0.3 - changed default multiSelect behavior, updating some plugins and making some more minor bugfixes. * __2013-03-08__ - Version 2.0.2 - minor bugfixes, updating some plugins. From 39aed266c0db0e83bab3c0057dc89eb02476a131 Mon Sep 17 00:00:00 2001 From: Brian Hann Date: Tue, 23 Apr 2013 13:20:48 -0500 Subject: [PATCH 10/17] Trying to get sortActual failing test working --- test/unit/directivesSpec.js | 37 ++++++++++++++++++++++++++++++++++--- 1 file changed, 34 insertions(+), 3 deletions(-) diff --git a/test/unit/directivesSpec.js b/test/unit/directivesSpec.js index e64f179dce..2233abd447 100644 --- a/test/unit/directivesSpec.js +++ b/test/unit/directivesSpec.js @@ -7,12 +7,31 @@ describe('directives', function () { var $scope; var $linker; var $cache; + var elm, scope; + + // Load the ngGrid module beforeEach(module('ngGrid')); + beforeEach(inject(function ($rootScope, $domUtilityService, $templateCache, $compile) { $scope = $rootScope.$new(); $dUtils = $domUtilityService; $linker = $compile; $cache = $templateCache; + + elm = angular.element( + '
' + ); + + scope = $rootScope; + scope.myData = [{name: "Moroni", age: 50}, + {name: "Tiancum", age: 43}, + {name: "Jacob", age: 27}, + {name: "Nephi", age: 29}, + {name: "Enos", age: 34}]; + scope.gridOptions = { data: 'myData' }; + + $compile(elm)(scope); + scope.$digest(); })); describe('ng-cell-has-focus', function() { @@ -88,9 +107,21 @@ describe('directives', function () { //add work here }); }); - describe('grid', function () { - it('should do something', function () { - //add work here + ddescribe('grid', function () { + describe('sortActual', function(){ + it('should maintain row selection post-sort', function(){ + + }); + + it('should allow newly created rows to be selected', function(){ + // Create a new row + var rowIndex = scope.myData.push({ name: 'Bob', age: '8' }); + scope.gridOptions.selectItem(rowIndex-1, true); + $scope.$apply(); + + var newRow = elm.find('.ngRow:nth-child(' + (rowIndex) + ')'); + expect(newRow.hasClass('selected')).toBe(true); + }); }); }); describe('row', function () { From a55190e66d9c415b8f4a62512f87c85144d913a4 Mon Sep 17 00:00:00 2001 From: Brian Hann Date: Tue, 23 Apr 2013 13:31:17 -0500 Subject: [PATCH 11/17] Failing test for sortActual working? --- test/unit/directivesSpec.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/unit/directivesSpec.js b/test/unit/directivesSpec.js index 2233abd447..ecf0c5baea 100644 --- a/test/unit/directivesSpec.js +++ b/test/unit/directivesSpec.js @@ -116,8 +116,9 @@ describe('directives', function () { it('should allow newly created rows to be selected', function(){ // Create a new row var rowIndex = scope.myData.push({ name: 'Bob', age: '8' }); + scope.$apply(); scope.gridOptions.selectItem(rowIndex-1, true); - $scope.$apply(); + scope.$apply(); var newRow = elm.find('.ngRow:nth-child(' + (rowIndex) + ')'); expect(newRow.hasClass('selected')).toBe(true); From 4e8cb2cadbe1210827096582beeba6b23a9bc657 Mon Sep 17 00:00:00 2001 From: Brian Hann Date: Tue, 23 Apr 2013 14:12:35 -0500 Subject: [PATCH 12/17] Fixes #348, patch supplied by sum4me sortActual() was using a non-existent variable for accessing the row in rowCache while iterating over the data. --- src/classes/grid.js | 2 +- test/unit/directivesSpec.js | 15 +++++++++++---- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/src/classes/grid.js b/src/classes/grid.js index 216bf7cb89..1c145a8184 100644 --- a/src/classes/grid.js +++ b/src/classes/grid.js @@ -565,7 +565,7 @@ var ngGrid = function ($scope, options, sortService, domUtilityService, $filter, angular.forEach(tempData, function(item, i) { var e = self.rowMap[i]; if (e != undefined) { - var v = self.rowCache[v]; + var v = self.rowCache[i]; if(v != undefined) { item.preSortSelected = v.selected; item.preSortIndex = i; diff --git a/test/unit/directivesSpec.js b/test/unit/directivesSpec.js index ecf0c5baea..3ef80ece9a 100644 --- a/test/unit/directivesSpec.js +++ b/test/unit/directivesSpec.js @@ -107,18 +107,25 @@ describe('directives', function () { //add work here }); }); - ddescribe('grid', function () { + describe('grid', function () { describe('sortActual', function(){ - it('should maintain row selection post-sort', function(){ + iit('should maintain row selection post-sort', function(){ + scope.gridOptions.selectItem(0, true); + scope.$digest(); + scope.gridOptions.sortBy('age'); + scope.$digest(); + var oldRow = elm.find('.ngRow:last'); + expect(oldRow.html()).toMatch(/Moroni.+?50/); + expect(oldRow.hasClass('selected')).toBe(true); }); it('should allow newly created rows to be selected', function(){ // Create a new row var rowIndex = scope.myData.push({ name: 'Bob', age: '8' }); - scope.$apply(); + scope.$digest(); scope.gridOptions.selectItem(rowIndex-1, true); - scope.$apply(); + scope.$digest(); var newRow = elm.find('.ngRow:nth-child(' + (rowIndex) + ')'); expect(newRow.hasClass('selected')).toBe(true); From 53bb3c06bdc454ef73f26258737cc3ff8503b150 Mon Sep 17 00:00:00 2001 From: Brian Hann Date: Tue, 23 Apr 2013 14:17:23 -0500 Subject: [PATCH 13/17] Turn off iit() of sortActual test --- test/unit/directivesSpec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/unit/directivesSpec.js b/test/unit/directivesSpec.js index 3ef80ece9a..9b4205de63 100644 --- a/test/unit/directivesSpec.js +++ b/test/unit/directivesSpec.js @@ -109,7 +109,7 @@ describe('directives', function () { }); describe('grid', function () { describe('sortActual', function(){ - iit('should maintain row selection post-sort', function(){ + it('should maintain row selection post-sort', function(){ scope.gridOptions.selectItem(0, true); scope.$digest(); scope.gridOptions.sortBy('age'); From ce2645d8eb277e04da97912abc8cc8e425b454a1 Mon Sep 17 00:00:00 2001 From: Brian Hann Date: Tue, 23 Apr 2013 14:33:39 -0500 Subject: [PATCH 14/17] Including notes on sortActual() fix --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index aae09f1f6e..ba2c4397e4 100644 --- a/README.md +++ b/README.md @@ -110,7 +110,7 @@ There is a task for CI testing with PhantomJS ## Change Log -* __2013-04-??__ - Version 2.0.5 - Moving to $http for external template fetching. Should fix issues with grid rendering before templates are retrieved, as well as fetching the same template multiple times. +* __2013-04-??__ - Version 2.0.5 - Moving to $http for external template fetching. Should fix issues with grid rendering before templates are retrieved, as well as fetching the same template multiple times. Also fixed bug that prevented the grid from maintaining row selections post-sort thanks to [sum4me](https://github.com/sum4me). * __2013-03-29__ - Version 2.0.3 - fixing some more minor bugs. * __2013-03-29__ - Version 2.0.3 - changed default multiSelect behavior, updating some plugins and making some more minor bugfixes. * __2013-03-08__ - Version 2.0.2 - minor bugfixes, updating some plugins. From 6c47ae26c4fbc8636d80c26e2c13121c40eb3667 Mon Sep 17 00:00:00 2001 From: Brian Hann Date: Tue, 23 Apr 2013 14:34:12 -0500 Subject: [PATCH 15/17] 2.0.5 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 3d6a21a6aa..a5f41d0649 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ng-grid", - "version": "2.0.4", + "version": "2.0.5", "description": "__Contributors:__", "main": "ng-grid.min.js", "directories": { From 7616107d5564c826bfada8c14f271d77f2e9f456 Mon Sep 17 00:00:00 2001 From: Brian Hann Date: Tue, 23 Apr 2013 14:39:05 -0500 Subject: [PATCH 16/17] Fixing dates, 2.0.4 messge --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index ba2c4397e4..fe0f73689b 100644 --- a/README.md +++ b/README.md @@ -110,8 +110,8 @@ There is a task for CI testing with PhantomJS ## Change Log -* __2013-04-??__ - Version 2.0.5 - Moving to $http for external template fetching. Should fix issues with grid rendering before templates are retrieved, as well as fetching the same template multiple times. Also fixed bug that prevented the grid from maintaining row selections post-sort thanks to [sum4me](https://github.com/sum4me). -* __2013-03-29__ - Version 2.0.3 - fixing some more minor bugs. +* __2013-04-23__ - Version 2.0.5 - Moving to $http for external template fetching. Should fix issues with grid rendering before templates are retrieved, as well as fetching the same template multiple times. Also fixed bug that prevented the grid from maintaining row selections post-sort thanks to [sum4me](https://github.com/sum4me). +* __2013-04-08__ - Version 2.0.4 - fixing some more minor bugs. * __2013-03-29__ - Version 2.0.3 - changed default multiSelect behavior, updating some plugins and making some more minor bugfixes. * __2013-03-08__ - Version 2.0.2 - minor bugfixes, updating some plugins. * __2013-03-05__ - Version 2.0.1 - Moved to grunt build system. No more international version; all languages are included by default. Fixed minor grouping display issue. Using $templateCache for templates instead of global namespace. From 0d1094e4f9bb41485bcd59eb5ada4e5489f2a137 Mon Sep 17 00:00:00 2001 From: Brian Hann Date: Tue, 23 Apr 2013 14:40:34 -0500 Subject: [PATCH 17/17] Updating to 2.0.5 --- build/ng-grid.debug.js | 360 +++++++++--------- build/ng-grid.js | 314 ++++++++------- build/ng-grid.min.js | 4 +- ng-grid-2.0.4.min.js | 2 - ...d-2.0.4.debug.js => ng-grid-2.0.5.debug.js | 360 +++++++++--------- ng-grid-2.0.5.min.js | 2 + 6 files changed, 550 insertions(+), 492 deletions(-) delete mode 100644 ng-grid-2.0.4.min.js rename ng-grid-2.0.4.debug.js => ng-grid-2.0.5.debug.js (92%) create mode 100644 ng-grid-2.0.5.min.js diff --git a/build/ng-grid.debug.js b/build/ng-grid.debug.js index e2929285bb..1ab885515c 100644 --- a/build/ng-grid.debug.js +++ b/build/ng-grid.debug.js @@ -2,7 +2,7 @@ * ng-grid JavaScript Library * Authors: https://github.com/angular-ui/ng-grid/blob/master/README.md * License: MIT (http://www.opensource.org/licenses/mit-license.php) -* Compiled At: 04/16/2013 15:21 +* Compiled At: 04/23/2013 14:36 ***********************************************/ (function(window, $) { 'use strict'; @@ -1196,7 +1196,7 @@ var ngFooter = function ($scope, grid) { /// /// /// -var ngGrid = function ($scope, options, sortService, domUtilityService, $filter, $templateCache, $utils, $timeout, $parse) { +var ngGrid = function ($scope, options, sortService, domUtilityService, $filter, $templateCache, $utils, $timeout, $parse, $http, $q) { var defaults = { //Define an aggregate template to customize the rows when grouped. See github wiki for more details. aggregateTemplate: undefined, @@ -1410,32 +1410,46 @@ var ngGrid = function ($scope, options, sortService, domUtilityService, $filter, self.data = []; self.lateBindColumns = false; self.filteredRows = []; + + self.initTemplates = function() { + var templates = ['rowTemplate', 'aggregateTemplate', 'headerRowTemplate', 'checkboxCellTemplate', 'checkboxHeaderTemplate', 'menuTemplate', 'footerTemplate']; + + var promises = []; + templates.forEach(function(template) { + promises.push( self.getTemplate(template) ); + }); + + return $q.all(promises); + }; //Templates // test templates for urls and get the tempaltes via synchronous ajax calls - var getTemplate = function (key) { + self.getTemplate = function (key) { var t = self.config[key]; var uKey = self.gridId + key + ".html"; + var p = $q.defer(); if (t && !TEMPLATE_REGEXP.test(t)) { - $templateCache.put(uKey, $.ajax({ - type: "GET", - url: t, - async: false - }).responseText); + $http.get(t, { + cache: $templateCache + }) + .success(function(data){ + $templateCache.put(uKey, data); + p.resolve(); + }) + .error(function(err){ + p.reject("Could not load template: " + t); + }); } else if (t) { $templateCache.put(uKey, t); + p.resolve(); } else { var dKey = key + ".html"; $templateCache.put(uKey, $templateCache.get(dKey)); + p.resolve(); } + + return p.promise; }; - getTemplate('rowTemplate'); - getTemplate('aggregateTemplate'); - getTemplate('headerRowTemplate'); - getTemplate('checkboxCellTemplate'); - getTemplate('checkboxHeaderTemplate'); - getTemplate('menuTemplate'); - getTemplate('footerTemplate'); if (typeof self.config.data == "object") { self.data = self.config.data; // we cannot watch for updates if you don't pass the string name @@ -1637,35 +1651,41 @@ var ngGrid = function ($scope, options, sortService, domUtilityService, $filter, } }; self.init = function() { - //factories and services - $scope.selectionProvider = new ngSelectionProvider(self, $scope, $parse); - $scope.domAccessProvider = new ngDomAccessProvider(self); - self.rowFactory = new ngRowFactory(self, $scope, domUtilityService, $templateCache, $utils); - self.searchProvider = new ngSearchProvider($scope, self, $filter); - self.styleProvider = new ngStyleProvider($scope, self); - $scope.$watch('configGroups', function(a) { - var tempArr = []; - angular.forEach(a, function(item) { - tempArr.push(item.field || item); - }); - self.config.groups = tempArr; - self.rowFactory.filteredRowsChanged(); - $scope.$emit('ngGridEventGroups', a); - }, true); - $scope.$watch('columns', function (a) { - domUtilityService.BuildStyles($scope, self, true); - $scope.$emit('ngGridEventColumns', a); - }, true); - $scope.$watch(function() { - return options.i18n; - }, function(newLang) { - $utils.seti18n($scope, newLang); + return self.initTemplates().then(function(){ + //factories and services + $scope.selectionProvider = new ngSelectionProvider(self, $scope, $parse); + $scope.domAccessProvider = new ngDomAccessProvider(self); + self.rowFactory = new ngRowFactory(self, $scope, domUtilityService, $templateCache, $utils); + self.searchProvider = new ngSearchProvider($scope, self, $filter); + self.styleProvider = new ngStyleProvider($scope, self); + $scope.$watch('configGroups', function(a) { + var tempArr = []; + angular.forEach(a, function(item) { + tempArr.push(item.field || item); + }); + self.config.groups = tempArr; + self.rowFactory.filteredRowsChanged(); + $scope.$emit('ngGridEventGroups', a); + }, true); + $scope.$watch('columns', function (a) { + domUtilityService.BuildStyles($scope, self, true); + $scope.$emit('ngGridEventColumns', a); + }, true); + $scope.$watch(function() { + return options.i18n; + }, function(newLang) { + $utils.seti18n($scope, newLang); + }); + self.maxCanvasHt = self.calcMaxCanvasHeight(); + if (self.config.sortInfo.fields && self.config.sortInfo.fields.length > 0) { + self.getColsFromFields(); + self.sortActual(); + } }); - self.maxCanvasHt = self.calcMaxCanvasHeight(); - if (self.config.sortInfo.fields && self.config.sortInfo.fields.length > 0) { - self.getColsFromFields(); - self.sortActual(); - } + + // var p = $q.defer(); + // p.resolve(); + // return p.promise; }; self.resizeOnData = function(col) { @@ -1760,7 +1780,7 @@ var ngGrid = function ($scope, options, sortService, domUtilityService, $filter, angular.forEach(tempData, function(item, i) { var e = self.rowMap[i]; if (e != undefined) { - var v = self.rowCache[v]; + var v = self.rowCache[i]; if(v != undefined) { item.preSortSelected = v.selected; item.preSortIndex = i; @@ -2008,8 +2028,6 @@ var ngGrid = function ($scope, options, sortService, domUtilityService, $filter, } return newDim; }; - //call init - self.init(); }; var ngRange = function (top, bottom) { @@ -2799,7 +2817,7 @@ ngGridDirectives.directive('ngGridMenu', ['$compile', '$templateCache', function }; return ngGridMenu; }]); -ngGridDirectives.directive('ngGrid', ['$compile', '$filter', '$templateCache', '$sortService', '$domUtilityService', '$utilityService', '$timeout', '$parse', function ($compile, $filter, $templateCache, sortService, domUtilityService, $utils, $timeout, $parse) { +ngGridDirectives.directive('ngGrid', ['$compile', '$filter', '$templateCache', '$sortService', '$domUtilityService', '$utilityService', '$timeout', '$parse', '$http', '$q', function ($compile, $filter, $templateCache, sortService, domUtilityService, $utils, $timeout, $parse, $http, $q) { var ngGridDirective = { scope: true, compile: function() { @@ -2808,138 +2826,140 @@ ngGridDirectives.directive('ngGrid', ['$compile', '$filter', '$templateCache', ' var $element = $(iElement); var options = $scope.$eval(iAttrs.ngGrid); options.gridDim = new ngDimension({ outerHeight: $($element).height(), outerWidth: $($element).width() }); - var grid = new ngGrid($scope, options, sortService, domUtilityService, $filter, $templateCache, $utils, $timeout, $parse); - // if columndefs are a string of a property ont he scope watch for changes and rebuild columns. - if (typeof options.columnDefs == "string") { - $scope.$parent.$watch(options.columnDefs, function (a) { - if (!a) { - grid.refreshDomSizes(); + var grid = new ngGrid($scope, options, sortService, domUtilityService, $filter, $templateCache, $utils, $timeout, $parse, $http, $q); + return grid.init().then(function() { + // if columndefs are a string of a property ont he scope watch for changes and rebuild columns. + if (typeof options.columnDefs == "string") { + $scope.$parent.$watch(options.columnDefs, function (a) { + if (!a) { + grid.refreshDomSizes(); + grid.buildColumns(); + return; + } + // we have to set this to false in case we want to autogenerate columns with no initial data. + grid.lateBoundColumns = false; + $scope.columns = []; + grid.config.columnDefs = a; grid.buildColumns(); - return; - } - // we have to set this to false in case we want to autogenerate columns with no initial data. - grid.lateBoundColumns = false; - $scope.columns = []; - grid.config.columnDefs = a; - grid.buildColumns(); - grid.configureColumnWidths(); - grid.eventProvider.assignEvents(); - domUtilityService.RebuildGrid($scope, grid); - }, true); - } else { - grid.buildColumns(); - } - - // if it is a string we can watch for data changes. otherwise you won't be able to update the grid data - if (typeof options.data == "string") { - var dataWatcher = function (a) { - // make a temporary copy of the data - grid.data = $.extend([], a); - grid.rowFactory.fixRowCache(); - angular.forEach(grid.data, function (item, j) { - var indx = grid.rowMap[j] || j; - if (grid.rowCache[indx]) { - grid.rowCache[indx].ensureEntity(item); + grid.configureColumnWidths(); + grid.eventProvider.assignEvents(); + domUtilityService.RebuildGrid($scope, grid); + }, true); + } else { + grid.buildColumns(); + } + + // if it is a string we can watch for data changes. otherwise you won't be able to update the grid data + if (typeof options.data == "string") { + var dataWatcher = function (a) { + // make a temporary copy of the data + grid.data = $.extend([], a); + grid.rowFactory.fixRowCache(); + angular.forEach(grid.data, function (item, j) { + var indx = grid.rowMap[j] || j; + if (grid.rowCache[indx]) { + grid.rowCache[indx].ensureEntity(item); + } + grid.rowMap[indx] = j; + }); + grid.searchProvider.evalFilter(); + grid.configureColumnWidths(); + grid.refreshDomSizes(); + if (grid.config.sortInfo.fields.length > 0) { + grid.getColsFromFields(); + grid.sortActual(); + grid.searchProvider.evalFilter(); + $scope.$emit('ngGridEventSorted', grid.config.sortInfo); } - grid.rowMap[indx] = j; + $scope.$emit("ngGridEventData", grid.gridId); + }; + $scope.$parent.$watch(options.data, dataWatcher); + $scope.$parent.$watch(options.data + '.length', function() { + dataWatcher($scope.$eval(options.data)); }); - grid.searchProvider.evalFilter(); - grid.configureColumnWidths(); - grid.refreshDomSizes(); - if (grid.config.sortInfo.fields.length > 0) { - grid.getColsFromFields(); - grid.sortActual(); - grid.searchProvider.evalFilter(); - $scope.$emit('ngGridEventSorted', grid.config.sortInfo); + } + + grid.footerController = new ngFooter($scope, grid); + //set the right styling on the container + iElement.addClass("ngGrid").addClass(grid.gridId.toString()); + if (!options.enableHighlighting) { + iElement.addClass("unselectable"); + } + if (options.jqueryUITheme) { + iElement.addClass('ui-widget'); + } + iElement.append($compile($templateCache.get('gridTemplate.html'))($scope)); // make sure that if any of these change, we re-fire the calc logic + //walk the element's graph and the correct properties on the grid + domUtilityService.AssignGridContainers($scope, iElement, grid); + //now use the manager to assign the event handlers + grid.eventProvider = new ngEventProvider(grid, $scope, domUtilityService, $timeout); + + // method for user to select a specific row programatically + options.selectRow = function (rowIndex, state) { + if (grid.rowCache[rowIndex]) { + if (grid.rowCache[rowIndex].clone) { + grid.rowCache[rowIndex].clone.setSelection(state ? true : false); + } + grid.rowCache[rowIndex].setSelection(state ? true : false); } - $scope.$emit("ngGridEventData", grid.gridId); }; - $scope.$parent.$watch(options.data, dataWatcher); - $scope.$parent.$watch(options.data + '.length', function() { - dataWatcher($scope.$eval(options.data)); - }); - } - - grid.footerController = new ngFooter($scope, grid); - //set the right styling on the container - iElement.addClass("ngGrid").addClass(grid.gridId.toString()); - if (!options.enableHighlighting) { - iElement.addClass("unselectable"); - } - if (options.jqueryUITheme) { - iElement.addClass('ui-widget'); - } - iElement.append($compile($templateCache.get('gridTemplate.html'))($scope)); // make sure that if any of these change, we re-fire the calc logic - //walk the element's graph and the correct properties on the grid - domUtilityService.AssignGridContainers($scope, iElement, grid); - //now use the manager to assign the event handlers - grid.eventProvider = new ngEventProvider(grid, $scope, domUtilityService, $timeout); - - // method for user to select a specific row programatically - options.selectRow = function (rowIndex, state) { - if (grid.rowCache[rowIndex]) { - if (grid.rowCache[rowIndex].clone) { - grid.rowCache[rowIndex].clone.setSelection(state ? true : false); - } - grid.rowCache[rowIndex].setSelection(state ? true : false); - } - }; - // method for user to select the row by data item programatically - options.selectItem = function (itemIndex, state) { - options.selectRow(grid.rowMap[itemIndex], state); - }; - // method for user to set the select all state. - options.selectAll = function (state) { - $scope.toggleSelectAll(state); - }; - // method for user to set the groups programatically - options.groupBy = function (field) { - if (field) { - $scope.groupBy($scope.columns.filter(function(c) { + // method for user to select the row by data item programatically + options.selectItem = function (itemIndex, state) { + options.selectRow(grid.rowMap[itemIndex], state); + }; + // method for user to set the select all state. + options.selectAll = function (state) { + $scope.toggleSelectAll(state); + }; + // method for user to set the groups programatically + options.groupBy = function (field) { + if (field) { + $scope.groupBy($scope.columns.filter(function(c) { + return c.field == field; + })[0]); + } else { + var arr = $.extend(true, [], $scope.configGroups); + angular.forEach(arr, $scope.groupBy); + } + }; + // method for user to set the sort field programatically + options.sortBy = function (field) { + var col = $scope.columns.filter(function (c) { return c.field == field; - })[0]); - } else { - var arr = $.extend(true, [], $scope.configGroups); - angular.forEach(arr, $scope.groupBy); - } - }; - // method for user to set the sort field programatically - options.sortBy = function (field) { - var col = $scope.columns.filter(function (c) { - return c.field == field; - })[0]; - if (col) col.sort(); - }; - // the grid Id, entity, scope for convenience - options.gridId = grid.gridId; - options.ngGrid = grid; - options.$gridScope = $scope; - options.$gridServices = { SortService: sortService, DomUtilityService: domUtilityService }; - $scope.$on('ngGridEventDigestGrid', function(){ - domUtilityService.digest($scope.$parent); - }); - - $scope.$on('ngGridEventDigestGridParent', function(){ - domUtilityService.digest($scope.$parent); - }); - // set up the columns - $scope.$evalAsync(function() { - $scope.adjustScrollLeft(0); - }); - //initialize plugins. - angular.forEach(options.plugins, function (p) { - if (typeof p === 'function') { - p = p.call(this); + })[0]; + if (col) col.sort(); + }; + // the grid Id, entity, scope for convenience + options.gridId = grid.gridId; + options.ngGrid = grid; + options.$gridScope = $scope; + options.$gridServices = { SortService: sortService, DomUtilityService: domUtilityService }; + $scope.$on('ngGridEventDigestGrid', function(){ + domUtilityService.digest($scope.$parent); + }); + + $scope.$on('ngGridEventDigestGridParent', function(){ + domUtilityService.digest($scope.$parent); + }); + // set up the columns + $scope.$evalAsync(function() { + $scope.adjustScrollLeft(0); + }); + //initialize plugins. + angular.forEach(options.plugins, function (p) { + if (typeof p === 'function') { + p = p.call(this); + } + p.init($scope.$new(), grid, options.$gridServices); + options.plugins[$utils.getInstanceType(p)] = p; + }); + //send initi finalize notification. + if (options.init == "function") { + options.init(grid, $scope); } - p.init($scope.$new(), grid, options.$gridServices); - options.plugins[$utils.getInstanceType(p)] = p; + return null; }); - //send initi finalize notification. - if (options.init == "function") { - options.init(grid, $scope); - } - return null; } }; } diff --git a/build/ng-grid.js b/build/ng-grid.js index 9d116f6d24..99537a4837 100644 --- a/build/ng-grid.js +++ b/build/ng-grid.js @@ -2,7 +2,7 @@ * ng-grid JavaScript Library * Authors: https://github.com/angular-ui/ng-grid/blob/master/README.md * License: MIT (http://www.opensource.org/licenses/mit-license.php) -* Compiled At: 04/16/2013 15:21 +* Compiled At: 04/23/2013 14:36 ***********************************************/ (function(window, $) { 'use strict'; @@ -1097,7 +1097,7 @@ var ngFooter = function ($scope, grid) { }; }; -var ngGrid = function ($scope, options, sortService, domUtilityService, $filter, $templateCache, $utils, $timeout, $parse) { +var ngGrid = function ($scope, options, sortService, domUtilityService, $filter, $templateCache, $utils, $timeout, $parse, $http, $q) { var defaults = { aggregateTemplate: undefined, afterSelectionChange: function() { @@ -1189,29 +1189,43 @@ var ngGrid = function ($scope, options, sortService, domUtilityService, $filter, self.data = []; self.lateBindColumns = false; self.filteredRows = []; - var getTemplate = function (key) { + + self.initTemplates = function() { + var templates = ['rowTemplate', 'aggregateTemplate', 'headerRowTemplate', 'checkboxCellTemplate', 'checkboxHeaderTemplate', 'menuTemplate', 'footerTemplate']; + + var promises = []; + templates.forEach(function(template) { + promises.push( self.getTemplate(template) ); + }); + + return $q.all(promises); + }; + self.getTemplate = function (key) { var t = self.config[key]; var uKey = self.gridId + key + ".html"; + var p = $q.defer(); if (t && !TEMPLATE_REGEXP.test(t)) { - $templateCache.put(uKey, $.ajax({ - type: "GET", - url: t, - async: false - }).responseText); + $http.get(t, { + cache: $templateCache + }) + .success(function(data){ + $templateCache.put(uKey, data); + p.resolve(); + }) + .error(function(err){ + p.reject("Could not load template: " + t); + }); } else if (t) { $templateCache.put(uKey, t); + p.resolve(); } else { var dKey = key + ".html"; $templateCache.put(uKey, $templateCache.get(dKey)); + p.resolve(); } + + return p.promise; }; - getTemplate('rowTemplate'); - getTemplate('aggregateTemplate'); - getTemplate('headerRowTemplate'); - getTemplate('checkboxCellTemplate'); - getTemplate('checkboxHeaderTemplate'); - getTemplate('menuTemplate'); - getTemplate('footerTemplate'); if (typeof self.config.data == "object") { self.data = self.config.data; @@ -1401,34 +1415,36 @@ var ngGrid = function ($scope, options, sortService, domUtilityService, $filter, } }; self.init = function() { - $scope.selectionProvider = new ngSelectionProvider(self, $scope, $parse); - $scope.domAccessProvider = new ngDomAccessProvider(self); - self.rowFactory = new ngRowFactory(self, $scope, domUtilityService, $templateCache, $utils); - self.searchProvider = new ngSearchProvider($scope, self, $filter); - self.styleProvider = new ngStyleProvider($scope, self); - $scope.$watch('configGroups', function(a) { - var tempArr = []; - angular.forEach(a, function(item) { - tempArr.push(item.field || item); - }); - self.config.groups = tempArr; - self.rowFactory.filteredRowsChanged(); - $scope.$emit('ngGridEventGroups', a); - }, true); - $scope.$watch('columns', function (a) { - domUtilityService.BuildStyles($scope, self, true); - $scope.$emit('ngGridEventColumns', a); - }, true); - $scope.$watch(function() { - return options.i18n; - }, function(newLang) { - $utils.seti18n($scope, newLang); + return self.initTemplates().then(function(){ + $scope.selectionProvider = new ngSelectionProvider(self, $scope, $parse); + $scope.domAccessProvider = new ngDomAccessProvider(self); + self.rowFactory = new ngRowFactory(self, $scope, domUtilityService, $templateCache, $utils); + self.searchProvider = new ngSearchProvider($scope, self, $filter); + self.styleProvider = new ngStyleProvider($scope, self); + $scope.$watch('configGroups', function(a) { + var tempArr = []; + angular.forEach(a, function(item) { + tempArr.push(item.field || item); + }); + self.config.groups = tempArr; + self.rowFactory.filteredRowsChanged(); + $scope.$emit('ngGridEventGroups', a); + }, true); + $scope.$watch('columns', function (a) { + domUtilityService.BuildStyles($scope, self, true); + $scope.$emit('ngGridEventColumns', a); + }, true); + $scope.$watch(function() { + return options.i18n; + }, function(newLang) { + $utils.seti18n($scope, newLang); + }); + self.maxCanvasHt = self.calcMaxCanvasHeight(); + if (self.config.sortInfo.fields && self.config.sortInfo.fields.length > 0) { + self.getColsFromFields(); + self.sortActual(); + } }); - self.maxCanvasHt = self.calcMaxCanvasHeight(); - if (self.config.sortInfo.fields && self.config.sortInfo.fields.length > 0) { - self.getColsFromFields(); - self.sortActual(); - } }; self.resizeOnData = function(col) { var longest = col.minWidth; @@ -1520,7 +1536,7 @@ var ngGrid = function ($scope, options, sortService, domUtilityService, $filter, angular.forEach(tempData, function(item, i) { var e = self.rowMap[i]; if (e != undefined) { - var v = self.rowCache[v]; + var v = self.rowCache[i]; if(v != undefined) { item.preSortSelected = v.selected; item.preSortIndex = i; @@ -1756,7 +1772,6 @@ var ngGrid = function ($scope, options, sortService, domUtilityService, $filter, } return newDim; }; - self.init(); }; var ngRange = function (top, bottom) { @@ -2506,7 +2521,7 @@ ngGridDirectives.directive('ngGridMenu', ['$compile', '$templateCache', function }; return ngGridMenu; }]); -ngGridDirectives.directive('ngGrid', ['$compile', '$filter', '$templateCache', '$sortService', '$domUtilityService', '$utilityService', '$timeout', '$parse', function ($compile, $filter, $templateCache, sortService, domUtilityService, $utils, $timeout, $parse) { +ngGridDirectives.directive('ngGrid', ['$compile', '$filter', '$templateCache', '$sortService', '$domUtilityService', '$utilityService', '$timeout', '$parse', '$http', '$q', function ($compile, $filter, $templateCache, sortService, domUtilityService, $utils, $timeout, $parse, $http, $q) { var ngGridDirective = { scope: true, compile: function() { @@ -2515,117 +2530,120 @@ ngGridDirectives.directive('ngGrid', ['$compile', '$filter', '$templateCache', ' var $element = $(iElement); var options = $scope.$eval(iAttrs.ngGrid); options.gridDim = new ngDimension({ outerHeight: $($element).height(), outerWidth: $($element).width() }); - var grid = new ngGrid($scope, options, sortService, domUtilityService, $filter, $templateCache, $utils, $timeout, $parse); - if (typeof options.columnDefs == "string") { - $scope.$parent.$watch(options.columnDefs, function (a) { - if (!a) { - grid.refreshDomSizes(); + + var grid = new ngGrid($scope, options, sortService, domUtilityService, $filter, $templateCache, $utils, $timeout, $parse, $http, $q); + return grid.init().then(function() { + if (typeof options.columnDefs == "string") { + $scope.$parent.$watch(options.columnDefs, function (a) { + if (!a) { + grid.refreshDomSizes(); + grid.buildColumns(); + return; + } + grid.lateBoundColumns = false; + $scope.columns = []; + grid.config.columnDefs = a; grid.buildColumns(); - return; - } - grid.lateBoundColumns = false; - $scope.columns = []; - grid.config.columnDefs = a; - grid.buildColumns(); - grid.configureColumnWidths(); - grid.eventProvider.assignEvents(); - domUtilityService.RebuildGrid($scope, grid); - }, true); - } else { - grid.buildColumns(); - } - if (typeof options.data == "string") { - var dataWatcher = function (a) { - grid.data = $.extend([], a); - grid.rowFactory.fixRowCache(); - angular.forEach(grid.data, function (item, j) { - var indx = grid.rowMap[j] || j; - if (grid.rowCache[indx]) { - grid.rowCache[indx].ensureEntity(item); + grid.configureColumnWidths(); + grid.eventProvider.assignEvents(); + domUtilityService.RebuildGrid($scope, grid); + }, true); + } else { + grid.buildColumns(); + } + if (typeof options.data == "string") { + var dataWatcher = function (a) { + grid.data = $.extend([], a); + grid.rowFactory.fixRowCache(); + angular.forEach(grid.data, function (item, j) { + var indx = grid.rowMap[j] || j; + if (grid.rowCache[indx]) { + grid.rowCache[indx].ensureEntity(item); + } + grid.rowMap[indx] = j; + }); + grid.searchProvider.evalFilter(); + grid.configureColumnWidths(); + grid.refreshDomSizes(); + if (grid.config.sortInfo.fields.length > 0) { + grid.getColsFromFields(); + grid.sortActual(); + grid.searchProvider.evalFilter(); + $scope.$emit('ngGridEventSorted', grid.config.sortInfo); } - grid.rowMap[indx] = j; + $scope.$emit("ngGridEventData", grid.gridId); + }; + $scope.$parent.$watch(options.data, dataWatcher); + $scope.$parent.$watch(options.data + '.length', function() { + dataWatcher($scope.$eval(options.data)); }); - grid.searchProvider.evalFilter(); - grid.configureColumnWidths(); - grid.refreshDomSizes(); - if (grid.config.sortInfo.fields.length > 0) { - grid.getColsFromFields(); - grid.sortActual(); - grid.searchProvider.evalFilter(); - $scope.$emit('ngGridEventSorted', grid.config.sortInfo); + } + grid.footerController = new ngFooter($scope, grid); + iElement.addClass("ngGrid").addClass(grid.gridId.toString()); + if (!options.enableHighlighting) { + iElement.addClass("unselectable"); + } + if (options.jqueryUITheme) { + iElement.addClass('ui-widget'); + } + iElement.append($compile($templateCache.get('gridTemplate.html'))($scope)); + domUtilityService.AssignGridContainers($scope, iElement, grid); + grid.eventProvider = new ngEventProvider(grid, $scope, domUtilityService, $timeout); + options.selectRow = function (rowIndex, state) { + if (grid.rowCache[rowIndex]) { + if (grid.rowCache[rowIndex].clone) { + grid.rowCache[rowIndex].clone.setSelection(state ? true : false); + } + grid.rowCache[rowIndex].setSelection(state ? true : false); } - $scope.$emit("ngGridEventData", grid.gridId); }; - $scope.$parent.$watch(options.data, dataWatcher); - $scope.$parent.$watch(options.data + '.length', function() { - dataWatcher($scope.$eval(options.data)); - }); - } - grid.footerController = new ngFooter($scope, grid); - iElement.addClass("ngGrid").addClass(grid.gridId.toString()); - if (!options.enableHighlighting) { - iElement.addClass("unselectable"); - } - if (options.jqueryUITheme) { - iElement.addClass('ui-widget'); - } - iElement.append($compile($templateCache.get('gridTemplate.html'))($scope)); - domUtilityService.AssignGridContainers($scope, iElement, grid); - grid.eventProvider = new ngEventProvider(grid, $scope, domUtilityService, $timeout); - options.selectRow = function (rowIndex, state) { - if (grid.rowCache[rowIndex]) { - if (grid.rowCache[rowIndex].clone) { - grid.rowCache[rowIndex].clone.setSelection(state ? true : false); - } - grid.rowCache[rowIndex].setSelection(state ? true : false); - } - }; - options.selectItem = function (itemIndex, state) { - options.selectRow(grid.rowMap[itemIndex], state); - }; - options.selectAll = function (state) { - $scope.toggleSelectAll(state); - }; - options.groupBy = function (field) { - if (field) { - $scope.groupBy($scope.columns.filter(function(c) { + options.selectItem = function (itemIndex, state) { + options.selectRow(grid.rowMap[itemIndex], state); + }; + options.selectAll = function (state) { + $scope.toggleSelectAll(state); + }; + options.groupBy = function (field) { + if (field) { + $scope.groupBy($scope.columns.filter(function(c) { + return c.field == field; + })[0]); + } else { + var arr = $.extend(true, [], $scope.configGroups); + angular.forEach(arr, $scope.groupBy); + } + }; + options.sortBy = function (field) { + var col = $scope.columns.filter(function (c) { return c.field == field; - })[0]); - } else { - var arr = $.extend(true, [], $scope.configGroups); - angular.forEach(arr, $scope.groupBy); - } - }; - options.sortBy = function (field) { - var col = $scope.columns.filter(function (c) { - return c.field == field; - })[0]; - if (col) col.sort(); - }; - options.gridId = grid.gridId; - options.ngGrid = grid; - options.$gridScope = $scope; - options.$gridServices = { SortService: sortService, DomUtilityService: domUtilityService }; - $scope.$on('ngGridEventDigestGrid', function(){ - domUtilityService.digest($scope.$parent); - }); - $scope.$on('ngGridEventDigestGridParent', function(){ - domUtilityService.digest($scope.$parent); - }); - $scope.$evalAsync(function() { - $scope.adjustScrollLeft(0); - }); - angular.forEach(options.plugins, function (p) { - if (typeof p === 'function') { - p = p.call(this); + })[0]; + if (col) col.sort(); + }; + options.gridId = grid.gridId; + options.ngGrid = grid; + options.$gridScope = $scope; + options.$gridServices = { SortService: sortService, DomUtilityService: domUtilityService }; + $scope.$on('ngGridEventDigestGrid', function(){ + domUtilityService.digest($scope.$parent); + }); + $scope.$on('ngGridEventDigestGridParent', function(){ + domUtilityService.digest($scope.$parent); + }); + $scope.$evalAsync(function() { + $scope.adjustScrollLeft(0); + }); + angular.forEach(options.plugins, function (p) { + if (typeof p === 'function') { + p = p.call(this); + } + p.init($scope.$new(), grid, options.$gridServices); + options.plugins[$utils.getInstanceType(p)] = p; + }); + if (options.init == "function") { + options.init(grid, $scope); } - p.init($scope.$new(), grid, options.$gridServices); - options.plugins[$utils.getInstanceType(p)] = p; + return null; }); - if (options.init == "function") { - options.init(grid, $scope); - } - return null; } }; } diff --git a/build/ng-grid.min.js b/build/ng-grid.min.js index 710d1f9f7e..53bd0d5052 100644 --- a/build/ng-grid.min.js +++ b/build/ng-grid.min.js @@ -1,2 +1,2 @@ -(function(e,n){"use strict";var t=6,o=4,r="asc",i="desc",l="_ng_field_",a="_ng_depth_",s="_ng_hidden_",c="_ng_column_",g=/CUSTOM_FILTERS/g,d=/COL_FIELD/g,u=/DISPLAY_CELL_TEMPLATE/g,f=/EDITABLE_CELL_TEMPLATE/g,p=/<.+>/;e.ngGrid={},e.ngGrid.i18n={};var h=angular.module("ngGrid.services",[]),m=angular.module("ngGrid.directives",[]),v=angular.module("ngGrid.filters",[]);angular.module("ngGrid",["ngGrid.services","ngGrid.directives","ngGrid.filters"]);var w=function(e,n,o,r){if(void 0===e.selectionProvider.selectedItems)return!0;var i,l=o.which||o.keyCode,a=!1,s=!1,c=e.selectionProvider.lastClickedRow.rowIndex,g=e.columns.filter(function(e){return e.visible}),d=e.columns.filter(function(e){return e.pinned});if(e.col&&(i=g.indexOf(e.col)),37!=l&&38!=l&&39!=l&&40!=l&&9!=l&&13!=l)return!0;if(e.enableCellSelection){9==l&&o.preventDefault();var u=e.showSelectionCheckbox?1==e.col.index:0==e.col.index,f=1==e.$index||0==e.$index,p=e.$index==e.renderedColumns.length-1||e.$index==e.renderedColumns.length-2,h=g.indexOf(e.col)==g.length-1,m=d.indexOf(e.col)==d.length-1;if(37==l||9==l&&o.shiftKey){var v=0;u||(i-=1),f?u&&9==l&&o.shiftKey?(v=r.$canvas.width(),i=g.length-1,s=!0):v=r.$viewport.scrollLeft()-e.col.width:d.length>0&&(v=r.$viewport.scrollLeft()-g[i].width),r.$viewport.scrollLeft(v)}else(39==l||9==l&&!o.shiftKey)&&(p?h&&9==l&&!o.shiftKey?(r.$viewport.scrollLeft(0),i=e.showSelectionCheckbox?1:0,a=!0):r.$viewport.scrollLeft(r.$viewport.scrollLeft()+e.col.width):m&&r.$viewport.scrollLeft(0),h||(i+=1))}var w;w=e.configGroups.length>0?r.rowFactory.parsedData.filter(function(e){return!e.isAggRow}):r.filteredRows;var C=0;if(0!=c&&(38==l||13==l&&o.shiftKey||9==l&&o.shiftKey&&s)?C=-1:c!=w.length-1&&(40==l||13==l&&!o.shiftKey||9==l&&a)&&(C=1),C){var b=w[c+C];b.beforeSelectionChange(b,o)&&(b.continueSelection(o),e.$emit("ngGridEventDigestGridParent"),e.selectionProvider.lastClickedRow.renderedRowIndex>=e.renderedRows.length-t-2?r.$viewport.scrollTop(r.$viewport.scrollTop()+e.rowHeight):t+2>=e.selectionProvider.lastClickedRow.renderedRowIndex&&r.$viewport.scrollTop(r.$viewport.scrollTop()-e.rowHeight))}return e.enableCellSelection&&setTimeout(function(){e.domAccessProvider.focusCellElement(e,e.renderedColumns.indexOf(g[i]))},3),!1};String.prototype.trim||(String.prototype.trim=function(){return this.replace(/^\s+|\s+$/g,"")}),Array.prototype.indexOf||(Array.prototype.indexOf=function(e){var n=this.length>>>0,t=Number(arguments[1])||0;for(t=0>t?Math.ceil(t):Math.floor(t),0>t&&(t+=n);n>t;t++)if(t in this&&this[t]===e)return t;return-1}),Array.prototype.filter||(Array.prototype.filter=function(e){var n=Object(this),t=n.length>>>0;if("function"!=typeof e)throw new TypeError;for(var o=[],r=arguments[1],i=0;t>i;i++)if(i in n){var l=n[i];e.call(r,l,i,n)&&o.push(l)}return o}),v.filter("checkmark",function(){return function(e){return e?"✔":"✘"}}),v.filter("ngColumns",function(){return function(e){return e.filter(function(e){return!e.isAggCol})}}),h.factory("$domUtilityService",["$utilityService",function(e){var t={},o={},r=function(){var e=n("
");e.appendTo("body"),e.height(100).width(100).css("position","absolute").css("overflow","scroll"),e.append('
'),t.ScrollH=e.height()-e[0].clientHeight,t.ScrollW=e.width()-e[0].clientWidth,e.empty(),e.attr("style",""),e.append('M'),t.LetterW=e.children().first().width(),e.remove()};return t.eventStorage={},t.AssignGridContainers=function(e,o,r){r.$root=n(o),r.$topPanel=r.$root.find(".ngTopPanel"),r.$groupPanel=r.$root.find(".ngGroupPanel"),r.$headerContainer=r.$topPanel.find(".ngHeaderContainer"),e.$headerContainer=r.$headerContainer,r.$headerScroller=r.$topPanel.find(".ngHeaderScroller"),r.$headers=r.$headerScroller.children(),r.$viewport=r.$root.find(".ngViewport"),r.$canvas=r.$viewport.find(".ngCanvas"),r.$footerPanel=r.$root.find(".ngFooterPanel"),e.$watch(function(){return r.$viewport.scrollLeft()},function(e){return r.$headerContainer.scrollLeft(e)}),t.UpdateGridLayout(e,r)},t.getRealWidth=function(e){var t=0,o={visibility:"hidden",display:"block"},r=e.parents().andSelf().not(":visible");return n.swap(r[0],o,function(){t=e.outerWidth()}),t},t.UpdateGridLayout=function(e,n){var o=n.$viewport.scrollTop();n.elementDims.rootMaxW=n.$root.width(),n.$root.is(":hidden")&&(n.elementDims.rootMaxW=t.getRealWidth(n.$root)),n.elementDims.rootMaxH=n.$root.height(),n.refreshDomSizes(),e.adjustScrollTop(o,!0)},t.numberOfGrids=0,t.BuildStyles=function(o,r,i){var l,a=r.config.rowHeight,s=r.$styleSheet,c=r.gridId,g=o.columns,d=0;s||(s=n("#"+c),s[0]||(s=n("