templateUrl : '/eg/staff/share/t_autogrid',
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.
-
- // load auto fields after eg-grid-field's so they are not clobbered
- scope.handleAutoFields();
- scope.collect();
-
- scope.grid_element = element;
- $(element)
- .find('.eg-grid-content-body')
- .bind('contextmenu', scope.showActionContextMenu);
+
+ // Give the grid config loading steps time to fetch the
+ // workstation setting and apply columns before loading data.
+ var loadPromise = scope.configLoadPromise || $q.when();
+ loadPromise.then(function() {
+
+ // load auto fields after eg-grid-field's so they are not clobbered
+ scope.handleAutoFields();
+ scope.collect();
+
+ scope.grid_element = element;
+
+ if(!attrs.id){
+ $(element).attr('id', attrs.persistKey);
+ }
+
+ $(element)
+ .find('.eg-grid-content-body')
+ .bind('contextmenu', scope.showActionContextMenu);
+ });
},
controller : [
var stored_limit = 0;
if ($scope.showPagination) {
+ // localStorage of grid limits is deprecated. Limits
+ // are now stored along with the columns configuration.
+ // Values found in localStorage will be migrated upon
+ // config save.
if (grid.persistKey) {
var stored_limit = Number(
egCore.hatch.getLocalItem('eg.grid.' + grid.persistKey + '.limit')
grid.applyControlFunctions();
- grid.loadConfig().then(function() {
+ $scope.configLoadPromise = grid.loadConfig().then(function() {
// link columns to scope after loadConfig(), since it
// replaces the columns array.
$scope.columns = grid.columnsProvider.columns;
return grid.getSelectedItems()
}
+ controls.selectItemsByValue = function(c,v) {
+ return grid.selectItemsByValue(c,v)
+ }
+
controls.allItems = function() {
return $scope.items;
}
grid.collect();
}
+ controls.prepend = function(limit) {
+ grid.prepend(limit);
+ }
+
controls.setLimit = function(limit,forget) {
- if (!forget && grid.persistKey)
- egCore.hatch.setLocalItem('eg.grid.' + grid.persistKey + '.limit', limit);
grid.limit = limit;
+ if (!forget && grid.persistKey) {
+ $scope.saveConfig();
+ }
}
controls.getLimit = function() {
return grid.limit;
}
grid.dataProvider.refresh = controls.refresh;
+ grid.dataProvider.prepend = controls.prepend;
grid.controls = controls;
}
}
// only store information about visible columns.
- var conf = grid.columnsProvider.columns.filter(
+ var cols = grid.columnsProvider.columns.filter(
function(col) {return Boolean(col.visible) });
// now scrunch the data down to just the needed info
- conf = conf.map(function(col) {
+ cols = cols.map(function(col) {
var c = {name : col.name}
// Apart from the name, only store non-default values.
// No need to store col.visible, since that's implicit
return c;
});
+ var conf = {
+ version: 2,
+ limit: grid.limit,
+ columns: cols
+ };
+
egCore.hatch.setItem('eg.grid.' + grid.persistKey, conf)
.then(function() {
// Save operation performed from the grid configuration UI.
// Hide the configuration UI and re-draw w/ sort applied
if ($scope.showGridConf)
$scope.toggleConfDisplay();
+
+ // Once a version-2 grid config is saved (with limit
+ // included) we can remove the local limit pref.
+ egCore.hatch.removeLocalItem(
+ 'eg.grid.' + grid.persistKey + '.limit');
});
}
var columns = grid.columnsProvider.columns;
var new_cols = [];
- angular.forEach(conf, function(col) {
+ if (Array.isArray(conf)) {
+ console.debug(
+ 'upgrading version 1 grid config to version 2');
+ conf = {
+ version : 2,
+ columns : conf
+ };
+ }
+
+ if (conf.limit) {
+ grid.limit = Number(conf.limit);
+ }
+
+ angular.forEach(conf.columns, function(col) {
var grid_col = columns.filter(
function(c) {return c.name == col.name})[0];
// configuration are marked as non-visible and
// appended to the end of the new list of columns.
angular.forEach(columns, function(col) {
- var found = conf.filter(
+ var found = conf.columns.filter(
function(c) {return (c.name == col.name)})[0];
if (!found) {
col.visible = false;
$scope.limit = function(l) {
if (angular.isNumber(l)) {
- if (grid.persistKey)
- egCore.hatch.setLocalItem('eg.grid.' + grid.persistKey + '.limit', l);
grid.limit = l;
+ if (grid.persistKey) {
+ $scope.saveConfig();
+ }
}
return grid.limit
}
if (!$scope.menu_dom) $scope.menu_dom = $($scope.grid_element).find('.grid-action-dropdown')[0];
if (!$scope.action_context_parent) $scope.action_context_parent = $($scope.menu_dom).parent();
- // we need the index of the row that got right-clicked...
+ // we need the the row that got right-clicked...
var e = $event.target; // the DOM element
var s = undefined; // the angular scope for that element
- while(e){ // searching for the row so we can get its index from s.$index
+ while(e){ // searching for the row
// abort & use the browser default context menu for links (lp1669856):
if(e.tagName.toLowerCase() === 'a' && e.href){ return true; }
s = angular.element(e).scope();
if(s.hasOwnProperty('item')){ break; }
e = e.parentElement;
}
- /* $scope.items and $scope.selected indexes are "backwards" to each other.
- $scope.items counts down from the top of the list (most recent item first)
- $scope.selected counts forward as items are scanned (most recent item last)
- s.$index is for $scope.items. we need the index for $scope.selected: */
- var selectable_index = ($scope.items.length-1) - s.$index;
// select the right-clicked row if it is not already selected (lp1776557):
- if(!$scope.selected[selectable_index]){ $event.target.click(); }
+ if(!$scope.selected[grid.indexValue(s.item)]){ $event.target.click(); }
if (!$scope.action_context_showing) {
$scope.action_context_width = $($scope.menu_dom).css('width');
$scope.selected[index] = true;
}
+ // selects items by a column value, first clearing selected list.
+ // we overwrite the object so that we can watch $scope.selected
+ grid.selectItemsByValue = function(column, value) {
+ $scope.selected = {};
+ angular.forEach($scope.items, function(item) {
+ var col_value;
+ if (angular.isFunction(item[column]))
+ col_value = item[column]();
+ else
+ col_value = item[column];
+
+ if (value == col_value) $scope.selected[grid.indexValue(item)] = true
+ });
+ }
+
// selects or deselects an item, without affecting the others.
// returns true if the item is selected; false if de-selected.
// we overwrite the object so that we can watch $scope.selected
return val;
}
+ $scope.getHtmlTooltip = function(col, item) {
+ return grid.getItemTextContent(item, col);
+ }
+
/**
* Fetches all grid data and transates each item into a simple
* key-value pair of column name => text-value.
});
}
+ grid.prepend = function(limit) {
+ var ran_into_duplicate = false;
+ var sort = grid.dataProvider.sort;
+ if (sort && sort.length) {
+ // If sorting is in effect, we have no way
+ // of knowing that the new item should be
+ // visible _if the sort order is retained_.
+ // However, since the grids that do prepending in
+ // the first place are ones where we always
+ // want the new row to show up on top, we'll
+ // remove the current sort options.
+ grid.dataProvider.sort = [];
+ }
+ if (grid.offset > 0) {
+ // if we're prepending, we're forcing the
+ // offset back to zero to display the top
+ // of the list
+ grid.offset = 0;
+ grid.collect();
+ return;
+ }
+ if (grid.collecting) return; // avoid parallel collect() or prepend()
+ grid.collecting = true;
+ console.debug('egGrid.prepend() starting');
+ // Note that we can count on the most-recently added
+ // item being at offset 0 in the data provider only
+ // for arrayNotifier data sources that do not have
+ // sort options currently set.
+ grid.dataProvider.get(0, 1).then(
+ null,
+ null,
+ function(item) {
+ if (item) {
+ var newIdx = grid.indexValue(item);
+ angular.forEach($scope.items, function(existing) {
+ if (grid.indexValue(existing) == newIdx) {
+ console.debug('egGrid.prepend(): refusing to add duplicate item ' + newIdx);
+ ran_into_duplicate = true;
+ return;
+ }
+ });
+ $scope.items.unshift(item);
+ if (limit && $scope.items.length > limit) {
+ // this accommodates the checkin grid that
+ // allows the user to set a definite limit
+ // without requiring that entire collect()
+ $scope.items.length = limit;
+ }
+ if ($scope.items.length > grid.limit) {
+ $scope.items.length = grid.limit;
+ }
+ if (grid.controls.itemRetrieved)
+ grid.controls.itemRetrieved(item);
+ if ($scope.selectAll)
+ $scope.selected[grid.indexValue(item)] = true
+ }
+ }).finally(function() {
+ console.debug('egGrid.prepend() complete');
+ grid.collecting = false;
+ $scope.selected = angular.copy($scope.selected);
+ if (ran_into_duplicate) {
+ grid.collect();
+ }
+ });
+ }
+
grid.init();
}]
};
// optional hash of functions that can be imported into
// the directive's scope; meant for cases where the "compiled"
// attribute is set
- handlers : '='
+ handlers : '=',
+
+ // optional: CSS class name that we want to have for this field.
+ // Auto generated from path if nothing is passed in via eg-grid-field declaration
+ cssSelector : "@"
},
link : function(scope, element, attrs, egGridCtrl) {
}
);
+ scope.cssSelector = attrs['cssSelector'] ? attrs['cssSelector'] : "";
+
+ // auto-generate CSS selector name for field if none declared in tt2 and there's a path
+ if (scope.path && !scope.cssSelector){
+ var cssClass = 'grid' + "." + scope.path;
+ cssClass = cssClass.replace(/\./g,'-');
+ element.addClass(cssClass);
+ scope.cssSelector = cssClass;
+ }
+
// any HTML content within the field is its custom template
var tmpl = element.html();
if (tmpl && !tmpl.match(/^\s*$/))
datecontext : colSpec.datecontext,
datefilter : colSpec.datefilter,
dateonlyinterval : colSpec.dateonlyinterval,
- parentIdlClass : colSpec.parentIdlClass
+ parentIdlClass : colSpec.parentIdlClass,
+ cssSelector : colSpec.cssSelector
};
}
// Calls the grid refresh function. Once instantiated, the
// grid will replace this function with it's own refresh()
gridData.refresh = function(noReset) { }
+ gridData.prepend = function(limit) { }
if (!gridData.get) {
// returns a promise whose notify() delivers items
/**
* 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..)
+ * 2. Converts bools to translated Yes/No strings
* Others likely to follow...
*/
-.filter('egGridValueFilter', ['$filter','egCore', function($filter,egCore) {
+.filter('egGridValueFilter', ['$filter','egCore', 'egStrings', function($filter,egCore,egStrings) {
function traversePath(obj,path) {
var list = path.split('.');
for (var part in path) {
case 't' :
case '1' : // legacy
case true:
- return ''+true;
+ return egStrings.YES;
case 'f' :
case '0' : // legacy
case false:
- return ''+false;
+ return egStrings.NO;
// value may be null, '', etc.
default : return '';
}