web-dev-qa-db-fra.com

Valider les champs après que l'utilisateur a quitté un champ

Avec AngularJS, je peux utiliser ng-pristine ou ng-dirty pour détecter si l'utilisateur a saisi le champ. Cependant, je souhaite effectuer la validation côté client uniquement après que l'utilisateur a quitté la zone de champ. En effet, lorsqu'un utilisateur entre dans un champ tel qu'un courrier électronique ou un téléphone, il reçoit toujours une erreur jusqu'à ce qu'il ait terminé de saisir son courrier électronique au complet. Il ne s'agit donc pas d'une expérience utilisateur optimale.

Exemple


UPDATE:

Angular est désormais livré avec un événement de flou personnalisé: https://docs.angularjs.org/api/ng/directive/ngBlur

91
mcranston18

Angular 1.3 a maintenant ng-model-options, et vous pouvez définir l’option sur { 'updateOn': 'blur'} par exemple, et vous pouvez même vous rebounce lorsque l’utilisation est trop rapide ou que vous souhaitez enregistrer quelques opérations DOM coûteuses. (comme un modèle écrivant dans plusieurs emplacements DOM et vous ne voulez pas qu'un cycle $ digest se produise à chaque touche enfoncée)

https://docs.angularjs.org/guide/forms#custom-triggers et https://docs.angularjs.org/guide/forms#non-immediate-debounce-model -updates

Par défaut, toute modification du contenu déclenche une mise à jour du modèle et une validation du formulaire. Vous pouvez remplacer ce comportement à l'aide de la directive ngModelOptions pour ne lier que la liste d'événements spécifiée. C'est à dire. ng-model-options = "{updateOn: 'blur'}" ne sera mis à jour et validé que lorsque le contrôle aura perdu le focus. Vous pouvez définir plusieurs événements à l'aide d'une liste délimitée par des espaces. C'est à dire. ng-model-options = "{updateOn: 'flou de souris'}"

Et rebondir

Vous pouvez retarder la mise à jour/validation du modèle en utilisant la clé debounce avec la directive ngModelOptions. Ce délai s'appliquera également aux analyseurs syntaxiques, aux validateurs et aux indicateurs de modèle tels que $ dirty ou $ Pristine.

C'est à dire. ng-model-options = "{debounce: 500}" attendra une demi-seconde depuis le dernier changement de contenu avant de déclencher la mise à jour du modèle et la validation du formulaire.

75
pocesar

À partir de la version 1.3.0, cela peut facilement être fait avec $touched, ce qui est vrai une fois que l'utilisateur a quitté le champ.

<p ng-show="form.field.$touched && form.field.$invalid">Error</p>

https://docs.angularjs.org/api/ng/type/ngModel.NgModelController

92
user1506145

J'ai résolu ce problème en développant ce que @jlmcdonald avait suggéré. J'ai créé une directive qui serait automatiquement appliquée à tous les éléments d'entrée et de sélection:

var blurFocusDirective = function () {
    return {
        restrict: 'E',
        require: '?ngModel',
        link: function (scope, Elm, attr, ctrl) {
            if (!ctrl) {
                return;
            }

            Elm.on('focus', function () {
                Elm.addClass('has-focus');

                scope.$apply(function () {
                    ctrl.hasFocus = true;
                });
            });

            Elm.on('blur', function () {
                Elm.removeClass('has-focus');
                Elm.addClass('has-visited');

                scope.$apply(function () {
                    ctrl.hasFocus = false;
                    ctrl.hasVisited = true;
                });
            });

            Elm.closest('form').on('submit', function () {
                Elm.addClass('has-visited');

                scope.$apply(function () {
                    ctrl.hasFocus = false;
                    ctrl.hasVisited = true;
                });
            });

        }
    };
};

app.directive('input', blurFocusDirective);
app.directive('select', blurFocusDirective);

Ceci ajoutera les classes has-focus et has-visited à divers éléments au fur et à mesure que l'utilisateur focalise/visite les éléments. Vous pouvez ensuite ajouter ces classes à vos règles CSS pour afficher les erreurs de validation:

input.has-visited.ng-invalid:not(.has-focus) {
    background-color: #ffeeee;   
}

Cela fonctionne bien en ce que les éléments obtiennent toujours $ propriétés invalides, etc., mais le CSS peut être utilisé pour offrir une meilleure expérience à l'utilisateur.

32
lambinator

J'ai réussi à faire cela avec un peu simple de CSS. Cela nécessite que les messages d'erreur soient frères de l'entrée à laquelle ils se rapportent et qu'ils ont une classe de error.

:focus ~ .error {
    display:none;
}

Une fois ces deux conditions remplies, tout message d'erreur lié à un champ de saisie ciblé sera masqué, ce que je pense que angularjs devrait faire de toute façon. On dirait un oubli.

16
nicholas

En ce qui concerne @ lambinator's solution ... je reçois l'erreur suivante dans angular.js 1.2.4:

Erreur: [$ rootScope: inprog] $ digest déjà en cours

Je ne sais pas si j'ai fait quelque chose de mal ou s'il s'agit d'un changement angulaire, mais la suppression des instructions scope.$apply a résolu le problème et les classes/états sont toujours mis à jour.

Si vous voyez également cette erreur, essayez ce qui suit:

var blurFocusDirective = function () {
  return {
    restrict: 'E',
    require: '?ngModel',
    link: function (scope, Elm, attr, ctrl) {
      if (!ctrl) {
        return;
      }
      Elm.on('focus', function () {
        Elm.addClass('has-focus');
        ctrl.$hasFocus = true;
      });

      Elm.on('blur', function () {
        Elm.removeClass('has-focus');
        Elm.addClass('has-visited');
        ctrl.$hasFocus = false;
        ctrl.$hasVisited = true;
      });

      Elm.closest('form').on('submit', function () {
        Elm.addClass('has-visited');

        scope.$apply(function () {
          ctrl.hasFocus = false;
          ctrl.hasVisited = true;
        });
      });
    }
  };
};
app.directive('input', blurFocusDirective);
app.directive('select', blurFocusDirective);
4
melcher

Cela semble être implémenté en standard dans les nouvelles versions de angular.

Les classes ng-intouched et ng-touched sont définies respectivement avant et après que l'utilisateur se soit concentré sur un élément validé.

CSS

input.ng-touched.ng-invalid {
   border-color: red;
}
4
Arg0n

Cela pourrait fonctionner pour vous d'écrire une directive personnalisée qui enveloppe la méthode javascript blur () (et exécute une fonction de validation lorsqu'elle est déclenchée); il existe un problème Angular qui en contient un exemple (ainsi qu'une directive générique pouvant se lier à d'autres événements non pris en charge de manière native par Angular):

https://github.com/angular/angular.js/issues/1277

Si vous ne voulez pas emprunter cette voie, votre autre option serait de configurer $ watch sur le champ, ce qui déclenche à nouveau la validation lorsque le champ est rempli.

2
jlmcdonald

Voici un exemple utilisant ng-messages (disponible dans angular 1.3) et une directive personnalisée.

Le message de validation est affiché sur le flou pour la première fois que l'utilisateur quitte le champ de saisie, mais lorsqu'il corrige la valeur, le message de validation est immédiatement supprimé (et non plus sur le flou).

JavaScript

myApp.directive("validateOnBlur", [function() {
    var ddo = {
        restrict: "A",
        require: "ngModel",
        scope: {},
        link: function(scope, element, attrs, modelCtrl) {
            element.on('blur', function () {
                modelCtrl.$showValidationMessage = modelCtrl.$dirty;
                scope.$apply();
            });
        }
    };
    return ddo;
}]);

HTML

<form name="person">
    <input type="text" ng-model="item.firstName" name="firstName" 
        ng-minlength="3" ng-maxlength="20" validate-on-blur required />
    <div ng-show="person.firstName.$showValidationMessage" ng-messages="person.firstName.$error">
        <span ng-message="required">name is required</span>
        <span ng-message="minlength">name is too short</span>
        <span ng-message="maxlength">name is too long</span>
    </div>
</form>

PS N'oubliez pas de télécharger et d'inclure ngMessages dans votre module:

var myApp = angular.module('myApp', ['ngMessages']);
2
jurgemar

Pour aller plus loin dans les réponses données, vous pouvez simplifier le marquage des entrées en utilisant des pseudo-classes CSS3 et en ne marquant les champs visités qu’avec une classe retardant l’affichage des erreurs de validation jusqu’à ce que l’utilisateur ne se concentre plus sur le champ:

(l'exemple nécessite jQuery)

JavaScript

module = angular.module('app.directives', []);
module.directive('lateValidateForm', function () {
    return {
        restrict: 'AC',
        link: function (scope, element, attrs) {
            $inputs = element.find('input, select, textarea');

            $inputs.on('blur', function () {
                $(this).addClass('has-visited');
            });

            element.on('submit', function () {
                $inputs.addClass('has-visited');
            });
        }
    };
});

CSS

input.has-visited:not(:focus):required:invalid,
textarea.has-visited:not(:focus):required:invalid,
select.has-visited:not(:focus):required:invalid {
  color: #b94a48;
  border-color: #ee5f5b;
}

HTML

<form late-validate-form name="userForm">
  <input type="email" name="email" required />
</form>
2
Patrick Glandien

basé sur @nicolas répondre .. CSS pur devrait l'astuce, il ne montrera que le message d'erreur sur le flou

<input type="email" id="input-email" required
               placeholder="Email address" class="form-control" name="email"
               ng-model="userData.email">
        <p ng-show="form.email.$error.email" class="bg-danger">This is not a valid email.</p>

CSS

.ng-invalid:focus ~ .bg-danger {
     display:none;
}
2
keithics

ng-model-options dans AngularJS 1.3 (version bêta à ce jour) supporte {updateOn: 'blur'}. Pour les versions précédentes, quelque chose comme ce qui suit a fonctionné pour moi:

myApp.directive('myForm', function() {
  return {
    require: 'form',
    link: function(scope, element, attrs, formController) {
      scope.validate = function(name) {
        formController[name].isInvalid
            = formController[name].$invalid;
      };
    }
  };
});

Avec un modèle comme celui-ci:

<form name="myForm" novalidate="novalidate" data-my-form="">
<input type="email" name="eMail" required="required" ng-blur="validate('eMail')" />
<span ng-show="myForm.eMail.isInvalid">Please enter a valid e-mail address.</span>
<button type="submit">Submit Form</button>
</form>
1
Terence Bandoian

Utiliser le champ state $ touched Le champ a été touché pour cela, comme indiqué dans l'exemple ci-dessous.

<div ng-show="formName.firstName.$touched && formName.firstName.$error.required">
    You must enter a value
</div>
1
Kushal Sharma

En utilisant ng-focus, vous pouvez atteindre votre objectif. vous devez fournir ng-focus dans votre champ de saisie. Et tout en écrivant vos dérivés ng-show, vous devez écrire une logique qui n’est pas égale. Comme le code ci-dessous:

<input type="text" class="form-control" name="inputPhone" ng-model="demo.phoneNumber" required ng-focus> <div ng-show="demoForm.inputPhone.$dirty && demoForm.inputPhone.$invalid && !demoForm.inputPhone.$focused"></div>

0
Ray

Nous pouvons utiliser les fonctions onfocus et onblur. Serait simple et meilleur.

<body ng-app="formExample">
  <div ng-controller="ExampleController">
  <form novalidate class="css-form">
    Name: <input type="text" ng-model="user.name" ng-focus="onFocusName='focusOn'" ng-blur="onFocusName=''" ng-class="onFocusName" required /><br />
    E-mail: <input type="email" ng-model="user.email" ng-focus="onFocusEmail='focusOn'" ng-blur="onFocusEmail=''" ng-class="onFocusEmail" required /><br />
  </form>
</div>

<style type="text/css">
 .css-form input.ng-invalid.ng-touched {
    border: 1px solid #FF0000;
    background:#FF0000;
   }
 .css-form input.focusOn.ng-invalid {
    border: 1px solid #000000;
    background:#FFFFFF;
 }
</style>

Essayez ici:

http://plnkr.co/edit/NKCmyru3knQiShFZ96tp?p=preview

0
Naveen Kumar

outI utilisé une directive. Voici le code:

app.directive('onBlurVal', function () {
    return {
        restrict: 'A',
        link: function (scope, element, attrs, controller) {

            element.on('focus', function () {
                element.next().removeClass('has-visited');
                element.next().addClass('has-focus');
            });

            element.on('blur', function () {

                element.next().removeClass('has-focus');
                element.next().addClass('has-visited');
            });
        }
    }
})

Tous mes contrôles d’entrée ont un élément span comme élément suivant. C’est là que mon message de validation est affiché. Ainsi, la directive en tant qu’attribut est ajoutée à chaque contrôle d’entrée.

J'ai également (facultatif) .has-focus et a visité la classe css dans mon fichier css que vous voyez être référencé dans la directive.

REMARQUE: n'oubliez pas d'ajouter exactement 'on-blur-val' de cette manière à votre contrôle d'entrée sans les apostrophes.

0
user3676608

Si vous utilisez bootstrap 3 et lesscss, vous pouvez activer la validation sur flou avec l'extrait de code suivant:

:focus ~ .form-control-feedback.glyphicon-ok {
  display:none;
}

:focus ~ .form-control-feedback.glyphicon-remove {
  display:none;
}

.has-feedback > :focus {
  & {
    .form-control-focus();
  }
}
0
Stefan

Vous pouvez définir de manière dynamique la classe css has-error (en supposant que vous utilisez bootstrap) à l'aide de ng-class et d'une propriété sur la portée du contrôleur associé:

plunkr: http://plnkr.co/edit/HYDlaTNThZE02VqXrUCH?p=info

HTML:

<div ng-class="{'has-error': badEmailAddress}">
    <input type="email" class="form-control" id="email" name="email"
        ng-model="email" 
        ng-blur="emailBlurred(email.$valid)">
</div>

Manette:

$scope.badEmailAddress = false;

$scope.emailBlurred = function (isValid) {
    $scope.badEmailAddress = !isValid;
};
0
Mike