From a547a52e7b699ffd33bd4c5eb661b22d4c066b39 Mon Sep 17 00:00:00 2001 From: Brian Hann Date: Fri, 19 Dec 2014 14:25:34 -0600 Subject: [PATCH] feat(uiGridRow): Allow dynamic row templates The uiGridRow directive will now watch the row's compiledElementFn property. If it changes the grid will recompile the element and swap it out. This appears to function pretty darn quickly during vertical scrolling. Also added unit tests to cover swapping templates using rows processors. --- misc/demo/grid-directive.html | 2 +- src/js/core/directives/ui-grid-row.js | 21 +++- src/js/core/services/gridClassFactory.js | 46 ++++++++- test/unit/core/directives/ui-grid-row.spec.js | 97 +++++++++++++++++++ 4 files changed, 161 insertions(+), 5 deletions(-) create mode 100644 test/unit/core/directives/ui-grid-row.spec.js diff --git a/misc/demo/grid-directive.html b/misc/demo/grid-directive.html index dac49a0733..5331e2e3d7 100644 --- a/misc/demo/grid-directive.html +++ b/misc/demo/grid-directive.html @@ -35,7 +35,7 @@

Grid

-
+
diff --git a/src/js/core/directives/ui-grid-row.js b/src/js/core/directives/ui-grid-row.js index c513668a18..3efd036b98 100644 --- a/src/js/core/directives/ui-grid-row.js +++ b/src/js/core/directives/ui-grid-row.js @@ -23,10 +23,25 @@ $scope.grid = uiGridCtrl.grid; $scope.colContainer = containerCtrl.colContainer; - grid.getRowTemplateFn.then(function (templateFn) { - templateFn($scope, function(clonedElement, scope) { - $elm.replaceWith(clonedElement); + // Function for attaching the template to this scope + function compileTemplate() { + var compiledElementFn = $scope.row.compiledElementFn; + + compiledElementFn($scope, function (clonedElement, scope) { + $elm.empty().append(clonedElement); }); + } + + // Initially attach the compiled template to this scope + compileTemplate(); + + // If the row's compiled element function changes, we need to replace this element's contents with the new compiled template + $scope.$watch('row.compiledElementFn', function (newFunc, oldFunc) { + if (newFunc !== oldFunc) { + newFunc($scope, function (clonedElement, scope) { + $elm.empty().append(clonedElement); + }); + } }); }, post: function($scope, $elm, $attrs, controllers) { diff --git a/src/js/core/services/gridClassFactory.js b/src/js/core/services/gridClassFactory.js index 887e6577ef..a48227cddd 100644 --- a/src/js/core/services/gridClassFactory.js +++ b/src/js/core/services/gridClassFactory.js @@ -43,6 +43,9 @@ grid.registerColumnBuilder(service.defaultColumnBuilder); + // Row builder for custom row templates + grid.registerRowBuilder(service.rowTemplateAssigner); + // Reset all rows to visible initially grid.registerRowsProcessor(function allRowsVisible(rows) { rows.forEach(function (row) { @@ -179,8 +182,49 @@ col.compiledElementFnDefer = $q.defer(); return $q.all(templateGetPromises); - } + }, + + rowTemplateAssigner: function rowTemplateAssigner(row) { + var grid = this; + + // Row has no template assigned to it + if (!row.rowTemplate) { + // Use the default row template from the grid + row.rowTemplate = grid.options.rowTemplate; + // Use the grid's function for fetching the compiled row template function + row.getRowTemplateFn = grid.getRowTemplateFn; + + // Get the compiled row template function... + grid.getRowTemplateFn.then(function (rowTemplateFn) { + // And assign it to the row + row.compiledElementFn = rowTemplateFn; + }); + } + // Row has its own template assigned + else { + // Create a promise for the compiled row template function + var perRowTemplateFnPromise = $q.defer(); + row.getRowTemplateFn = perRowTemplateFnPromise.promise; + + // Get the row template + gridUtil.getTemplate(row.rowTemplate) + .then(function (template) { + // Compile the template + var rowTemplateFn = $compile(template); + + // Assign the compiled template function to this row + row.compiledElementFn = rowTemplateFn; + + // Resolve the compiled template function promise + perRowTemplateFnPromise.resolve(rowTemplateFn); + }, + function (res) { + // Todo handle response error here? + throw new Error("Couldn't fetch/use row template '" + row.rowTemplate + "'"); + }); + } + } }; //class definitions (moved to separate factories) diff --git a/test/unit/core/directives/ui-grid-row.spec.js b/test/unit/core/directives/ui-grid-row.spec.js new file mode 100644 index 0000000000..e0eba78515 --- /dev/null +++ b/test/unit/core/directives/ui-grid-row.spec.js @@ -0,0 +1,97 @@ +ddescribe('uiGridRow', function () { + var grid, data, columnDefs, $scope, $compile, $document, recompile, uiGridConstants, GridRow, gridUtil; + + data = [ + { "name": "Bob", "age": 35 }, + { "name": "Bill", "age": 25 }, + { "name": "Sam", "age": 17 }, + { "name": "Jane", "age": 19 } + ]; + + columnDefs = [ + { name: 'name' }, + { name: 'age' } + ]; + + beforeEach(module('ui.grid')); + + beforeEach(inject(function (_$compile_, $rootScope, _$document_, _uiGridConstants_, _GridRow_, _gridUtil_) { + $scope = $rootScope; + $compile = _$compile_; + $document = _$document_; + uiGridConstants = _uiGridConstants_; + GridRow = _GridRow_; + gridUtil = _gridUtil_; + + $scope.gridOpts = { + columnDefs: columnDefs, + data: data, + onRegisterApi: function( gridApi ){ $scope.gridApi = gridApi; } + }; + + $scope.extScope = 'test'; + + recompile = function () { + grid = angular.element('
'); + + $compile(grid)($scope); + + $scope.$digest(); + }; + + recompile(); + })); + + describe('with different row templates', function () { + beforeEach(inject(function($templateCache) { + $templateCache.put('customRowTemplate', '
The name is: {{ row.entity.name }}
'); + + $scope.gridApi.grid.registerRowsProcessor(function alterTemplates(rows, cols) { + var grid = this; + + rows.forEach(function (row) { + if (row.entity.name === 'Sam') { + row.rowTemplate = 'customRowTemplate'; + gridUtil.getTemplate(row.rowTemplate) + .then(function (template) { + row.compiledElementFn = $compile(template); + }); + } + }); + + return rows; + }); + + $scope.gridApi.grid.refresh(); + $scope.$digest(); + })); + + it('should allow rows to compile with different templates', function() { + // The third row in the template should have a different template + var thirdRow = $(grid).find('.ui-grid-row:nth-child(3)'); + + expect(thirdRow.text()).toEqual('The name is: Sam'); + }); + + it('should change templates properly after a sort', function () { + var refreshed = false; + runs(function () { + $scope.gridApi.grid.sortColumn($scope.gridApi.grid.columns[0], uiGridConstants.ASC) + .then(function () { + $scope.gridApi.grid.refresh(); + refreshed = true; + }); + + $scope.$digest(); + }); + + waitsFor(function () { return refreshed; }, 10000); + + runs(function () { + var fourthRow = $(grid).find('.ui-grid-row:nth-child(4)'); + + expect(fourthRow.text()).toEqual('The name is: Sam'); + }); + }); + }); +}); \ No newline at end of file