/*! * ui-grid - v4.4.6 - 2018-04-06 * Copyright (c) 2018 ; License: MIT */ (function () { 'use strict'; /** * @ngdoc overview * @name ui.grid.rowEdit * @description * * # ui.grid.rowEdit * *
* gridApi.rowEdit.on.saveRow(scope,function(rowEntity){})
*
* and somewhere within the event handler:
*
* gridApi.rowEdit.setSavePromise( rowEntity, savePromise)
*
* @param {object} rowEntity the options.data element that was edited
* @returns {promise} Your saveRow method should return a promise, the
* promise should either be resolved (implying successful save), or
* rejected (implying an error).
*/
saveRow: function (rowEntity) {
}
}
},
methods: {
rowEdit: {
/**
* @ngdoc method
* @methodOf ui.grid.rowEdit.api:PublicApi
* @name setSavePromise
* @description Sets the promise associated with the row save, mandatory that
* the saveRow event handler calls this method somewhere before returning.
*
* gridApi.rowEdit.setSavePromise(rowEntity, savePromise)
*
* @param {object} rowEntity a data row from the grid for which a save has
* been initiated
* @param {promise} savePromise the promise that will be resolved when the
* save is successful, or rejected if the save fails
*
*/
setSavePromise: function ( rowEntity, savePromise) {
service.setSavePromise(grid, rowEntity, savePromise);
},
/**
* @ngdoc method
* @methodOf ui.grid.rowEdit.api:PublicApi
* @name getDirtyRows
* @description Returns all currently dirty rows
*
* gridApi.rowEdit.getDirtyRows(grid)
*
* @returns {array} An array of gridRows that are currently dirty
*
*/
getDirtyRows: function () {
return grid.rowEdit.dirtyRows ? grid.rowEdit.dirtyRows : [];
},
/**
* @ngdoc method
* @methodOf ui.grid.rowEdit.api:PublicApi
* @name getErrorRows
* @description Returns all currently errored rows
*
* gridApi.rowEdit.getErrorRows(grid)
*
* @returns {array} An array of gridRows that are currently in error
*
*/
getErrorRows: function () {
return grid.rowEdit.errorRows ? grid.rowEdit.errorRows : [];
},
/**
* @ngdoc method
* @methodOf ui.grid.rowEdit.api:PublicApi
* @name flushDirtyRows
* @description Triggers a save event for all currently dirty rows, could
* be used where user presses a save button or navigates away from the page
*
* gridApi.rowEdit.flushDirtyRows(grid)
*
* @returns {promise} a promise that represents the aggregate of all
* of the individual save promises - i.e. it will be resolved when all
* the individual save promises have been resolved.
*
*/
flushDirtyRows: function () {
return service.flushDirtyRows(grid);
},
/**
* @ngdoc method
* @methodOf ui.grid.rowEdit.api:PublicApi
* @name setRowsDirty
* @description Sets each of the rows passed in dataRows
* to be dirty. note that if you have only just inserted the
* rows into your data you will need to wait for a $digest cycle
* before the gridRows are present - so often you would wrap this
* call in a $interval or $timeout
*
* $interval( function() {
* gridApi.rowEdit.setRowsDirty(myDataRows);
* }, 0, 1);
*
* @param {array} dataRows the data entities for which the gridRows
* should be set dirty.
*
*/
setRowsDirty: function ( dataRows) {
service.setRowsDirty(grid, dataRows);
},
/**
* @ngdoc method
* @methodOf ui.grid.rowEdit.api:PublicApi
* @name setRowsClean
* @description Sets each of the rows passed in dataRows
* to be clean, removing them from the dirty cache and the error cache,
* and clearing the error flag and the dirty flag
*
* var gridRows = $scope.gridApi.rowEdit.getDirtyRows();
* var dataRows = gridRows.map( function( gridRow ) { return gridRow.entity; });
* $scope.gridApi.rowEdit.setRowsClean( dataRows );
*
* @param {array} dataRows the data entities for which the gridRows
* should be set clean.
*
*/
setRowsClean: function ( dataRows) {
service.setRowsClean(grid, dataRows);
}
}
}
};
grid.api.registerEventsFromObject(publicApi.events);
grid.api.registerMethodsFromObject(publicApi.methods);
grid.api.core.on.renderingComplete( scope, function ( gridApi ) {
grid.api.edit.on.afterCellEdit( scope, service.endEditCell );
grid.api.edit.on.beginCellEdit( scope, service.beginEditCell );
grid.api.edit.on.cancelCellEdit( scope, service.cancelEditCell );
if ( grid.api.cellNav ) {
grid.api.cellNav.on.navigate( scope, service.navigate );
}
});
},
defaultGridOptions: function (gridOptions) {
/**
* @ngdoc object
* @name ui.grid.rowEdit.api:GridOptions
*
* @description Options for configuring the rowEdit feature, these are available to be
* set using the ui-grid {@link ui.grid.class:GridOptions gridOptions}
*/
},
/**
* @ngdoc method
* @methodOf ui.grid.rowEdit.service:uiGridRowEditService
* @name saveRow
* @description Returns a function that saves the specified row from the grid,
* and returns a promise
* @param {object} grid the grid for which dirty rows should be flushed
* @param {GridRow} gridRow the row that should be saved
* @returns {function} the saveRow function returns a function. That function
* in turn, when called, returns a promise relating to the save callback
*/
saveRow: function ( grid, gridRow ) {
var self = this;
return function() {
gridRow.isSaving = true;
if ( gridRow.rowEditSavePromise ){
// don't save the row again if it's already saving - that causes stale object exceptions
return gridRow.rowEditSavePromise;
}
var promise = grid.api.rowEdit.raise.saveRow( gridRow.entity );
if ( gridRow.rowEditSavePromise ){
gridRow.rowEditSavePromise.then( self.processSuccessPromise( grid, gridRow ), self.processErrorPromise( grid, gridRow ));
} else {
gridUtil.logError( 'A promise was not returned when saveRow event was raised, either nobody is listening to event, or event handler did not return a promise' );
}
return promise;
};
},
/**
* @ngdoc method
* @methodOf ui.grid.rowEdit.service:uiGridRowEditService
* @name setSavePromise
* @description Sets the promise associated with the row save, mandatory that
* the saveRow event handler calls this method somewhere before returning.
*
* gridApi.rowEdit.setSavePromise(grid, rowEntity)
*
* @param {object} grid the grid for which dirty rows should be returned
* @param {object} rowEntity a data row from the grid for which a save has
* been initiated
* @param {promise} savePromise the promise that will be resolved when the
* save is successful, or rejected if the save fails
*
*/
setSavePromise: function (grid, rowEntity, savePromise) {
var gridRow = grid.getRow( rowEntity );
gridRow.rowEditSavePromise = savePromise;
},
/**
* @ngdoc method
* @methodOf ui.grid.rowEdit.service:uiGridRowEditService
* @name processSuccessPromise
* @description Returns a function that processes the successful
* resolution of a save promise
* @param {object} grid the grid for which the promise should be processed
* @param {GridRow} gridRow the row that has been saved
* @returns {function} the success handling function
*/
processSuccessPromise: function ( grid, gridRow ) {
var self = this;
return function() {
delete gridRow.isSaving;
delete gridRow.isDirty;
delete gridRow.isError;
delete gridRow.rowEditSaveTimer;
delete gridRow.rowEditSavePromise;
self.removeRow( grid.rowEdit.errorRows, gridRow );
self.removeRow( grid.rowEdit.dirtyRows, gridRow );
};
},
/**
* @ngdoc method
* @methodOf ui.grid.rowEdit.service:uiGridRowEditService
* @name processErrorPromise
* @description Returns a function that processes the failed
* resolution of a save promise
* @param {object} grid the grid for which the promise should be processed
* @param {GridRow} gridRow the row that is now in error
* @returns {function} the error handling function
*/
processErrorPromise: function ( grid, gridRow ) {
return function() {
delete gridRow.isSaving;
delete gridRow.rowEditSaveTimer;
delete gridRow.rowEditSavePromise;
gridRow.isError = true;
if (!grid.rowEdit.errorRows){
grid.rowEdit.errorRows = [];
}
if (!service.isRowPresent( grid.rowEdit.errorRows, gridRow ) ){
grid.rowEdit.errorRows.push( gridRow );
}
};
},
/**
* @ngdoc method
* @methodOf ui.grid.rowEdit.service:uiGridRowEditService
* @name removeRow
* @description Removes a row from a cache of rows - either
* grid.rowEdit.errorRows or grid.rowEdit.dirtyRows. If the row
* is not present silently does nothing.
* @param {array} rowArray the array from which to remove the row
* @param {GridRow} gridRow the row that should be removed
*/
removeRow: function( rowArray, removeGridRow ){
if (typeof(rowArray) === 'undefined' || rowArray === null){
return;
}
rowArray.forEach( function( gridRow, index ){
if ( gridRow.uid === removeGridRow.uid ){
rowArray.splice( index, 1);
}
});
},
/**
* @ngdoc method
* @methodOf ui.grid.rowEdit.service:uiGridRowEditService
* @name isRowPresent
* @description Checks whether a row is already present
* in the given array
* @param {array} rowArray the array in which to look for the row
* @param {GridRow} gridRow the row that should be looked for
*/
isRowPresent: function( rowArray, removeGridRow ){
var present = false;
rowArray.forEach( function( gridRow, index ){
if ( gridRow.uid === removeGridRow.uid ){
present = true;
}
});
return present;
},
/**
* @ngdoc method
* @methodOf ui.grid.rowEdit.service:uiGridRowEditService
* @name flushDirtyRows
* @description Triggers a save event for all currently dirty rows, could
* be used where user presses a save button or navigates away from the page
*
* gridApi.rowEdit.flushDirtyRows(grid)
*
* @param {object} grid the grid for which dirty rows should be flushed
* @returns {promise} a promise that represents the aggregate of all
* of the individual save promises - i.e. it will be resolved when all
* the individual save promises have been resolved.
*
*/
flushDirtyRows: function(grid){
var promises = [];
grid.api.rowEdit.getDirtyRows().forEach( function( gridRow ){
service.cancelTimer( grid, gridRow );
service.saveRow( grid, gridRow )();
promises.push( gridRow.rowEditSavePromise );
});
return $q.all( promises );
},
/**
* @ngdoc method
* @methodOf ui.grid.rowEdit.service:uiGridRowEditService
* @name endEditCell
* @description Receives an afterCellEdit event from the edit function,
* and sets flags as appropriate. Only the rowEntity parameter
* is processed, although other params are available. Grid
* is automatically provided by the gridApi.
* @param {object} rowEntity the data entity for which the cell
* was edited
*/
endEditCell: function( rowEntity, colDef, newValue, previousValue ){
var grid = this.grid;
var gridRow = grid.getRow( rowEntity );
if ( !gridRow ){ gridUtil.logError( 'Unable to find rowEntity in grid data, dirty flag cannot be set' ); return; }
if ( newValue !== previousValue || gridRow.isDirty ){
if ( !grid.rowEdit.dirtyRows ){
grid.rowEdit.dirtyRows = [];
}
if ( !gridRow.isDirty ){
gridRow.isDirty = true;
grid.rowEdit.dirtyRows.push( gridRow );
}
delete gridRow.isError;
service.considerSetTimer( grid, gridRow );
}
},
/**
* @ngdoc method
* @methodOf ui.grid.rowEdit.service:uiGridRowEditService
* @name beginEditCell
* @description Receives a beginCellEdit event from the edit function,
* and cancels any rowEditSaveTimers if present, as the user is still editing
* this row. Only the rowEntity parameter
* is processed, although other params are available. Grid
* is automatically provided by the gridApi.
* @param {object} rowEntity the data entity for which the cell
* editing has commenced
*/
beginEditCell: function( rowEntity, colDef ){
var grid = this.grid;
var gridRow = grid.getRow( rowEntity );
if ( !gridRow ){ gridUtil.logError( 'Unable to find rowEntity in grid data, timer cannot be cancelled' ); return; }
service.cancelTimer( grid, gridRow );
},
/**
* @ngdoc method
* @methodOf ui.grid.rowEdit.service:uiGridRowEditService
* @name cancelEditCell
* @description Receives a cancelCellEdit event from the edit function,
* and if the row was already dirty, restarts the save timer. If the row
* was not already dirty, then it's not dirty now either and does nothing.
*
* Only the rowEntity parameter
* is processed, although other params are available. Grid
* is automatically provided by the gridApi.
*
* @param {object} rowEntity the data entity for which the cell
* editing was cancelled
*/
cancelEditCell: function( rowEntity, colDef ){
var grid = this.grid;
var gridRow = grid.getRow( rowEntity );
if ( !gridRow ){ gridUtil.logError( 'Unable to find rowEntity in grid data, timer cannot be set' ); return; }
service.considerSetTimer( grid, gridRow );
},
/**
* @ngdoc method
* @methodOf ui.grid.rowEdit.service:uiGridRowEditService
* @name navigate
* @description cellNav tells us that the selected cell has changed. If
* the new row had a timer running, then stop it similar to in a beginCellEdit
* call. If the old row is dirty and not the same as the new row, then
* start a timer on it.
* @param {object} newRowCol the row and column that were selected
* @param {object} oldRowCol the row and column that was left
*
*/
navigate: function( newRowCol, oldRowCol ){
var grid = this.grid;
if ( newRowCol.row.rowEditSaveTimer ){
service.cancelTimer( grid, newRowCol.row );
}
if ( oldRowCol && oldRowCol.row && oldRowCol.row !== newRowCol.row ){
service.considerSetTimer( grid, oldRowCol.row );
}
},
/**
* @ngdoc property
* @propertyOf ui.grid.rowEdit.api:GridOptions
* @name rowEditWaitInterval
* @description How long the grid should wait for another change on this row
* before triggering a save (in milliseconds). If set to -1, then saves are
* never triggered by timer (implying that the user will call flushDirtyRows()
* manually)
*
* @example
* Setting the wait interval to 4 seconds
*
* $scope.gridOptions = { rowEditWaitInterval: 4000 }
*
*
*/
/**
* @ngdoc method
* @methodOf ui.grid.rowEdit.service:uiGridRowEditService
* @name considerSetTimer
* @description Consider setting a timer on this row (if it is dirty). if there is a timer running
* on the row and the row isn't currently saving, cancel it, using cancelTimer, then if the row is
* dirty and not currently saving then set a new timer
* @param {object} grid the grid for which we are processing
* @param {GridRow} gridRow the row for which the timer should be adjusted
*
*/
considerSetTimer: function( grid, gridRow ){
service.cancelTimer( grid, gridRow );
if ( gridRow.isDirty && !gridRow.isSaving ){
if ( grid.options.rowEditWaitInterval !== -1 ){
var waitTime = grid.options.rowEditWaitInterval ? grid.options.rowEditWaitInterval : 2000;
gridRow.rowEditSaveTimer = $interval( service.saveRow( grid, gridRow ), waitTime, 1);
}
}
},
/**
* @ngdoc method
* @methodOf ui.grid.rowEdit.service:uiGridRowEditService
* @name cancelTimer
* @description cancel the $interval for any timer running on this row
* then delete the timer itself
* @param {object} grid the grid for which we are processing
* @param {GridRow} gridRow the row for which the timer should be adjusted
*
*/
cancelTimer: function( grid, gridRow ){
if ( gridRow.rowEditSaveTimer && !gridRow.isSaving ){
$interval.cancel(gridRow.rowEditSaveTimer);
delete gridRow.rowEditSaveTimer;
}
},
/**
* @ngdoc method
* @methodOf ui.grid.rowEdit.service:uiGridRowEditService
* @name setRowsDirty
* @description Sets each of the rows passed in dataRows
* to be dirty. note that if you have only just inserted the
* rows into your data you will need to wait for a $digest cycle
* before the gridRows are present - so often you would wrap this
* call in a $interval or $timeout
*
* $interval( function() {
* gridApi.rowEdit.setRowsDirty( myDataRows);
* }, 0, 1);
*
* @param {object} grid the grid for which rows should be set dirty
* @param {array} dataRows the data entities for which the gridRows
* should be set dirty.
*
*/
setRowsDirty: function( grid, myDataRows ) {
var gridRow;
myDataRows.forEach( function( value, index ){
gridRow = grid.getRow( value );
if ( gridRow ){
if ( !grid.rowEdit.dirtyRows ){
grid.rowEdit.dirtyRows = [];
}
if ( !gridRow.isDirty ){
gridRow.isDirty = true;
grid.rowEdit.dirtyRows.push( gridRow );
}
delete gridRow.isError;
service.considerSetTimer( grid, gridRow );
} else {
gridUtil.logError( "requested row not found in rowEdit.setRowsDirty, row was: " + value );
}
});
},
/**
* @ngdoc method
* @methodOf ui.grid.rowEdit.service:uiGridRowEditService
* @name setRowsClean
* @description Sets each of the rows passed in dataRows
* to be clean, clearing the dirty flag and the error flag, and removing
* the rows from the dirty and error caches.
* @param {object} grid the grid for which rows should be set clean
* @param {array} dataRows the data entities for which the gridRows
* should be set clean.
*
*/
setRowsClean: function( grid, myDataRows ) {
var gridRow;
myDataRows.forEach( function( value, index ){
gridRow = grid.getRow( value );
if ( gridRow ){
delete gridRow.isDirty;
service.removeRow( grid.rowEdit.dirtyRows, gridRow );
service.cancelTimer( grid, gridRow );
delete gridRow.isError;
service.removeRow( grid.rowEdit.errorRows, gridRow );
} else {
gridUtil.logError( "requested row not found in rowEdit.setRowsClean, row was: " + value );
}
});
}
};
return service;
}]);
/**
* @ngdoc directive
* @name ui.grid.rowEdit.directive:uiGridEdit
* @element div
* @restrict A
*
* @description Adds row editing features to the ui-grid-edit directive.
*
*/
module.directive('uiGridRowEdit', ['gridUtil', 'uiGridRowEditService', 'uiGridEditConstants',
function (gridUtil, uiGridRowEditService, uiGridEditConstants) {
return {
replace: true,
priority: 0,
require: '^uiGrid',
scope: false,
compile: function () {
return {
pre: function ($scope, $elm, $attrs, uiGridCtrl) {
uiGridRowEditService.initializeGrid($scope, uiGridCtrl.grid);
},
post: function ($scope, $elm, $attrs, uiGridCtrl) {
}
};
}
};
}]);
/**
* @ngdoc directive
* @name ui.grid.rowEdit.directive:uiGridViewport
* @element div
*
* @description Stacks on top of ui.grid.uiGridViewport to alter the attributes used
* for the grid row to allow coloring of saving and error rows
*/
module.directive('uiGridViewport',
['$compile', 'uiGridConstants', 'gridUtil', '$parse',
function ($compile, uiGridConstants, gridUtil, $parse) {
return {
priority: -200, // run after default directive
scope: false,
compile: function ($elm, $attrs) {
var rowRepeatDiv = angular.element($elm.children().children()[0]);
var existingNgClass = rowRepeatDiv.attr("ng-class");
var newNgClass = '';
if ( existingNgClass ) {
newNgClass = existingNgClass.slice(0, -1) + ", 'ui-grid-row-dirty': row.isDirty, 'ui-grid-row-saving': row.isSaving, 'ui-grid-row-error': row.isError}";
} else {
newNgClass = "{'ui-grid-row-dirty': row.isDirty, 'ui-grid-row-saving': row.isSaving, 'ui-grid-row-error': row.isError}";
}
rowRepeatDiv.attr("ng-class", newNgClass);
return {
pre: function ($scope, $elm, $attrs, controllers) {
},
post: function ($scope, $elm, $attrs, controllers) {
}
};
}
};
}]);
})();