AngularJS和ng-grid – 在单元格更改后自动将数据保存到服务器

我的使用案例非常简单。 用户在编辑Cell(enableCellEdit:true)之后,应该将数据“自动”发送到服务器(在单元模糊上)。 我尝试了不同的方法,但是他们都没有正确的解决。 我有一个简约的网格:

// Configure ng-grid $scope.gridOptions = { data: 'questions', enableCellSelection: true, selectedItems: $scope.selectedRow, multiSelect: false, columnDefs: [ {field: 'id', displayName: 'Id'}, {field: 'name', displayName: 'Name'}, {field: 'answers[1].valuePercent', displayName: 'Rural', enableCellEdit: true} ] }; 

例如,我试图观察传递给网格的数据模型。 但是这样做不会使编辑的单元格返回给我:

 $scope.$watch('myData', function (foo) { // myModel.$update() }, true); 

我试图摆弄“ngGridEventData”数据事件,但它不会在单元格编辑后触发

 $scope.$on('ngGridEventData', function (e, gridId) { // myModel.$update() }); 

最后,我试图观察一个单元格。 但是,这只能通过网格的“selectedCell”属性的平均值工作:

 $scope.selectedRow = []; $scope.gridOptions = { selectedItems: $scope.selectedRow, } $scope.$watch('selectedRow', function (foo) { console.log(foo) }, true); 

是否需要一个ng-grid插件 ? 我不能相信这不是开箱即用的东西。

你会有一个指针/片段如何解决自动保存/发送到服务器?

也许这是新的,但ng-grid实际上发布事件,可以用来实现简单的更新更新。

事件参考: https : //github.com/angular-ui/ng-grid/wiki/Grid-Events

示例代码(添加到您设置网格的控制器):

 $scope.$on('ngGridEventEndCellEdit', function(evt){ console.log(evt.targetScope.row.entity); // the underlying data bound to the row // Detect changes and send entity to server }); 

需要注意的是,即使没有更改,事件也会触发,所以在发送到服务器之前(例如,通过“ngGridEventStartCellEdit”),您仍然可能需要检查更改

我发现我认为是一个更好的解决scheme:

  cellEditableTemplate = "<input ng-class=\"'colt' + col.index\" ng-input=\"COL_FIELD\" ng-model=\"COL_FIELD\" ng-change=\"updateEntity(row.entity)\"/>" 

使用ng-change这种方式将导致updateEntity被整个被更改的对象(行)所调用,并且可以将其发送回服务器。 你不需要任何新的范围variables。 以前的解决scheme不足之处在于,当您单击以开始编辑字段时,在开始编辑之前,它始终是空白的,而不是原始值。

这将导致每个击键都会调用updateEntity()。 如果对于您来说太频繁了,您可以在发布到服务器之前使用超时,或者使用updateEntity()logging您想要推送的ID,然后使用ng-blur发布logging的ID。

看起来我find了一个解决scheme,感谢Angular 邮件列表 。 有人指出,AngularJS缺lessonBlur事件(以及onFocus)。 但是,这可以通过添加一个“简单”指令来克服。

 angular.module('myApp.ngBlur', []) .directive('ngBlur', function () { return function (scope, elem, attrs) { elem.bind('blur', function () { scope.$apply(attrs.ngBlur); }); }; }); 

作为信息,还有另一个与blur事件指令相关的实现例子。

然后,控制器中的其余代码如下所示:

 // Define the template of the cell editing with input type "number" (for my case). // Notice the "ng-blur" directive var cellEditableTemplate = "<input style=\"width: 90%\" step=\"any\" type=\"number\" ng-class=\"'colt' + col.index\" ng-input=\"COL_FIELD\" ng-blur=\"updateEntity(col, row)\"/>"; // Configure ng-grid $scope.gridOptions = { data: 'questions', enableCellSelection: true, multiSelect: false, columnDefs: [ {field: 'id', displayName: 'Id'}, {field: 'name', displayName: 'Name'}, // Notice the "editableCellTemplate" {field: 'answers[0].valuePercent', displayName: 'Rural', enableCellEdit: true, editableCellTemplate: cellEditableTemplate} ] }; // Update Entity on the server side $scope.updateEntity = function(column, row) { console.log(row.entity); console.log(column.field); // code for saving data to the server... // row.entity.$update() ... <- the simple case // I have nested Entity / data in the row <- the complex case // var answer = new Answer(question.answers[answerIndex]); // answerIndex is computed with "column.field" variable // answer.$update() ... } 

我已经花了一些时间把这个ng-grid 2.x的这些部分放在一起。 我仍然有点击两次编辑行的问题,但我认为这是一个引导问题,而不是一个ngGrid问题,它不会发生在我的示例代码(它没有引导程序)。

我也在ui-grid 3.0的教程中实现了类似的逻辑,这个教程还在testing阶段,但很快就会成为首选版本。 这可以在这里find: http : //technpol.wordpress.com/2014/08/23/upgrading-to-ng-grid-3-0-ui-grid/ ,并提供了一个更容易,更干净的API这个function。

对于2.x版本来说,我已经创build了一个正在运行的plunker,它有一个带有下拉和input字段的可编辑网格,使用ngBlur指令,并使用$ timeout来避免重复保存更新: http : //plnkr.co/edit/VABAEu?p=preview

代码的基础是:

 var app = angular.module('plunker', ["ngGrid"]); app.controller('MainCtrl', function($scope, $timeout, StatusesConstant) { $scope.statuses = StatusesConstant; $scope.cellInputEditableTemplate = '<input ng-class="\'colt\' + col.index" ng-input="COL_FIELD" ng-model="COL_FIELD" ng-blur="updateEntity(row)" />'; $scope.cellSelectEditableTemplate = '<select ng-class="\'colt\' + col.index" ng-input="COL_FIELD" ng-model="COL_FIELD" ng-options="id as name for (id, name) in statuses" ng-blur="updateEntity(row)" />'; $scope.list = [ { name: 'Fred', age: 45, status: 1 }, { name: 'Julie', age: 29, status: 2 }, { name: 'John', age: 67, status: 1 } ]; $scope.gridOptions = { data: 'list', enableRowSelection: false, enableCellEditOnFocus: true, multiSelect: false, columnDefs: [ { field: 'name', displayName: 'Name', enableCellEditOnFocus: true, editableCellTemplate: $scope.cellInputEditableTemplate }, { field: 'age', displayName: 'Age', enableCellEdit: false }, { field: 'status', displayName: 'Status', enableCellEditOnFocus: true, editableCellTemplate: $scope.cellSelectEditableTemplate, cellFilter: 'mapStatus'} ] }; $scope.updateEntity = function(row) { if(!$scope.save) { $scope.save = { promise: null, pending: false, row: null }; } $scope.save.row = row.rowIndex; if(!$scope.save.pending) { $scope.save.pending = true; $scope.save.promise = $timeout(function(){ // $scope.list[$scope.save.row].$update(); console.log("Here you'd save your record to the server, we're updating row: " + $scope.save.row + " to be: " + $scope.list[$scope.save.row].name + "," + $scope.list[$scope.save.row].age + "," + $scope.list[$scope.save.row].status); $scope.save.pending = false; }, 500); } }; }) .directive('ngBlur', function () { return function (scope, elem, attrs) { elem.bind('blur', function () { scope.$apply(attrs.ngBlur); }); }; }) .filter('mapStatus', function( StatusesConstant ) { return function(input) { if (StatusesConstant[input]) { return StatusesConstant[input]; } else { return 'unknown'; } }; }) .factory( 'StatusesConstant', function() { return { 1: 'active', 2: 'inactive' }; }); 

当你运行这个运算器,丢失焦点时,你应该在控制台上看到更新触发器。

我还将一个README.md文件包含在重读器中,对于那些给我带来困难的东西有一些想法,在这里转载。

这里的function是我有一个人的名单,这些人的名字,年龄和状态。 根据我们在真实应用程序中可能做的事情,状态是一个代码,我们想要显示解码。 因此,我们有一个状态代码列表(可能在一个真正的应用程序来自数据库),我们有一个filter将代码映射到解码。

我们想要的是两件事。 我们希望能够在input框中编辑名称,并在下拉列表中编辑状态。

评论我所学到的东西。

  1. 在gridOptions级别,有enableCellEditOnFocus和enableCellEdit。 不要同时启用,你需要select。 onFocus表示单击,CellEdit表示双击。 如果你启用了这两个function,那么你的网格位就会出现意想不到的行为,你不希望被编辑

  2. 在columnDefs级别,你有相同的选项。 但是这一次你需要设置CellEdit和onFocus,并且你需要在任何你不想编辑的单元格上设置cellEdit为false – 这不是默认的

  3. 该文档说,您的可编辑单元格模板可以是:

    <input ng-class =“'colt'+ col.index”ng-input =“COL_FIELD”/>

    其实它需要是:

    <input ng-class =“'colt'+ col.index”ng-input =“COL_FIELD”ng-model =“COL_FIELD”/>

  4. 要在失去焦点时触发保存事件,我们创build了一个blur指令,这是我在stackoverflow中find的逻辑: AngularJS和ng-grid – 在单元格更改后自动将数据保存到服务器

  5. 这也意味着更改每个可编辑单元格模板以调用ng-blur,您可以在可编辑单元格模板的末尾看到它

  6. 当我们离开字段时(至less在Chrome中),我们得到两个模糊事件,所以我们使用一个计时器,以便只处理其中的一个。 丑,但它的作品。

我还创build了一篇博客文章,对此代码进行了更全面的介绍: http : //technpol.wordpress.com/2013/12/06/editable-nggrid-with-both-dropdowns-and-selects/

如果您使用的是UI Grid 3.0,那么事件是: uiGridEventEndCellEdit

 $scope.$on('uiGridEventEndCellEdit', function (data) { console.log(data.targetScope.row.entity); } 

这是一个有一些缺陷的答案的改进: – 它触发一个JSexception,如答案的注释之一所示 – 单元格中的数据input不保留在网格中 – updateEntity方法不说明如何保存input数据

为了移除exception,创build一个范围属性并将其添加到cellEditableTemplate:

 $scope.cellValue; ... var cellEditableTemplate = "<input style=\"width: 90%\" step=\"any\" type=\"number\" ng-class=\"'colt' + col.index\" ng-input=\"COL_FIELD\" ng-blur=\"updateEntity(col, row, cellValue)\" ng-model='cellValue'/>"; 

请注意,现在对updateEntity的ng-blur调用包含cellValue作为参数。 接下来,更新updateEntity模糊处理程序以包含参数并更新网格:

 $scope.updateEntity = function(column, row, cellValue) { console.log(row.entity); console.log(column.field); row.entity[column.field] = cellValue; // code for saving data to the server... // row.entity.$update() ... <- the simple case // I have nested Entity / data in the row <- the complex case // var answer = new Answer(question.answers[answerIndex]); // answerIndex is computed with "column.field" variable // answer.$update() ... }; 

我现在可以看到屏幕上的更改以及触发基于单元的后端更新。

正如PaulL在其中一个注释中提到的,ui-grid现在有一个rowEditfunction,可以在完成编辑时保存整行。 请参阅http://ui-grid.info/docs/#/tutorial/205_row_editable