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框中编辑名称,并在下拉列表中编辑状态。
评论我所学到的东西。
-
在gridOptions级别,有enableCellEditOnFocus和enableCellEdit。 不要同时启用,你需要select。 onFocus表示单击,CellEdit表示双击。 如果你启用了这两个function,那么你的网格位就会出现意想不到的行为,你不希望被编辑
-
在columnDefs级别,你有相同的选项。 但是这一次你需要设置CellEdit和onFocus,并且你需要在任何你不想编辑的单元格上设置cellEdit为false – 这不是默认的
-
该文档说,您的可编辑单元格模板可以是:
<input ng-class =“'colt'+ col.index”ng-input =“COL_FIELD”/>
其实它需要是:
<input ng-class =“'colt'+ col.index”ng-input =“COL_FIELD”ng-model =“COL_FIELD”/>
-
要在失去焦点时触发保存事件,我们创build了一个blur指令,这是我在stackoverflow中find的逻辑: AngularJS和ng-grid – 在单元格更改后自动将数据保存到服务器
-
这也意味着更改每个可编辑单元格模板以调用ng-blur,您可以在可编辑单元格模板的末尾看到它
-
当我们离开字段时(至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 。