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

Fab*_*ien 38 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}
    ]
};
Run Code Online (Sandbox Code Playgroud)

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

$scope.$watch('myData', function (foo) {
    // myModel.$update()
}, true);
Run Code Online (Sandbox Code Playgroud)

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

$scope.$on('ngGridEventData', function (e, gridId) {
    // myModel.$update()
});
Run Code Online (Sandbox Code Playgroud)

最后,我试图观察一个Cell.但是,这仅适用于网格的"selectedCell"属性的行:

$scope.selectedRow = [];

$scope.gridOptions = {
    selectedItems: $scope.selectedRow,
}

$scope.$watch('selectedRow', function (foo) {
    console.log(foo)
}, true);
Run Code Online (Sandbox Code Playgroud)

它是否需要ng-grid插件?我不敢相信它不是开箱即用的东西.

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

小智 30

也许这是新的,但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 
});
Run Code Online (Sandbox Code Playgroud)

需要注意的一点是,即使没有进行任何更改,事件也会触发,因此您可能仍希望在发送到服务器之前检查更改(例如,通过'ngGridEventStartCellEdit')


Mic*_*kin 21

我发现了我认为更好的解决方案:

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

以这种方式使用ng-change将导致使用已更改的整个对象(行)调用updateEntity,并将其发布回服务器.您不需要任何新的范围变量.之前解决方案的一个缺点是,当您单击开始编辑字段时,在开始编辑之前,它将始终为空白而不是原始值.

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


Fab*_*ien 17

看起来我找到了一个解决方案,感谢Angular 邮件列表.有人指出AngularJS缺少onBlur事件(以及onFocus).但是,这可以通过添加"简单"指令来克服.

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

随着信息,还有相关的模糊事件指令执行的另一个例子在这里.

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

// 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() ...
}
Run Code Online (Sandbox Code Playgroud)

  • 当我使用你的CellEditableTemplate时,会调用updateEntity函数,但是我得到一个"错误:没有控制器:ngModel"错误,单元格被卡在一个文本框中.你有这个问题吗? (4认同)

Pau*_*ulL 6

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

我还在ui-grid 3.0的教程中实现了类似的逻辑,它仍然是测试版,但很快就会成为首选版本.可以在以下网址找到:http://technpol.wordpress.com/2014/08/23/upgrading-to-ng-grid-3-0-ui-grid/,并为此功能提供更简单,更清晰的API .

对于2.x版本,为了说明所有的比特,我创建了一个运行的plunker,它有一个带有下拉列表和输入字段的可编辑网格,使用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'
  };
});
Run Code Online (Sandbox Code Playgroud)

当您运行此plunker并且失去焦点时,您应该在控制台上看到更新触发器触发.

我还在plunker中包含了一个README.md,其中有一些关于让我困难的事情的想法,在这里再现.

这里的功能是我有一个人名单,那些人有姓名,年龄和状态.与我们在真实应用程序中可能执行的操作一致,状态是代码,我们希望显示解码.因此,我们有一个状态代码列表(可能在真正的应用程序中来自数据库),我们有一个过滤器将代码映射到解码.

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

评论我在这个问题上学到的东西.

  1. 在gridOptions级别,有enableCellEditOnFocus和enableCellEdit.不要同时启用,需要选择.onFocus意味着单击,CellEdit意味着双击.如果同时启用这两个,那么您不希望可编辑的网格位出现意外行为

  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. 为了在失去焦点时触发保存事件,我们创建了一个模糊指令,我在stackoverflow中找到的逻辑:AngularJS和ng-grid - 在单元格更改后自动将数据保存到服务器

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

  6. 当我们离开现场时(至少在Chrome中)我们得到两个模糊事件,因此我们使用计时器,以便只处理其中一个.丑陋,但它的工作原理.

我还创建了一篇博文,对这段代码进行了更全面的演练:http://technpol.wordpress.com/2013/12/06/editable-nggrid-with-both-dropdowns-and-selects/