From 65e49fd59a165672c71738e4ba7df553e7f6e673 Mon Sep 17 00:00:00 2001 From: StrangelyTyped Date: Sat, 6 Aug 2016 21:51:09 +0100 Subject: [PATCH] feat(core): Allow binding a column to the row entity itself This allows grids to be created for an array of primitives, such as an array of Strings, and bind a column to each value in the data array instead of having to bind to a property within each value. Satisfies #5359 --- misc/tutorial/323_more_binding.ngdoc | 119 +++++++++++++++++++++++ src/js/core/constants.js | 3 +- src/js/core/factories/Grid.js | 6 +- src/js/core/factories/GridRow.js | 8 +- test/unit/core/factories/Grid.spec.js | 24 +++++ test/unit/core/factories/GridRow.spec.js | 8 ++ 6 files changed, 164 insertions(+), 4 deletions(-) create mode 100644 misc/tutorial/323_more_binding.ngdoc diff --git a/misc/tutorial/323_more_binding.ngdoc b/misc/tutorial/323_more_binding.ngdoc new file mode 100644 index 0000000000..5fdfe90416 --- /dev/null +++ b/misc/tutorial/323_more_binding.ngdoc @@ -0,0 +1,119 @@ +@ngdoc overview +@name Tutorial: 323 More Binding examples +@description + +UI-Grid can also bind be to a one-dimensional array of primitives - in this case using `uiGridConstants.ENTITY_BINDING` will use the entire entry in the data array as the value for the cell instead of a field therein. This is useful if the data is an array of strings, or also if a cell filter needs access to multiple fields within each row object. + +@example + + + var app = angular.module('app', ['ngTouch', 'ui.grid', 'ui.grid.edit']); + + app.controller('OneDimensionCtrl', ['$scope', 'uiGridConstants', function ($scope, uiGridConstants) { + + $scope.gridOptions = { + enableSorting: true, + columnDefs: [ + { name:'Name', field: uiGridConstants.ENTITY_BINDING } + ], + data : [ + "John Rogers", + "David Michaels", + "Andrew Johnson", + "Donald McDonald" + ] + }; + }]); + + + app.filter('calculatePercentage', function () { + return function (input, resultField, maxField) { + return Math.floor((input[resultField] * 100) / input[maxField]) + "%"; + }; + }); + app.controller('ComplexFilterCtrl', ['$scope', 'uiGridConstants', function ($scope, uiGridConstants) { + + $scope.gridOptions = { + enableSorting: true, + columnDefs: [ + { name:'Exam', field: 'examName' }, + { name:'Possible Score', field: 'maxScore' }, + { name:'Your Score', field: 'actualScore' }, + { name:'Percentage', field: uiGridConstants.ENTITY_BINDING, cellFilter: 'calculatePercentage:"actualScore":"maxScore"', sortCellFiltered: true, enableCellEdit: false } + ], + data : [ + { + examName: 'Basic Trig', + maxScore: 105, + actualScore: 77 + }, + { + examName: 'Graph Theory', + maxScore: 85, + actualScore: 82 + }, + { + examName: 'Counting', + maxScore: 40, + actualScore: 12 + }, + ] + }; + }]); + + +
+

Array of Strings Example

+
+
+
+

Complex CellFilter Example

+
+
+
+ + .grid { + width: 500px; + height: 250px; + } + + + var gridTestUtils = require('../../test/e2e/gridTestUtils.spec.js'); + it('grid should have four visible rows and one column', function () { + gridTestUtils.expectRowCount( 'grid1', 4 ); + gridTestUtils.expectHeaderColumnCount( 'grid1', 1 ); + }); + + it('headers as specified', function () { + gridTestUtils.expectHeaderCellValueMatch( 'grid1', 0, 'Name' ); + }); + + it('row values should be as expected', function () { + gridTestUtils.expectRowValuesMatch( 'grid1', 0, [ 'John Rogers' ]); + gridTestUtils.expectRowValuesMatch( 'grid1', 1, [ 'David Michaels' ]); + gridTestUtils.expectRowValuesMatch( 'grid1', 2, [ 'Andrew Johnson' ]); + gridTestUtils.expectRowValuesMatch( 'grid1', 3, [ 'Donald McDonald' ]); + }); + + + var gridTestUtils = require('../../test/e2e/gridTestUtils.spec.js'); + it('grid should have four visible rows and 4 columns', function () { + gridTestUtils.expectRowCount( 'grid2', 4 ); + gridTestUtils.expectHeaderColumnCount( 'grid2', 4 ); + }); + + it('headers as specified', function () { + gridTestUtils.expectHeaderCellValueMatch( 'grid2', 0, 'Exam' ); + gridTestUtils.expectHeaderCellValueMatch( 'grid2', 1, 'Possible Exam' ); + gridTestUtils.expectHeaderCellValueMatch( 'grid2', 2, 'Actual Exam' ); + gridTestUtils.expectHeaderCellValueMatch( 'grid2', 3, 'Percentage' ); + }); + + it('row values should be as expected', function () { + gridTestUtils.expectRowValuesMatch( 'grid2', 0, [ 'Basic Trig', '105', '77', '73%' ]); + gridTestUtils.expectRowValuesMatch( 'grid2', 1, [ 'Graph Theory', '85', '82', '96%' ]); + gridTestUtils.expectRowValuesMatch( 'grid2', 2, [ 'Counting', '40', '12', '30%' ]); + }); + +
+ diff --git a/src/js/core/constants.js b/src/js/core/constants.js index a4b99a707c..156b9340f9 100644 --- a/src/js/core/constants.js +++ b/src/js/core/constants.js @@ -15,6 +15,7 @@ APOS_REGEXP: /'/g, BRACKET_REGEXP: /^(.*)((?:\s*\[\s*\d+\s*\]\s*)|(?:\s*\[\s*"(?:[^"\\]|\\.)*"\s*\]\s*)|(?:\s*\[\s*'(?:[^'\\]|\\.)*'\s*\]\s*))(.*)$/, COL_CLASS_PREFIX: 'ui-grid-col', + ENTITY_BINDING: '$$this', events: { GRID_SCROLL: 'uiGridScroll', COLUMN_MENU_SHOWN: 'uiGridColMenuShown', @@ -112,4 +113,4 @@ } }); -})(); \ No newline at end of file +})(); diff --git a/src/js/core/factories/Grid.js b/src/js/core/factories/Grid.js index 8ae914a1bb..016213906a 100644 --- a/src/js/core/factories/Grid.js +++ b/src/js/core/factories/Grid.js @@ -954,7 +954,11 @@ angular.module('ui.grid') * @param {GridColumn} col col object */ Grid.prototype.getQualifiedColField = function (col) { - return 'row.entity.' + gridUtil.preEval(col.field); + var base = 'row.entity'; + if ( col.field === uiGridConstants.ENTITY_BINDING ) { + return base; + } + return gridUtil.preEval(base + '.' + col.field); }; /** diff --git a/src/js/core/factories/GridRow.js b/src/js/core/factories/GridRow.js index 579bcef58c..c628966d39 100644 --- a/src/js/core/factories/GridRow.js +++ b/src/js/core/factories/GridRow.js @@ -1,7 +1,7 @@ (function(){ angular.module('ui.grid') -.factory('GridRow', ['gridUtil', function(gridUtil) { +.factory('GridRow', ['gridUtil', 'uiGridConstants', function(gridUtil, uiGridConstants) { /** * @ngdoc function @@ -94,7 +94,11 @@ angular.module('ui.grid') * @returns {string} resulting name that can be evaluated against a row */ GridRow.prototype.getEntityQualifiedColField = function(col) { - return gridUtil.preEval('entity.' + col.field); + var base = 'entity'; + if ( col.field === uiGridConstants.ENTITY_BINDING ) { + return base; + } + return gridUtil.preEval(base + '.' + col.field); }; diff --git a/test/unit/core/factories/Grid.spec.js b/test/unit/core/factories/Grid.spec.js index 74e27a75c4..53732c79b6 100644 --- a/test/unit/core/factories/Grid.spec.js +++ b/test/unit/core/factories/Grid.spec.js @@ -568,6 +568,30 @@ describe('Grid factory', function () { }); + it('should bind correctly to $$this', function() { + var colDefs = [ + {name: 'thisProp', field: '$$this'} + ]; + var grid = new Grid({ id: 1, columnDefs:colDefs }); + var data = [ + "abc", + "def" + ]; + var rows = [ + new GridRow(data[0], 1, grid), + new GridRow(data[1], 2, grid) + ]; + + grid.buildColumns(); + grid.modifyRows(data); + + expect(grid.getCellValue(rows[0], grid.getColumn('thisProp'))).toBe('abc'); + expect(grid.getCellValue(rows[1], grid.getColumn('thisProp'))).toBe('def'); + + expect(grid.getCellDisplayValue(rows[0], grid.getColumn('thisProp'))).toBe('abc'); + expect(grid.getCellDisplayValue(rows[1], grid.getColumn('thisProp'))).toBe('def'); + }); + it('should apply angularjs filters', function(){ var colDefs = [ {displayName:'date', field:'dateProp', cellFilter: 'date:"yyyy-MM-dd"'}, diff --git a/test/unit/core/factories/GridRow.spec.js b/test/unit/core/factories/GridRow.spec.js index e59829fb64..923a69de78 100644 --- a/test/unit/core/factories/GridRow.spec.js +++ b/test/unit/core/factories/GridRow.spec.js @@ -39,6 +39,14 @@ describe('GridRow factory', function () { expect(gridRow.getQualifiedColField(col)).toBe('row.entity[\'simpleProp\']'); }); + it('binds correctly to $$this', function() { + var gridRow = new GridRow(entity,0,grid); + var col = { + field: '$$this' + }; + expect(gridRow.getQualifiedColField(col)).toBe('row.entity'); + }); + });