From 6b8675b75ed70ad990f8b943b5663d7beffc9dd4 Mon Sep 17 00:00:00 2001 From: Bill Erickson Date: Tue, 11 Mar 2014 20:32:14 -0400 Subject: [PATCH] web staff: autogrid experiments Signed-off-by: Bill Erickson --- Open-ILS/src/templates/staff/parts/t_autogrid.tt2 | 10 +- Open-ILS/src/templates/staff/test/t_autogrid.tt2 | 38 ++-- .../web/js/ui/default/staff/services/autogrid.js | 202 ++++++++++++++++----- Open-ILS/web/js/ui/default/staff/services/ui.js | 15 ++ Open-ILS/web/js/ui/default/staff/test/app.js | 11 +- 5 files changed, 201 insertions(+), 75 deletions(-) diff --git a/Open-ILS/src/templates/staff/parts/t_autogrid.tt2 b/Open-ILS/src/templates/staff/parts/t_autogrid.tt2 index be9f99d5e6..8a781b3ab5 100644 --- a/Open-ILS/src/templates/staff/parts/t_autogrid.tt2 +++ b/Open-ILS/src/templates/staff/parts/t_autogrid.tt2 @@ -1,12 +1,15 @@ -
+
@@ -20,7 +23,8 @@
- {{column.label}} + {{column.label}}
- {{dataList.fieldValue(item, column.name)}} + {{dataList.fieldValue(item, column.name) | egGridvalueFilter:column}}
diff --git a/Open-ILS/src/templates/staff/test/t_autogrid.tt2 b/Open-ILS/src/templates/staff/test/t_autogrid.tt2 index 12729b28e5..946e2bb0e0 100644 --- a/Open-ILS/src/templates/staff/test/t_autogrid.tt2 +++ b/Open-ILS/src/templates/staff/test/t_autogrid.tt2 @@ -1,18 +1,28 @@ +

AutoGrid Explicit Fields

-

AutoGrid Test

-
- -
+ + + + + + + + +

AutoGrid w/ Auto Fields

+ + + diff --git a/Open-ILS/web/js/ui/default/staff/services/autogrid.js b/Open-ILS/web/js/ui/default/staff/services/autogrid.js index dccfb0c7e7..4632c4ed6d 100644 --- a/Open-ILS/web/js/ui/default/staff/services/autogrid.js +++ b/Open-ILS/web/js/ui/default/staff/services/autogrid.js @@ -1,17 +1,41 @@ -angular.module('egGridMod', ['egCoreMod', 'egListMod']) +angular.module('egGridMod', ['egCoreMod', 'egListMod', 'egUiMod']) .directive('egGrid', function() { return { - restrict : 'A', + restrict : 'AE', transclude : true, scope : { idlClass : '@', + + // points to a structure in the calling scope which defines + // a PCRUD-compliant query. query : '=', - sort : '@', - autoFields : '=' + + // if true, grid columns are derived from all non-virtual + // fields on the base idlClass + autoFields : '=', + + // optional, custom data retrieval function + dataFetcher : '=', + + // grid preferences will be stored / retrieved with this key + persistKey : '@', + + // if true, use the scroll CSS to force a vertical height + // and scroll bar + isScroll : '=' + }, + + link : function(scope, element, attrs) { + // link() is called after page compilation, which means our + // eg-grid-field's have been parsed and loaded. Now it's + // safe to perform our initial page load. + scope.fetchData(); }, - templateUrl : '/eg/staff/parts/t_autogrid', // TODO: abs url + + templateUrl : '/eg/staff/parts/t_autogrid', // TODO: avoid abs url + controller : function($scope, $timeout, egIDL, egAuth, egNet, egList) { // TODO: reqs list var self = this; @@ -21,14 +45,33 @@ angular.module('egGridMod', ['egCoreMod', 'egListMod']) $scope.dataList = egList.create(); - this.addField = function(fieldScope) { + // column-header click quick sort + $scope.sortOn = function(col_name) { + if ($scope.sort && $scope.sort.length && + $scope.sort[0] == col_name) { + var blob = {}; + blob[col_name] = 'desc'; + $scope.sort = [blob]; + } else { + $scope.sort = [col_name]; + } + $scope.fetchData(); + } + + /** + * Adds a column from an eg-grid-field or directly from + * an IDL field via compileAutoFields. + */ + this.addColumn = function(fieldSpec) { var field = { - name : fieldScope.name, - label : fieldScope.label, - path : fieldScope.path, - display : (fieldScope.display === false) ? false : true + name : fieldSpec.name, + label : fieldSpec.label, + path : fieldSpec.path, + datatype : fieldSpec.datatype, + display : (fieldSpec.display === false) ? false : true }; - self.applyFieldLabel(field); + if (!field.path) field.path = field.name; + field = self.absorbField(field); $scope.dataList.addColumn(field); } @@ -58,31 +101,33 @@ angular.module('egGridMod', ['egCoreMod', 'egListMod']) } } } + + // once we exceeed the number of display fields, + // the others are added as hidden fields, accessible + // via the column picker. if ($scope.dataList.allColumns.length >= self.maxFieldCount) { console.log('setting to false ' + field.name); field.display = false; } - self.addField(field); + self.addColumn(field); } ); } - this.applyFieldLabel = function(field) { - if (field.label) return; // label already applied - - var class_obj = egIDL.classes[$scope.idlClass]; - if (!field.path) field.path = field.name; - var path_parts = field.path.split(/\./); + // given a base class and a dotpath, find the IDL field + this.getIDLFieldFromPath = function(idlClass, path) { + var class_obj = egIDL.classes[idlClass]; + var path_parts = path.split(/\./); // note: use of for() is intentional for early exit - var field_obj; + var idl_field; for (var path_idx in path_parts) { var part = path_parts[path_idx]; // find the field object matching the path component for (var field_idx in class_obj.fields) { if (class_obj.fields[field_idx].name == part) { - field_obj = class_obj.fields[field_idx]; + idl_field = class_obj.fields[field_idx]; break; } } @@ -90,23 +135,74 @@ angular.module('egGridMod', ['egCoreMod', 'egListMod']) // unless we're at the end of the list, this field should // link to another class. - if (field_obj && field_obj['class'] && ( - field_obj.datatype == 'link' || - field_obj.datatype == 'org_unit')) { - class_obj = egIDL.classes[field_obj['class']]; + if (idl_field && idl_field['class'] && ( + idl_field.datatype == 'link' || + idl_field.datatype == 'org_unit')) { + class_obj = egIDL.classes[idl_field['class']]; } else { if (path_idx < (path_parts.length - 1)) { // we ran out of classes to hop through before // we ran out of path components - console.error("egGrid: invalid IDL path: " + field.path); + console.error("egGrid: invalid IDL path: " + path); } } } - field.label = field_obj ? field_obj.label : field.name; + return idl_field; + } + + /** + * Looks for the matching IDL field to extract the label + * and datattype as needed. + * Creates a local copy of the field for our internal + * machinations. + */ + this.absorbField = function(field) { + + // start by cloning the field so we can flesh it out. + // note: aungular.copy won't work, because 'field' may + // be a $scope object. + var new_field = { + name : field.name, + label : field.label, + path : field.path || field.name, + display : (field.display === false) ? false : true + }; + + // lookup the matching IDL field + var idl_field = field.datatype ? field : + self.getIDLFieldFromPath($scope.idlClass, field.path); + + // No matching IDL field. Caller has gone commando. + // Nothing left to do. + if (!idl_field) return new_field; + + new_field.datatype = idl_field.datatype; + + if (field.label) { + // caller-provided label + new_field.label = field.label; + } else { + if (idl_field.label) { + new_field.label = idl_field.label; + } else { + new_field.label = new_field.name; + } + } + + return new_field; } - this.fetchData = function() { + /** + * For stock grids, makes a flattened_search call to retrieve + * the requested values. + * For non-stock grids, calls the external data fetcher + */ + $scope.fetchData = function() { + $scope.dataList.resetPageData(); + + if (self.dataFetcher) + return self.dataFetcher(); if (!$scope.query) { console.error("egGrid requires a query"); @@ -133,29 +229,13 @@ angular.module('egGridMod', ['egCoreMod', 'egListMod']) egAuth.token(), $scope.idlClass, queryFields, $scope.query, { sort : $scope.sort, - limit : 10, // TODO + limit : 20, // TODO offset : 0 // TODO } - ).then(null, null, function(item) { - $scope.dataList.items.push(item); - } - ); - } - - // Don't call fetchData until we know what the fields are - // TODO: this is a hack which polls to see if our eg-grid-field's - // have been processed. There has to be a better way... - readycheck = 0; - this.checkReadyForDraw = function() { - if ($scope.autoFields || $scope.dataList.allColumns.length) { - self.fetchData(); - } else { - if (++readycheck > 10000) return; // failsafe, no fields defined - // check again after the next $digest loop - $scope.$evalAsync(function() {self.checkReadyForDraw()}); - } + ).then(null, null, function(item) { + $scope.dataList.items.push(item); + }); } - this.checkReadyForDraw(); } }; }) @@ -168,14 +248,36 @@ angular.module('egGridMod', ['egCoreMod', 'egListMod']) .directive('egGridField', function() { return { require : '^egGrid', - restrict : 'A', + restrict : 'AE', transclude : true, scope : {name : '@', path : '@', label : '@'}, template : '
', // NOOP template link : function(scope, element, attrs, egGridCtrl) { - egGridCtrl.addField(scope); + egGridCtrl.addColumn(scope); } }; -}); +}) + +/** + * Translates bare IDL object values into display values. + * 1. Passes dates through the angular date filter + * 2. Translates bools to Booleans so the browser can display translated + * value. (Though we could manually translate instead..) + * Others likely to follow... + */ +.filter('egGridvalueFilter', ['$filter', function($filter) { + return function(value, item) { + switch(item.datatype) { + case 'bool': + // Browser will translate true/false for us + return Boolean(value == 't'); + case 'timestamp': + // canned angular date filter FTW + return $filter('date')(value); + default: + return value; + } + } +}]); diff --git a/Open-ILS/web/js/ui/default/staff/services/ui.js b/Open-ILS/web/js/ui/default/staff/services/ui.js index 4eebe7d36f..3670b53683 100644 --- a/Open-ILS/web/js/ui/default/staff/services/ui.js +++ b/Open-ILS/web/js/ui/default/staff/services/ui.js @@ -133,3 +133,18 @@ function($timeout, $parse) { return service; }) + +/* + * http://stackoverflow.com/questions/15731634/how-do-i-handle-right-click-events-in-angular-js + */ +.directive('ngRightClick', function($parse) { + return function(scope, element, attrs) { + var fn = $parse(attrs.ngRightClick); + element.bind('contextmenu', function(event) { + scope.$apply(function() { + event.preventDefault(); + fn(scope, {$event:event}); + }); + }); + }; +}); diff --git a/Open-ILS/web/js/ui/default/staff/test/app.js b/Open-ILS/web/js/ui/default/staff/test/app.js index 28ca01c0ef..b9917c8060 100644 --- a/Open-ILS/web/js/ui/default/staff/test/app.js +++ b/Open-ILS/web/js/ui/default/staff/test/app.js @@ -15,23 +15,18 @@ angular.module('egTestApp', ['ngRoute', 'ui.bootstrap', //$routeProvider.otherwise({redirectTo : '/circ/patron/search'}); }) -.controller('egTestCtrl', ['$scope', function($scope) { - /* - $scope.$on('$viewContentLoaded', function() { - console.debug("viewContentLoaded"); - }); - */ +.controller('egTestCtrl', ['$scope', '$rootScope', '$timeout', + function($scope, $rootScope, $timeout) { }]) .controller('TestGridCtrl', ['$scope', function($scope) { + var self = this; console.log('TestGridCtrl'); $scope.testGridQuery = {id : {'<>' : null}}; $scope.testGridSort = ['depth', 'parent_ou_id', 'name'] - - //$scope.fmClass="aou"; }]); -- 2.11.0