38

我的用例非常简单。用户在编辑单元格 (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}
    ]
};

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

$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 插件?我不敢相信这不是开箱即用的东西。

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

4

7 回答 7

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 
});

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

于 2014-03-02T16:37:27.290 回答
21

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

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

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

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

于 2013-08-25T05:35:19.780 回答
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);
        });
    };
});

作为信息,这里有另一个与模糊事件指令相关的实现示例。

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

// 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() ...
}
于 2013-04-05T11:11:30.970 回答
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'
  };
});

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

我还在 plunker 中包含了一个 README.md,其中包含对给我带来困难的事情的一些想法,在此处复制。

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

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

评论我在这个 plunk 上学到的东西。

  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/

于 2013-12-06T02:06:59.517 回答
2

如果您使用的是 UI Grid 3.0,则该事件是:uiGridEventEndCellEdit

$scope.$on('uiGridEventEndCellEdit', function (data) {
    console.log(data.targetScope.row.entity);
}
于 2015-04-21T01:01:36.163 回答
1

这是对有一些缺陷的答案的改进: - 它触发了一个 JS 异常,如答案的评论之一所示 - 单元格中的数据输入未保留在网格中 - updateEntity 方法没有说明如何保存输入数据

为了删除异常,创建一个范围属性并将其添加到 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 blur 处理程序以包含参数并更新网格:

$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() ...
};

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

于 2013-08-01T00:02:06.960 回答
0

正如 PaulL 在评论之一中提到的,ui-grid 现在具有 rowEdit 功能,旨在允许在完成编辑时保存整行。请参阅http://ui-grid.info/docs/#/tutorial/205_row_editable

于 2015-10-09T15:51:16.167 回答