38 votes

AngularJS et ng-grid - sauvegarde automatique des données sur le serveur après le changement d'une cellule

Mon Cas d'Utilisation est assez simple. L'Utilisateur, après l'édition d'une Cellule (enableCellEdit: true), devraient avoir les données automatiquement envoyé au serveur (sur la cellule de flou). J'ai essayé différentes approches, mais aucun d'entre eux ont correctement fonctionné. J'ai un minimaliste de la grille:

// 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 transmis à la Grille. Mais cela ne sera pas de retour me la cellule modifiée:

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

J'ai essayé de jouer avec le "ngGridEventData" les données de l'événement, mais il ne se déclenche pas après la cellule modifier

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

Enfin, j'ai essayé d'observateur d'une Cellule. Toutefois, cela ne fonctionne que pour une ligne par la moyenne de la "selectedCell" propriété de la grille:

$scope.selectedRow = [];

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

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

Est-ce une ng-grille plugin nécessaire? Je ne peux pas croire qu'il n'est pas quelque chose hors de la boîte.

Auriez-vous un pointeur / extrait de comment je pourrais résoudre l'auto-enregistrer / envoyer vers le serveur?

30voto

chriswhitmore Points 321

C'est peut-être nouveau, mais ng-grille réellement publie des événements qui peuvent être utilisés pour mettre en œuvre une simple mise à jour du changement.

Événement de Référence: https://github.com/angular-ui/ng-grid/wiki/Grid-Events

Exemple de code (à ajouter dans le contrôleur de l'endroit où vous l'installation de 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 déclencheur, même si aucune modification n'a été faite, de sorte que vous pouvez toujours vérifier les modifications avant de les envoyer au serveur (par exemple via des "ngGridEventStartCellEdit')

21voto

J'ai trouvé ce que je pense, c'est tellement plus agréable de la solution:

  cellEditableTemplate = "<input ng-class=\"'colt' + col.index\" ng-input=\"COL_FIELD\" ng-model=\"COL_FIELD\" ng-change=\"updateEntity(row.entity)\"/>"

À l'aide de ng-changement de cette façon peut provoquer des updateEntity d'être appelé avec l'ensemble de l'objet (ligne) qui a été modifié et vous pouvez poster à nouveau sur le serveur. Vous n'avez pas besoin de tout nouveau portée des variables. Une carence de la solution précédente est que lorsque vous avez cliqué pour commencer à éditer le champ, il serait toujours vide au lieu de la valeur d'origine avant de vous commencé à modifier.

Qui sera la cause de updateEntity() doit être appelée à chaque frappe. Si c'est trop fréquent pour vous, vous pouvez utiliser un délai d'attente avant de poster sur le serveur, ou tout simplement utiliser updateEntity() pour enregistrer l'id que vous souhaitez pousser, et ensuite utiliser ng-flou de publier l'enregistrement de l'id.

17voto

Fabien Points 316

Il semble que j'ai trouvé une solution grâce à l'angle de la liste de diffusion. Il a été signalé que AngularJS est manquant, l'événement onBlur (ainsi que le onFocus). Cependant, cela peut être surmonté par l'ajout d'un "simple" de la directive.

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

Comme l'information, il est un autre exemple de mise en œuvre liées à l'effet de flou de l'événement directive ici.

Ensuite, le reste du code dans le contrôleur ressemble:

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

6voto

PaulL Points 714

J'ai passé un peu de temps à rassembler les morceaux de ce pour ng-grille 2.x. J'ai encore un problème avec le fait d'avoir à cliquer deux fois pour modifier une ligne, mais je pense que c'est un bootstrap question, pas une ngGrid problème, il ne se passe pas dans mon exemple de code (qui n'ont pas de bootstrap encore).

J'ai également mis en œuvre une logique similaire dans un tutoriel pour l'interface utilisateur de la grille 3.0, qui est encore en version bêta, mais deviendra bientôt la version préférée. Cela peut être trouvé à: http://technpol.wordpress.com/2014/08/23/upgrading-to-ng-grid-3-0-ui-grid/et fournit un beaucoup plus facile et plus propre api pour cette fonctionnalité.

Pour la 2.x version, pour illustrer tous les bits, j'ai créé une course plunker qui a une grille modifiable à la fois avec une liste déroulante et un champ de saisie, utilise le ngBlur directive, et utilise un $timeout d'éviter de dupliquer les enregistre sur la mise à jour: http://plnkr.co/edit/VABAEu?p=preview

Les bases de ce 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'
  };
});

Lorsque vous exécutez cette plunker, et le perdre de vue l'objectif de feux, vous devriez voir sur la console de la mise à jour de déclenchement de tir.

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, c'est que j'ai une liste de gens, ces gens les noms, âges et de statuts. En ligne avec ce que nous pourrions faire dans un vrai app, l'état est un code, et nous voulons montrer à la décoder. En conséquence nous avons un état de la liste des codes (qui pourrait, dans une application réelle proviennent de la base de données), et nous avons un filtre à la carte le code pour le décoder.

Ce que nous voulons sont deux choses. Nous aimerions être en mesure de modifier le nom dans une zone de saisie, et de modifier le statut dans une liste déroulante.

Commentaires sur des choses que j'ai appris sur ce plunk.

  1. Au gridOptions niveau, il y a deux enableCellEditOnFocus et enableCellEdit. Ne pas permettre à la fois, vous devez choisir. onFocus moyens seul clic, CellEdit à-dire le 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

  2. Au columnDefs niveau, vous avez les mêmes options. Mais cette fois, vous besoin de définir à la fois CellEdit et onFocus, et vous devez définir cellEdit à faux sur les cellules que vous ne voulez pas éditer - ce n'est pas la valeur par défaut

  3. La documentation dit que votre cellule modifiable modèle peut être:

    <input ng-classe="'colt' + col.l'indice de" ng-entrée="COL_FIELD" />

    en fait il doit être:

    <input ng-classe="'colt' + col.l'indice de" ng-entrée="COL_FIELD" ng-model="COL_FIELD" />

  4. Pour déclencher un enregistrement de l'événement quand nous perdons, nous avons créé un flou de la directive, la logique que j'ai trouvé dans stackoverflow: AngularJS et ng-grille - sauvegarde automatique des données vers le serveur après qu'une cellule a été modifiée

  5. Cela signifie aussi changer chaque cellule modifiable modèle d'appeler ng-flou, qui vous pouvez voir à la fin de la cellule modifiable modèle

  6. Nous obtenons deux flou événements lorsque nous quittons le terrain (au moins dans google Chrome), de sorte que nous utilisez une minuterie de sorte que seulement l'un d'eux est traité. Moche, mais ça fonctionne.

J'ai également créé un blog qui n'est plus approfondie de la soluce de ce code: http://technpol.wordpress.com/2013/12/06/editable-nggrid-with-both-dropdowns-and-selects/

1voto

C'est une amélioration de la réponse qui a quelques défauts: - il déclenche un JS exception, comme l'indique l'un de répondre à des commentaires de - la saisie des données dans la cellule n'est pas conservé dans la grille - le updateEntity méthode ne permet pas d'illustrer comment enregistrer 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 le ng-flou appel à updateEntity comprend maintenant cellValue comme argument. Ensuite, mettez à jour le updateEntity flou gestionnaire d'inclure l'argument et de la mise à jour de 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 pour déclencher les cellules à base de back-end mises à jour.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X