AngularJS et ng-grid-enregistrer automatiquement les données sur le serveur après qu'une cellule a été changée
mon étui est assez simple. Un utilisateur, après avoir édité une cellule (enableCellEdit: true), doit avoir les données "automatiquement" envoyées au serveur (sur cell blur). J'ai essayé différentes approches, mais aucune n'a fonctionné correctement. J'ai une grille minimaliste:
// 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}
]
};
par exemple, j'ai essayé de regarder le modèle de données passé à la grille. Mais cela ne me rendra pas la cellule éditée:
$scope.$watch('myData', function (foo) {
// myModel.$update()
}, true);
j'ai essayé de jouer avec la "nggrideventdata" événement de données, mais il ne se déclenche pas après la cellule modifier
$scope.$on('ngGridEventData', function (e, gridId) {
// myModel.$update()
});
finalement, j'ai essayé d'observer une cellule. Cependant, cela ne fonctionne que pour une rangée par la moyenne de la propriété "selectedCell" de la grille:
$scope.selectedRow = [];
$scope.gridOptions = {
selectedItems: $scope.selectedRow,
}
$scope.$watch('selectedRow', function (foo) {
console.log(foo)
}, true);
Est-il un ng-grille plugin nécessaire? Je ne peux pas croire que ce n'est pas quelque chose qui sort de la boîte.
auriez-vous un pointeur / snippet comment je pourrais résoudre l'auto Enregistrer / Envoyer à la le serveur?
7 réponses
peut-être que c'est nouveau mais ng-grid publie en fait des événements qui peuvent être utilisés pour mettre en œuvre une simple mise à jour sur le changement.
référence de L'événement: https://github.com/angular-ui/ng-grid/wiki/Grid-Events
exemple de code (Ajouter au contrôleur où vous avez configuré la grille):
$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
});
une chose à noter est que l'événement se déclenchera même si aucun changement n'a été fait, donc vous pouvez toujours vouloir vérifier pour les modifications avant l'envoi au serveur (par exemple via 'ngGridEventStartCellEdit')
j'ai trouvé ce que je pense être une bien meilleure solution:
cellEditableTemplate = "<input ng-class=\"'colt' + col.index\" ng-input=\"COL_FIELD\" ng-model=\"COL_FIELD\" ng-change=\"updateEntity(row.entity)\"/>"
en utilisant ng-change de cette façon, updateEntity sera appelé avec l'objet entier (ligne) qui a été modifié et vous pouvez le renvoyer au serveur. Vous n'avez pas besoin de nouvelles variables. Une lacune de la solution précédente était que lorsque vous cliquiez pour commencer à éditer le champ, il serait toujours vide au lieu de la valeur originale avant que vous commenciez à éditer.
qui provoquera l'appel de updateEntity() sur chaque touche. Si cela est trop fréquent pour vous, vous pouvez utiliser un timeout avant de poster sur le serveur, ou tout simplement utiliser updateEntity() pour enregistrer l'id que vous voulez pousser, puis utiliser ng-blur pour afficher l'id enregistré.
il semble que j'ai trouvé une solution grâce à L'angulaire mailing list . Il a été souligné Qu'AngularJS manque l'événement onBlur (ainsi que l'onFocus). Toutefois, on peut remédier à cette situation en ajoutant une directive "simple".
angular.module('myApp.ngBlur', [])
.directive('ngBlur', function () {
return function (scope, elem, attrs) {
elem.bind('blur', function () {
scope.$apply(attrs.ngBlur);
});
};
});
comme information, il y a un autre exemple de mise en œuvre lié à la directive flou event ici .
ensuite, le reste du code dans le contrôleur regarde comme:
// 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() ...
}
j'ai passé du temps à rassembler les morceaux pour la grille ng 2.x. J'ai encore un problème avec le fait de devoir cliquer deux fois pour éditer une ligne, mais je pense que c'est un problème de bootstrap, pas un problème de ngGrid, ça ne se produit pas dans mon code échantillon (qui n'a pas encore de bootstrap).
j'ai aussi implémenté une logique similaire dans un tutoriel pour ui-grid 3.0, qui est encore en version bêta mais deviendra bientôt la version préférée. Ceci peut être trouvé à: http://technpol.wordpress.com/2014/08/23/upgrading-to-ng-grid-3-0-ui-grid / , et fournit une api beaucoup plus facile et plus propre pour cette fonctionnalité.
pour le 2.version x, pour illustrer tous les bits, j'ai créé un plunker en cours d'exécution qui a une grille modifiable avec à la fois un dropdown et un champ d'entrée, utilise la directive ngBlur, et utilise un $ timeout pour éviter les sauvegardes en double sur la mise à jour: http://plnkr.co/edit/VABAEu?p=preview
les bases du code sont:
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'
};
});
quand vous lancez ce plunker, et que la mise au point se déclenche, vous devriez voir sur la console le déclenchement de la mise à jour.
j'ai aussi inclus un fichier README.md dans le plunker avec quelques pensées sur des choses qui m'a donné de la difficulté, de les reproduire ici.
la fonctionnalité ici est que j'ai une liste de personnes, ces personnes les noms, âges et de statuts. En ligne avec ce que nous pourrions faire dans un réel app, l'état est un code, et nous voulons montrer à la décoder. Conséquent nous avons une liste de codes d'état (qui pourrait dans une application réelle venir de la base de données), et nous avons un filtre pour associer le code au décodage.
ce que nous voulons, c'est deux choses. Nous aimerions pouvoir éditer le nom dans une zone de saisie, et de modifier le statut dans une liste déroulante.
commente ce que j'ai appris sur ce plunk.
-
au gridOptions niveau, Il ya à la fois facilecelleditonfocus et enableCellEdit. N'activez pas les deux, vous devez choisir. onFocus signifie simple clic, CellEdit signifie double clic. Si vous activez les deux alors vous obtenez un comportement inattendu sur les bits de votre grille, vous ne voulez pas pour être modifiable
-
au niveau des chroniqueurs, vous avez les mêmes options. Mais cette fois, vous besoin de définir à la fois CellEdit et onFocus, et vous avez besoin de définir cellEdit à faux sur les cellules vous ne voulez pas d'édition-ce n'est pas la valeur par défaut
-
la documentation dit que votre modèle de cellule modifiable peut être:
en fait, il doit être:
-
pour déclencher un événement de sauvegarde lorsque nous perdons de vue, nous avons créé une directive flou, la logique pour laquelle j'ai trouvé dans stackoverflow: AngularJS et ng-grid-enregistrer automatiquement les données au serveur après qu'une cellule a été changée
-
cela signifie également changer chaque modèle de cellule modifiable à l'appel ng-blur, qui vous pouvez voir à la fin du modèle de cellule modifiable
-
nous obtenons deux événements flous lorsque nous quittons le terrain (au moins en Google Chrome), de sorte que nous utilisez une minuterie pour qu'un seul d'entre eux soit traité. Moche, mais ça fonctionne.
j'ai aussi créé un billet de blog qui fait un tour d'horizon plus complet de ce code: http://technpol.wordpress.com/2013/12/06/editable-nggrid-with-both-dropdowns-and-selects /
si vous utilisez la grille D'assurance-chômage 3.0 cet événement est: uiGridEventEndCellEdit
$scope.$on('uiGridEventEndCellEdit', function (data) {
console.log(data.targetScope.row.entity);
}
C'est une amélioration de la réponse qui a quelques défauts: - il déclenche une exception JS, comme indiqué dans l'un des commentaires de réponse - la saisie des données dans la cellule n'est pas conservé dans la grille - la méthode updateEntity n'illustre pas comment sauvegarder les données d'entrée
afin de supprimer l'exception, créer un attribut scope et l'ajouter à 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'/>";
notez que l'appel ng-flou à updateEntity maintenant inclut cellValue comme argument. Ensuite, mettez à jour le gestionnaire de flou updateEntity pour inclure l'argument et mettez à jour la grille:
$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() ...
};
je suis maintenant en mesure de voir les changements sur l'écran ainsi que de déclencher des mises à jour basées sur la cellule back-end.
comme PaulL l'a mentionné dans l'un des commentaires, ui-grid a maintenant une fonctionnalité de rowEdit conçue pour permettre l'enregistrement de toute la ligne lorsqu'elle est éditée. Voir http://ui-grid.info/docs/#/tutorial/205_row_editable .