57 votes

AngularJS - Validation côté serveur et formulaires côté client

Je suis en train de chercher à comprendre comment faire les choses suivantes:

Quelle est la manière acceptée de déclarer un formulaire. À ma connaissance, vous déclarez simplement le formulaire en HTML et ajoutez des directives ng-model comme ceci:

ng-model="item.name"

Quoi envoyer au serveur. Je peux simplement envoyer l'objet item au serveur en tant que JSON, et l'interpréter. Ensuite, je peux effectuer une validation sur l'objet. S'il échoue, je lance une erreur JSON, et renvoie quoi exactement? Existe-t-il une façon acceptée de le faire? Comment puis-je pousser les erreurs de validation du serveur vers le client de manière agréable?

J'ai vraiment besoin d'un exemple, mais la documentation d'Angular est assez difficile à comprendre.

Modifier: Il semble que j'aie mal posé ma question.

Je sais comment valider côté client et comment gérer les erreurs/succès en tant que rappels de promesses. Ce que je veux savoir, c'est la manière acceptée de regrouper les messages d'erreur CÔTÉ SERVEUR vers le client. Disons que j'ai un formulaire d'inscription avec nom d'utilisateur et mot de passe. Je ne veux pas interroger le serveur pour les noms d'utilisateur et ensuite utiliser Angular pour déterminer s'il existe un doublon. Je veux envoyer le nom d'utilisateur au serveur, valider qu'aucun autre compte n'existe avec le même nom, puis soumettre le formulaire. En cas d'erreur, comment la renvoyer?

Que faire pour envoyer les données au serveur telles quelles (clés et valeurs) avec un champ d'erreur ajouté comme ceci:

{
  ...données...

  "erreurs": [
    {
      "contexte": null,
      "message": "Un message d'erreur détaillé.",
      "nomException": null
    }
  ]
}

Puis lier au DOM.

0 votes

Consultez le module $resource. C'est exactement ce que vous cherchez probablement. stackoverflow.com/questions/13269882/…

0 votes

64voto

Derek Ekins Points 5524

J'ai également expérimenté ce genre de choses récemment et j'ai créé cette démo. Je pense que ça fait ce dont vous avez besoin.

Configurez votre formulaire comme d'habitude avec les validations côté client que vous voulez utiliser :

            Vous devez entrer une valeur ici
            {{myForm.firstName.$error.serverMessage}}

            {{myForm.lastName.$error.serverMessage}}

        Soumettre

Notez que j'ai ajouté un serverMessage pour chaque champ :

{{myForm.firstName.$error.serverMessage}}

C'est un message personnalisable qui vient du serveur et il fonctionne de la même manière qu'un autre message d'erreur (autant que je puisse dire).

Voici le contrôleur :

function MyCtrl($scope, $parse) {
      var pretendThisIsOnTheServerAndCalledViaAjax = function(){
          var fieldState = {firstName: 'VALID', lastName: 'VALID'};
          var allowedNames = ['Bob', 'Jill', 'Murray', 'Sally'];

          if (allowedNames.indexOf($scope.firstName) == -1) fieldState.firstName = 'Valeurs autorisées : ' + allowedNames.join(',');
          if ($scope.lastName == $scope.firstName) fieldState.lastName = 'Votre nom de famille doit être différent de votre prénom';

          return fieldState;
      };
      $scope.submit = function(){
          var serverResponse = pretendThisIsOnTheServerAndCalledViaAjax();

          for (var fieldName in serverResponse) {
              var message = serverResponse[fieldName];
              var serverMessage = $parse('myForm.'+fieldName+'.$error.serverMessage');

              if (message == 'VALID') {
                  $scope.myForm.$setValidity(fieldName, true, $scope.myForm);
                  serverMessage.assign($scope, undefined);
              }
              else {
                  $scope.myForm.$setValidity(fieldName, false, $scope.myForm);
                  serverMessage.assign($scope, serverResponse[fieldName]);
              }
          }
      };
}

Je fais semblant d'appeler le serveur dans pretendThisIsOnTheServerAndCalledViaAjax, vous pouvez le remplacer par un appel ajax, le point est simplement de retourner l'état de validation pour chaque champ. Dans ce cas simple, j'utilise la valeur VALID pour indiquer que le champ est valide, toute autre valeur est traitée comme un message d'erreur. Vous voudrez peut-être quelque chose de plus sophistiqué !

Une fois que vous avez l'état de validation du serveur, il vous suffit de mettre à jour l'état dans votre formulaire.

Vous pouvez accéder au formulaire à partir du scope, dans ce cas le formulaire s'appelle myForm, donc $scope.myForm vous donne le formulaire. (La source du contrôleur de formulaire est ici si vous voulez en savoir plus sur son fonctionnement).

Ensuite, vous voulez dire au formulaire si le champ est valide/invalide :

$scope.myForm.$setValidity(fieldName, true, $scope.myForm);

ou

$scope.myForm.$setValidity(fieldName, false, $scope.myForm);

Nous devons également définir le message d'erreur. Tout d'abord, obtenez l'accessoir pour le champ en utilisant $parse. Ensuite, attribuez la valeur du serveur.

var serverMessage = $parse('myForm.'+fieldName+'.$error.serverMessage');
serverMessage.assign($scope, serverResponse[fieldName]);

2 votes

Vous pouvez utiliser ng-submit à la place de ng-click et contourner onsubmit.

0 votes

Belle solution. Mais deux remarques: 1) ce serait génial d'automatiser le processus, ainsi je n'aurais pas à mettre ces "span ng-show" sous chaque champ pour les erreurs client et serveur; 2) le serveur pourrait en fait envoyer plus d'une erreur pour le même champ et j'aimerais les afficher toutes, afin que l'utilisateur ne soit pas maudit si chaque tentative de remplir le champ donne lieu à une nouvelle plainte du serveur; 3) je ne suis pas sûr dans quels cas $ dirty est nécessaire - peut-être devrions-nous également effacer les erreurs serveur si l'utilisateur modifie la valeur du champ ? En bref - une solution configurable (plugin Angular) serait géniale.

2 votes

JustAMartin, il s'agit simplement d'un code exemple et non d'une implémentation complète. Vous devrez implémenter tout ce qui est nécessaire par vous-même. N'hésitez pas à publier le vôtre en tant que réponse supplémentaire.

17voto

yanoo Points 307

J'ai une solution similaire à celle de Derek, décrite sur le blog de codetunes. TL;DR :

  • Afficher une erreur de manière similaire à la solution de Derek :

    {{errors.fieldName}}
  • Ajouter une directive qui nettoiera une erreur lorsque l'utilisateur change l'entrée :

    angular.module('app').directive 'serverError', ->
      {
        restrict: 'A'
        require: '?ngModel'
        link: (scope, element, attrs, ctrl) ->
          element.on 'change', ->
            scope.$apply ->
              ctrl.$setValidity('server', true)
      }
  • Gérer une erreur en transmettant le message d'erreur à la portée et en indiquant que le formulaire comporte une erreur :

    errorCallback = (result) ->
      # Le serveur renverra quelque chose comme :
      # { errors: { name: ["Doit être unique"] } }
      angular.forEach result.data.errors, (errors, field) ->
        # Indiquer au formulaire que le champ est invalide
        $scope.form[field].$setValidity('server', false)
        # Conserver les messages d'erreur du serveur
        $scope.errors[field] = errors.join(', ') 

J'espère que cela vous sera utile :)

2 votes

Est-ce que cela fonctionne pour angular-1.3/1.4 ? La valeur de $scope.form semble être indéfinie même lorsque le formulaire a été nommé et lorsque j'essaie d'y accéder avec le formulaire ou le nom du formulaire, je vois que c'est indéfini.

0 votes

@DivKis01 avez-vous ajouté name="form" au formulaire ? Depuis les docs : "Nom du formulaire. Si spécifié, le contrôleur de formulaire sera publié dans la portée associée, sous ce nom."

0 votes

Oui, j'ai ajouté le nom="form"

5voto

Yosh Points 141

Eh bien, la réponse donnée par Derek Ekins est très intéressante à travailler. Mais : Si vous désactivez le bouton de soumission avec ng-disable="myForm.$invalid" - le bouton ne se réactivera pas automatiquement car l'état d'erreur basé sur le serveur ne semble pas être modifié. Même si vous modifiez TOUS les champs d'un formulaire pour qu'ils respectent les entrées valides (basées sur la validation côté client).

3 votes

Si vous souhaitez pouvoir désactiver le bouton de soumission, vous devrez déclencher la validation lorsque votre entrée change, sinon il n'y a aucun moyen de savoir si elle est valide. Je peux faire un exemple de cela si vous le souhaitez vraiment.

0 votes

Oui, je veux une validation sur l'entrée pour l'unicité des codes-barres. J'aime votre implémentation, d'ailleurs, cela aide beaucoup.

3voto

kfis Points 2826

Si vous choisissez ngResource, cela ressemblerait à ceci

var Item = $resource('/items/');
$scope.item = new Item();
$scope.submit = function(){
  $scope.item.$save(
    function(data) {
        //Yahooooo :)
    }, function(response) {
        //oh noooo :(
        //Je ne suis pas sûr, mais votre réponse JSON personnalisée devrait être contenue dans response.data, inspectez simplement l'objet réponse
    }
  );
};

La chose la plus importante est que le code de votre réponse HTTP doit être un 4xx pour entrer dans la fonction de rappel d'échec.

3voto

matsko Points 5493

Par défaut, le formulaire est soumis normalement. Si vous ne fournissez pas de propriété de nom pour chaque champ dans le formulaire, alors il ne soumettra pas les données correctes. Ce que vous pouvez faire, c'est capturer le formulaire avant qu'il ne soit soumis et soumettre vous-même ces données via ajax.

Et ensuite dans votre fonction $scope.onSubmit():

$scope.onSubmit = function() {
  var data = {
    'name' : $scope.item.name
  };
  $http.post(url, data)
    .success(function() {
    })
    .failure(function() {

    });
};

Vous pouvez aussi valider les données en configurant des attributs requis.

0 votes

@mastko. J'ai vu votre tutoriel sur le dressage des formulaires en AngularJs 1.3 J'ai vraiment aimé cela mais j'ai quelques questions concernant ma mise en œuvre. Je voudrais en discuter par email, si possible.

0 votes

@Naomi merci de me contacter via mon site web.

0 votes

Je pense avoir essayé via Twitter, je n'ai pas vu d'autres moyens de communication. J'ai pu résoudre mes problèmes, donc tout fonctionne maintenant correctement. J'ai combiné plusieurs idées que j'ai vues sur le web et finalement, j'ai réussi à ne lancer les validateurs que lorsque quelque chose change dans le formulaire, pas au chargement.

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