ejemplos - AngularJS y ng-grid: guarda automáticamente los datos en el servidor después de cambiar una celda
ui-grid angularjs (7)
Como mencionó PaulL en uno de los comentarios, ui-grid ahora tiene una función de edición de fila diseñada para permitir guardar la fila completa cuando finalice la edición. Consulte http://ui-grid.info/docs/#/tutorial/205_row_editable .
Mi caso de uso es bastante simple. Un usuario, después de editar una celda (enableCellEdit: true), debe recibir los datos "automáticamente" al servidor (en el desenfoque de la celda) Probé diferentes enfoques, pero ninguno de ellos ha funcionado correctamente. Tengo una cuadrícula 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}
]
};
Por ejemplo, traté de ver el modelo de datos pasado a la cuadrícula. Pero hacerlo no me devolverá la celda editada:
$scope.$watch(''myData'', function (foo) {
// myModel.$update()
}, true);
Intenté jugar con el evento de datos "ngGridEventData" pero no se dispara después de editar la celda
$scope.$on(''ngGridEventData'', function (e, gridId) {
// myModel.$update()
});
Finalmente, traté de observar una célula. Sin embargo, esto solo funciona para una fila por la media de la propiedad "selectedCell" de la cuadrícula:
$scope.selectedRow = [];
$scope.gridOptions = {
selectedItems: $scope.selectedRow,
}
$scope.$watch(''selectedRow'', function (foo) {
console.log(foo)
}, true);
¿Es un complemento ng-grid necesario? No puedo creer que no sea algo fuera de la caja.
¿Tendría un puntero / fragmento de código sobre cómo podría resolver el guardado / envío automático al servidor?
Esta es una mejora de la respuesta que tiene algunas fallas: - activa una excepción JS, como se indica en uno de los comentarios de la respuesta - la entrada de datos en la celda no se conserva en la cuadrícula - el método updateEntity no ilustra cómo guardar los datos de entrada
Para eliminar la excepción, cree un atributo de ámbito y agréguelo a 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''/>";
Tenga en cuenta que la llamada ng-blur a updateEntity ahora incluye cellValue como argumento. A continuación, actualice el controlador de desenfoque updateEntity para incluir el argumento y actualice la cuadrícula:
$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() ...
};
Ahora puedo ver los cambios en la pantalla, así como activar actualizaciones de back-end basadas en células.
He encontrado lo que creo que es una solución mucho mejor:
cellEditableTemplate = "<input ng-class=/"''colt'' + col.index/" ng-input=/"COL_FIELD/" ng-model=/"COL_FIELD/" ng-change=/"updateEntity(row.entity)/"/>"
El uso de ng-change de esta manera hará que se llame a updateEntity con el objeto completo (fila) que se ha cambiado y usted puede enviarlo de nuevo al servidor. No necesitas nuevas variables de alcance. Una deficiencia de la solución anterior era que al hacer clic para comenzar a editar el campo, siempre estaría en blanco en lugar del valor original antes de comenzar a editar.
Eso hará que se llame a updateEntity () en cada pulsación de tecla. Si eso es demasiado frecuente para usted, puede usar un tiempo de espera antes de publicar en el servidor, o simplemente usar updateEntity () para registrar la identificación que desea insertar, y luego usar ng-blur para publicar la identificación registrada.
He pasado un tiempo reuniendo los bits de esto para ng-grid 2.x. Todavía tengo un problema con tener que hacer clic dos veces para editar una fila, pero creo que es un problema de bootstrap, no un problema de ngGrid, no ocurre en mi código de muestra (que aún no tiene bootstrap).
También implementé una lógica similar en un tutorial para ui-grid 3.0, que todavía es beta pero pronto se convertirá en la versión preferida. Esto se puede encontrar en: http://technpol.wordpress.com/2014/08/23/upgrading-to-ng-grid-3-0-ui-grid/ , y proporciona una api mucho más fácil y más limpia para esta funcionalidad .
Para la versión 2.x, para ilustrar todos los bits, he creado un plunker en ejecución que tiene una cuadrícula editable con un campo desplegable y un campo de entrada, usa la directiva ngBlur y usa un tiempo de espera de $ para evitar duplicados en la actualización: http://plnkr.co/edit/VABAEu?p=preview
Los fundamentos del código son:
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''
};
});
Cuando ejecute este plunker y el foco de pérdida se dispare, debería ver en la consola el disparador de actualización.
También incluí un archivo README.md en el plunker con algunas reflexiones sobre cosas que me causaron dificultades, reproducidas aquí.
La funcionalidad aquí es que tengo una lista de personas, esas personas tienen nombres, edades y estados. De acuerdo con lo que podríamos hacer en una aplicación real, el estado es un código y queremos mostrar la decodificación. En consecuencia, tenemos una lista de códigos de estado (que en una aplicación real podría provenir de la base de datos), y tenemos un filtro para asignar el código a la decodificación.
Lo que queremos son dos cosas. Nos gustaría poder editar el nombre en un cuadro de entrada y editar el estado en un menú desplegable.
Comentarios sobre cosas que he aprendido en este plunk.
En el nivel de gridOptions, hay enableCellEditOnFocus y enableCellEdit. No habilites los dos, debes elegir. onFocus significa un solo clic, CellEdit significa doble clic. Si habilitas ambos, obtienes un comportamiento inesperado en los bits de tu cuadrícula que no quisiste poder editar.
En el nivel columnDefs, tiene las mismas opciones. Pero esta vez, debe configurar tanto CellEdit como onFocus, y debe establecer cellEdit en falso en las celdas que no quiera editar, este no es el valor predeterminado
La documentación dice que su plantilla de celda editable puede ser:
<input ng-class = "''colt'' + col.index" ng-input = "COL_FIELD" />
en realidad tiene que ser:
<input ng-class = "''colt'' + col.index" ng-input = "COL_FIELD" ng-model = "COL_FIELD" />
Para desencadenar un evento de guardado cuando perdemos el foco, hemos creado una directiva de desenfoque, la lógica que encontré en : AngularJS y ng-grid: guardar automáticamente los datos en el servidor después de cambiar una celda
Esto también significa cambiar cada plantilla de celda editable para llamar a ng-blur, que puede ver al final de la plantilla de celda editable
Obtenemos dos eventos de desenfoque cuando salimos del campo (al menos en Chrome), por lo que usamos un temporizador para que solo se procese uno de ellos. Feo, pero funciona.
También he creado una publicación de blog que hace un recorrido más completo de este código: http://technpol.wordpress.com/2013/12/06/editable-nggrid-with-both-dropdowns-and-selects/
Parece que encontré una solución gracias a la lista de correo Angular. Se señaló que a AngularJS le falta el evento onBlur (así como el onFocus). Sin embargo, esto se puede superar agregando una directiva "simple".
angular.module(''myApp.ngBlur'', [])
.directive(''ngBlur'', function () {
return function (scope, elem, attrs) {
elem.bind(''blur'', function () {
scope.$apply(attrs.ngBlur);
});
};
});
Como información, hay otro ejemplo de implementación relacionada con la directiva de eventos de desenfoque here .
Luego, el resto del código en el controlador se ve así:
// 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() ...
}
Quizás esto sea nuevo, pero ng-grid en realidad publica eventos que se pueden usar para implementar una actualización simple en el cambio.
Referencia del evento: https://github.com/angular-ui/ng-grid/wiki/Grid-Events
Código de ejemplo (agregar al controlador donde configura la cuadrícula):
$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 a tener en cuenta es que el evento se activará incluso si no se han realizado cambios, por lo que es posible que desee verificar los cambios antes de enviarlos al servidor (por ejemplo, a través de ''ngGridEventStartCellEdit'')
Si está utilizando UI Grid 3.0, ese evento es: uiGridEventEndCellEdit
$scope.$on(''uiGridEventEndCellEdit'', function (data) {
console.log(data.targetScope.row.entity);
}