web-dev-qa-db-ja.com

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

たとえば、グリッドに渡されたデータモデルを監視しようとしました。しかし、そうしても編集されたセルは返されません:

$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プラグイン 必要ですか?私はそれがすぐに使えるものではないとは信じられません。

自動保存を解決してサーバーに送信する方法を示すポインター/スニペットがありますか?

38
Fabien

たぶんこれは新しいかもしれませんが、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」を使用)

30
chriswhitmore

私ははるかに優れたソリューションだと思うものを見つけました:

  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を投稿できます。

21
Michael Natkin

Angular メーリングリスト のおかげで解決策を見つけたようです。 「単純な」ディレクティブを追加することで克服できます。

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

情報として、ぼかしイベントディレクティブ here に関連する実装の別の例があります。

次に、コントローラー内の残りのコードは次のようになります。

// 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() ...
}
17
Fabien

Ng-grid 2.xのこの部分をまとめるのに時間を費やしました。行を編集するために2回クリックする必要があるという問題がまだありますが、それはbootstrapの問題であり、ngGridの問題ではなく、サンプルコードでは発生しません(これはt bootstrap yet)を持っています。

Ui-grid 3.0のチュートリアルでも同様のロジックを実装しました。これはまだベータ版ですが、すぐに推奨バージョンになります。これは次の場所にあります: http://technpol.wordpress.com/2014/08/23/upgrading-to-ng-grid-3-0-ui-grid/ この機能のためのより簡単でクリーンなAPI。

2.xバージョンでは、すべてのビットを説明するために、ドロップダウンと入力フィールドの両方を備えた編集可能なグリッドを持ち、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をプランカーに含めて、ここで再現した、私に困難を与えたものについてのいくつかの考えを入れました。

ここでの機能は、私が人々のリストを持っているということです、それらの人々は名前、年齢とステータスを持っています。実際のアプリでできることと一致して、ステータスはコードであり、デコードを表示したいと思います。したがって、ステータスコードリスト(実際のアプリではデータベースから取得される可能性があります)があり、コードをデコードにマップするフィルターがあります。

私たちが望んでいるのは2つのことです。入力ボックスで名前を編集し、ドロップダウンでステータスを編集できるようにします。

このプランクで学んだことに対するコメント。

  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で見つけたロジックであるblurディレクティブを作成しました: AngularJS and ng-grid-セルが変更された後、サーバーにデータを自動保存 =

  5. これは、編集可能な各セルテンプレートを変更してng-blurを呼び出すことも意味します。これは、編集可能なセルテンプレートの最後に表示されます。

  6. フィールドを離れると(少なくともChromeで)2つのblurイベントが発生するため、タイマーを使用して、そのうちの1つだけが処理されるようにします。 glyいですが、動作します。

また、このコードのより詳細なウォークスルーを行うブログ投稿を作成しました。 http://technpol.wordpress.com/2013/12/06/editable-nggrid-with-both-dropdowns-and- select /

6
PaulL

UI Grid 3.0を使用している場合、そのイベントは次のとおりです。 uiGridEventEndCellEdit

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

これは、いくつかの欠陥がある回答の改善です。-回答のコメントの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() ...
};

画面に変更を表示できるようになり、セルベースのバックエンド更新をトリガーできるようになりました。

1

PaulLがコメントの1つで言及したように、ui-gridには、編集が完了したときに行全体を保存できるように設計されたrowEdit機能があります。 http://ui-grid.info/docs/#/tutorial/205_row_editable を参照してください。

0
Jeremy Cumbo