AngularJS e ng-grid – salva automaticamente i dati sul server dopo che una cella è stata cambiata

Il mio caso d’uso è piuttosto semplice. Un utente, dopo aver modificato una cella (enableCellEdit: true), dovrebbe avere i dati “automaticamente” inviati al server (sulla cella sfocata). Ho provato approcci diversi ma nessuno di loro ha funzionato correttamente. Ho una griglia minimalista:

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

Ad esempio, ho provato a guardare il modello di dati passato alla griglia. Ma così facendo non mi restituirà la cella modificata:

 $scope.$watch('myData', function (foo) { // myModel.$update() }, true); 

Ho provato a trafficare con l’evento dati “ngGridEventData” ma non si triggers dopo la modifica della cella

 $scope.$on('ngGridEventData', function (e, gridId) { // myModel.$update() }); 

Alla fine, ho provato ad osservare una cella. Tuttavia, questo funziona solo per una riga in base alla proprietà “selectedCell” della griglia:

 $scope.selectedRow = []; $scope.gridOptions = { selectedItems: $scope.selectedRow, } $scope.$watch('selectedRow', function (foo) { console.log(foo) }, true); 

È necessario un plug-in di griglia ng ? Non posso credere che non sia qualcosa fuori dalla scatola.

    Avresti un puntatore / snippet come potrei risolvere il salvataggio automatico / inviare al server?

    Forse questo è nuovo ma ng-grid pubblica eventi che possono essere utilizzati per implementare un semplice aggiornamento sui cambiamenti.

    Riferimento evento: https://github.com/angular-ui/ng-grid/wiki/Grid-Events

    Codice di esempio (aggiungere al controller in cui è stata configurata la griglia):

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

    Una cosa da notare è che l’evento si innescherà anche se non sono state apportate modifiche, quindi potresti comunque voler controllare le modifiche prima di inviarle al server (ad esempio tramite ‘ngGridEventStartCellEdit’)

    Ho trovato quello che penso sia una soluzione molto più bella:

      cellEditableTemplate = "" 

    L’uso di ng-change in questo modo fa sì che updateEntity venga chiamato con l’intero object (riga) che è stato modificato e che è ansible postarlo sul server. Non hai bisogno di nuove variabili di scope. Una carenza della soluzione precedente era che quando avevi fatto clic per iniziare a modificare il campo, sarebbe sempre vuoto invece del valore originale prima di iniziare a modificare.

    Ciò causerà updateEntity () per essere chiamato su ogni tasto. Se è troppo frequente per te, potresti usare un timeout prima di postare sul server, o semplicemente usare updateEntity () per registrare l’id che vuoi spingere, e quindi usare ng-blur per postare l’id registrato.

    Sembra che ho trovato una soluzione grazie alla mailing list Angular. È stato sottolineato che AngularJS manca l’evento onBlur (così come onFocus). Tuttavia, questo può essere superato aggiungendo una direttiva “semplice”.

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

    Come informazione, qui c’è un altro esempio di implementazione relativa alla direttiva degli eventi di sfocatura.

    Quindi, il resto del codice nel controller è simile al seguente:

     // Define the template of the cell editing with input type "number" (for my case). // Notice the "ng-blur" directive var cellEditableTemplate = ""; // 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() ... } 

    Ho passato un po ‘di tempo a mettere insieme i pezzi di questo per ng-grid 2.x. Ho ancora un problema con dover fare clic due volte per modificare una riga, ma penso che sia un problema di bootstrap, non un problema di ngGrid, non si verifica nel mio codice di esempio (che non ha ancora il bootstrap).

    Ho anche implementato una logica simile in un tutorial per ui-grid 3.0, che è ancora beta ma presto diventerà la versione preferita. Questo può essere trovato su: http://technpol.wordpress.com/2014/08/23/upgrading-to-ng-grid-3-0-ui-grid/ , e fornisce una API molto più facile e più pulita per questa funzionalità .

    Per la versione 2.x, per illustrare tutti i bit, ho creato un plunker in esecuzione che ha una griglia modificabile con un dropdown e un campo di input, usa la direttiva ngBlur e usa un timeout $ per evitare i salvataggi duplicati sul aggiornamento: http://plnkr.co/edit/VABAEu?p=preview

    Le basi del codice sono:

     var app = angular.module('plunker', ["ngGrid"]); app.controller('MainCtrl', function($scope, $timeout, StatusesConstant) { $scope.statuses = StatusesConstant; $scope.cellInputEditableTemplate = ''; $scope.cellSelectEditableTemplate = ''; $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' }; }); 

    Quando esegui questo plunker, e la perdita di fuoco si triggers, dovresti vedere sulla console l’triggerszione del trigger di aggiornamento.

    Ho anche incluso un README.md nel plunker con alcune riflessioni sulle cose che mi hanno dato problemi, qui riprodotto.

    La funzionalità qui è che ho una lista di persone, quelle persone hanno nomi, età e status. In linea con ciò che potremmo fare in una vera app, lo stato è un codice e vogliamo mostrare la decodifica. Di conseguenza abbiamo una lista dei codici di stato (che potrebbe provenire da una vera app proveniente dal database) e abbiamo un filtro per mappare il codice alla decodifica.

    Quello che vogliamo sono due cose. Ci piacerebbe essere in grado di modificare il nome in una casella di input e di modificare lo stato in un menu a discesa.

    Commenti su cose che ho imparato su questo plunk.

    1. A livello di gridOptions, ci sono sia enableCellEditOnFocus che enableCellEdit. Non abilitare entrambi, è necessario selezionare. onFocus significa singolo clic, CellEdit significa doppio clic. Se si abilita entrambi, si ottiene un comportamento imprevisto sui bit della griglia che non si desidera modificare

    2. Al livello columnDefs, hai le stesse opzioni. Ma questa volta è necessario impostare sia CellEdit che onFocus ed è necessario impostare cellEdit su false su tutte le celle che non si desidera modificare: non è l’impostazione predefinita

    3. La documentazione dice che il modello di cella modificabile può essere:

      in realtà deve essere:

    4. Per triggersre un evento di salvataggio quando perdiamo il focus, abbiamo creato una direttiva di sfocatura, la logica per la quale ho trovato in stackoverflow: AngularJS e ng-grid: salvataggio automatico dei dati sul server dopo che una cella è stata modificata

    5. Ciò significa anche cambiare ogni modello di cella modificabile per chiamare ng-blur, che puoi vedere alla fine del modello di cella modificabile

    6. Riceviamo due sfocature quando lasciamo il campo (almeno in Chrome), quindi utilizziamo un timer in modo che venga elaborato solo uno di essi. Brutto, ma funziona.

    Ho anche creato un post sul blog che fornisce una spiegazione più completa di questo codice: http://technpol.wordpress.com/2013/12/06/editable-nggrid-with-both-dropdowns-and-selects/

    Se stai utilizzando UI Grid 3.0, quell’evento è: uiGridEventEndCellEdit

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

    Questo è un miglioramento della risposta che ha alcuni difetti: – innesca un’eccezione JS, come indicato in uno dei commenti della risposta – l’input di dati nella cella non viene mantenuto nella griglia – il metodo updateEntity non illustra come salvare i dati di input

    Per rimuovere l’eccezione, crea un attributo scope e aggiungilo a cellEditableTemplate:

     $scope.cellValue; ... var cellEditableTemplate = ""; 

    Si noti che la chiamata ng-blur per updateEntity ora include cellaValore come argomento. Successivamente, aggiorna il gestore di sfocatura updateEntity per includere l’argomento e aggiornare la griglia:

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

    Ora sono in grado di vedere le modifiche sullo schermo e di triggersre gli aggiornamenti back-end basati su celle.

    Come menzionato da PaulL in uno dei commenti, ui-grid ora ha una funzione rowEdit progettata per consentire il salvataggio dell’intera riga quando viene eseguita la modifica. Vedi http://ui-grid.info/docs/#/tutorial/205_row_editable .