Подтвердить что ты не робот

AngularJS и ng-grid - автоматическое сохранение данных на сервере после изменения ячейки

Мой пример использования довольно прост. Пользователь после редактирования ячейки (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()
});

Наконец, я попытался наблюдать за Cell. Однако это работает только для строки по средству свойства selectedCell для сетки:

$scope.selectedRow = [];

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

$scope.$watch('selectedRow', function (foo) {
    console.log(foo)
}, true);

Требуется ли ng-grid plugin? Я не могу поверить, что это не что-то из коробки.

У вас есть указатель/фрагмент, как я могу решить автоматическое сохранение/отправку на сервер?

4b9b3361

Ответ 1

Возможно, это новое, но 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')

Ответ 2

Я нашел то, что, по моему мнению, гораздо приятнее:

  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() для записи идентификатора, который хотите нажать, а затем использовать ng-blur для размещения записанного идентификатора.

Ответ 3

Похоже, я нашел решение благодаря списку рассылки Angular . Было указано, что AngularJS отсутствует событие onBlur (а также 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() ...
}

Ответ 4

Я потратил некоторое время, собирая биты этого для ng-grid 2.x. У меня все еще есть проблема с необходимостью дважды щелкнуть, чтобы отредактировать строку, но я думаю, что проблема с начальной загрузкой, а не проблема с ngGrid, это не происходит в моем примере кода (у которого еще нет начальной загрузки).

Я также реализовал аналогичную логику в учебнике по ui-grid 3.0, который все еще является бета-версией, но скоро станет предпочтительной. Это можно найти по адресу http://technpol.wordpress.com/2014/08/23/upgrading-to-ng-grid-3-0-ui-grid/ и обеспечивает гораздо более легкую и чистую апи для этой функции.

Для версии 2.x, чтобы проиллюстрировать все биты, я создал запущенный плункер, который имеет редактируемую сетку с раскрывающимся списком и поле ввода, использует директиву ngBlur и использует тайм-аут $, чтобы избежать дублирования сохраняет обновление: 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 в плункер с некоторыми мыслями о вещах, которые мне затруднили, воспроизведенных здесь.

Функциональность здесь в том, что у меня есть список людей, этих людей имеют имена, возраст и статусы. В соответствии с тем, что мы можем сделать в реальном app, статус - это код, и мы хотим показать декодирование. Соответственно у нас есть список кодов состояний (который может в реальном приложении поступать из базы данных), и у нас есть фильтр для сопоставления кода с декодированием.

Нам нужны две вещи. Мы хотели бы иметь возможность редактировать имя в поле ввода и изменить статус в выпадающем меню.

Комментарии к тем вещам, которые я узнал на этом сюжете.

  • На уровне gridOptions есть как enableCellEditOnFocus, так и enableCellEdit. Не включайте оба варианта, вам нужно выбрать. onFocus означает один клик, CellEdit означает двойной щелчок. Если вы включите вы получаете неожиданное поведение на битах вашей сетки, которые вам не нужны для редактирования

  • На уровне columnDefs у вас одинаковые параметры. Но на этот раз вы необходимо установить как CellEdit, так и onFocus, и вам нужно установить cellEdit в false на любых ячейках, которые вы не хотите редактировать - это не значение по умолчанию

  • В документации говорится, что ваш редактируемый шаблон ячейки может быть:

    < вход ng- class= "'colt' + col.index" ng-input = "COL_FIELD" / >

    на самом деле это должно быть:

    < вход ng- class= "'colt' + col.index" ng-input = "COL_FIELD" ng-model = "COL_FIELD" / >

  • Чтобы вызвать событие сохранения, когда мы теряем фокус, мы создали директиву размытия, логику, для которой я нашел в stackoverflow: AngularJS и ng-grid - автоматическое сохранение данных на сервере после изменения ячейки

  • Это также означает изменение каждого редактируемого шаблона ячейки для вызова ng-blur, который вы можете увидеть в конце редактируемого шаблона ячейки

  • Мы получаем два события размытия, когда мы покидаем поле (по крайней мере, в Chrome), поэтому мы используйте таймер так, чтобы обрабатывался только один из них. Ужасно, но он работает.

Я также создал сообщение в блоге, которое делает более тщательное прохождение этого кода: http://technpol.wordpress.com/2013/12/06/editable-nggrid-with-both-dropdowns-and-selects/

Ответ 5

Если вы используете UI Grid 3.0, это событие: uiGridEventEndCellEdit

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

Ответ 6

Это улучшение ответа, имеющего несколько недостатков:  - он вызывает исключение JS, как указано в одном из ответов ответа  - ввод данных в ячейку не сохраняется в сетке  - метод updateEntity не показывает, как сохранить входные данные

Чтобы удалить исключение, создайте атрибут scope и добавьте его в 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'/>";

Обратите внимание, что вызов ng-blur для updateEntity теперь включает cellValue в качестве аргумента. Затем обновите обработчик blure 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() ...
};

Теперь я могу видеть изменения на экране, а также запускать базовые обновления на основе сотовой сети.

Ответ 7

Как упоминает PaulL в одном из комментариев, ui-grid теперь имеет функцию rowEdit, предназначенную для сохранения всей строки при редактировании. См. http://ui-grid.info/docs/#/tutorial/205_row_editable.