diff --git a/src/features/edit/js/gridEdit.js b/src/features/edit/js/gridEdit.js index ca9879d51c..8c7df01879 100644 --- a/src/features/edit/js/gridEdit.js +++ b/src/features/edit/js/gridEdit.js @@ -457,8 +457,8 @@ */ module.directive('uiGridCell', - ['$compile', '$injector', '$timeout', 'uiGridConstants', 'uiGridEditConstants', 'gridUtil', '$parse', 'uiGridEditService', '$rootScope', - function ($compile, $injector, $timeout, uiGridConstants, uiGridEditConstants, gridUtil, $parse, uiGridEditService, $rootScope) { + ['$compile', '$injector', '$timeout', 'uiGridConstants', 'uiGridEditConstants', 'gridUtil', '$parse', 'uiGridEditService', '$rootScope', '$q', + function ($compile, $injector, $timeout, uiGridConstants, uiGridEditConstants, gridUtil, $parse, uiGridEditService, $rootScope, $q) { var touchstartTimeout = 500; if ($injector.has('uiGridCellNavService')) { var uiGridCellNavService = $injector.get('uiGridCellNavService'); @@ -652,6 +652,40 @@ * * */ + /** + * @ngdoc service + * @name editDropdownOptionsFunction + * @methodOf ui.grid.edit.api:ColumnDef + * @description a function returning an array of values in the format + * [ {id: xxx, value: xxx} ], which will be used to populate + * the edit dropdown. This can be used when the dropdown values are dependent on + * the backing row entity with some kind of algorithm. + * If this property is set then both editDropdownOptionsArray and + * editDropdownRowEntityOptionsArrayPath will be ignored. + * @param {object} rowEntity the options.data element that the returned array refers to + * @param {object} colDef the column that implements this dropdown + * @returns {object} an array of values in the format + * [ {id: xxx, value: xxx} ] used to populate the edit dropdown + * @example + *
+ * $scope.gridOptions = { + * columnDefs: [ + * {name: 'status', editableCellTemplate: 'ui-grid/dropdownEditor', + * editDropdownOptionsFunction: function(rowEntity, colDef) { + * if (rowEntity.foo === 'bar') { + * return [{id: 'bar1', value: 'BAR 1'}, + * {id: 'bar2', value: 'BAR 2'}, + * {id: 'bar3', value: 'BAR 3'}]; + * } else { + * return [{id: 'foo1', value: 'FOO 1'}, + * {id: 'foo2', value: 'FOO 2'}]; + * } + * }, + * editDropdownIdLabel: 'code', editDropdownValueLabel: 'status' } + * ], + *+ * + */ /** * @ngdoc property * @name editDropdownValueLabel @@ -698,20 +732,18 @@ return; } + var modelField = $scope.row.getQualifiedColField($scope.col); + if ($scope.col.colDef.editModelField) { + modelField = gridUtil.preEval('row.entity.' + $scope.col.colDef.editModelField); + } + + cellModel = $parse(modelField); - cellModel = $parse($scope.row.getQualifiedColField($scope.col)); //get original value from the cell origCellValue = cellModel($scope); html = $scope.col.editableCellTemplate; - - if ($scope.col.colDef.editModelField) { - html = html.replace(uiGridConstants.MODEL_COL_FIELD, gridUtil.preEval('row.entity.' + $scope.col.colDef.editModelField)); - } - else { - html = html.replace(uiGridConstants.MODEL_COL_FIELD, $scope.row.getQualifiedColField($scope.col)); - } - + html = html.replace(uiGridConstants.MODEL_COL_FIELD, modelField); html = html.replace(uiGridConstants.COL_FIELD, 'grid.getCellValue(row, col)'); var optionFilter = $scope.col.colDef.editDropdownFilter ? '|' + $scope.col.colDef.editDropdownFilter : ''; @@ -731,12 +763,24 @@ } html = html.replace('INPUT_TYPE', inputType); - var editDropdownRowEntityOptionsArrayPath = $scope.col.colDef.editDropdownRowEntityOptionsArrayPath; - if (editDropdownRowEntityOptionsArrayPath) { - $scope.editDropdownOptionsArray = resolveObjectFromPath($scope.row.entity, editDropdownRowEntityOptionsArrayPath); - } - else { - $scope.editDropdownOptionsArray = $scope.col.colDef.editDropdownOptionsArray; + // In order to fill dropdown options we use: + // - A function/promise or + // - An array inside of row entity if no function exists or + // - A single array for the whole column if none of the previous exists. + var editDropdownOptionsFunction = $scope.col.colDef.editDropdownOptionsFunction; + if (editDropdownOptionsFunction) { + $q.when(editDropdownOptionsFunction($scope.row.entity, $scope.col.colDef)) + .then(function(result) { + $scope.editDropdownOptionsArray = result; + }); + } else { + var editDropdownRowEntityOptionsArrayPath = $scope.col.colDef.editDropdownRowEntityOptionsArrayPath; + if (editDropdownRowEntityOptionsArrayPath) { + $scope.editDropdownOptionsArray = resolveObjectFromPath($scope.row.entity, editDropdownRowEntityOptionsArrayPath); + } + else { + $scope.editDropdownOptionsArray = $scope.col.colDef.editDropdownOptionsArray; + } } $scope.editDropdownIdLabel = $scope.col.colDef.editDropdownIdLabel ? $scope.col.colDef.editDropdownIdLabel : 'id'; $scope.editDropdownValueLabel = $scope.col.colDef.editDropdownValueLabel ? $scope.col.colDef.editDropdownValueLabel : 'value'; @@ -760,6 +804,9 @@ //stop editing when grid is scrolled var deregOnGridScroll = $scope.col.grid.api.core.on.scrollBegin($scope, function () { + if ($scope.grid.disableScrolling) { + return; + } endEdit(); $scope.grid.api.edit.raise.afterCellEdit($scope.row.entity, $scope.col.colDef, cellModel($scope), origCellValue); deregOnGridScroll(); @@ -807,7 +854,10 @@ var gridCellContentsEl = angular.element($elm.children()[0]); //remove edit element editCellScope.$destroy(); - angular.element($elm.children()[1]).remove(); + var children = $elm.children(); + for (var i = 1; i < children.length; i++) { + angular.element(children[i]).remove(); + } gridCellContentsEl.removeClass('ui-grid-cell-contents-hidden'); inEdit = false; registerBeginEditEvents(); @@ -886,7 +936,7 @@ $timeout(function () { $elm[0].focus(); //only select text if it is not being replaced below in the cellNav viewPortKeyPress - if ($scope.col.colDef.enableCellEditOnFocus || !(uiGridCtrl && uiGridCtrl.grid.api.cellNav)) { + if ($elm[0].select && ($scope.col.colDef.enableCellEditOnFocus || !(uiGridCtrl && uiGridCtrl.grid.api.cellNav))) { $elm[0].select(); } else { @@ -909,16 +959,27 @@ if (uiGridCtrl && uiGridCtrl.grid.api.cellNav) { var viewPortKeyDownUnregister = uiGridCtrl.grid.api.cellNav.on.viewPortKeyPress($scope, function (evt, rowCol) { if (uiGridEditService.isStartEditKey(evt)) { - ngModel.$setViewValue(String.fromCharCode(evt.keyCode), evt); + ngModel.$setViewValue(String.fromCharCode( typeof evt.which === 'number' ? evt.which : evt.keyCode), evt); ngModel.$render(); } viewPortKeyDownUnregister(); }); } - $elm.on('blur', function (evt) { - $scope.stopEdit(evt); + // macOS will blur the checkbox when clicked in Safari and Firefox, + // to get around this, we disable the blur handler on mousedown, + // and then focus the checkbox and re-enable the blur handler after $timeout + $elm.on('mousedown', function(evt) { + if ($elm[0].type === 'checkbox') { + $elm.off('blur', $scope.stopEdit); + $timeout(function() { + $elm.focus(); + $elm.on('blur', $scope.stopEdit); + }); + } }); + + $elm.on('blur', $scope.stopEdit); }); @@ -1065,8 +1126,8 @@ * */ module.directive('uiGridEditDropdown', - ['uiGridConstants', 'uiGridEditConstants', - function (uiGridConstants, uiGridEditConstants) { + ['uiGridConstants', 'uiGridEditConstants', '$timeout', + function (uiGridConstants, uiGridEditConstants, $timeout) { return { require: ['?^uiGrid', '?^uiGridRenderContainer'], scope: true, @@ -1081,7 +1142,10 @@ //set focus at start of edit $scope.$on(uiGridEditConstants.events.BEGIN_CELL_EDIT, function () { - $elm[0].focus(); + $timeout(function(){ + $elm[0].focus(); + }); + $elm[0].style.width = ($elm[0].parentElement.offsetWidth - 1) + 'px'; $elm.on('blur', function (evt) { $scope.stopEdit(evt);