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 est entré dans le champ. Cependant, je veux faire la validation côté client seulement après que l'utilisateur a quitté la zone de champ. En effet, lorsqu'un utilisateur entre dans un champ comme e-mail ou téléphone, ils seront toujours obtenir une erreur levée jusqu'à ce qu'ils ont terminé de taper leur e-mail complet, et ce n'est pas une expérience utilisateur optimale.

Exemple


Mise à jour:

Angulaire est maintenant livré avec une coutume le flou de l'événement: https://docs.angularjs.org/api/ng/directive/ngBlur

89
demandé sur isherwood 2013-04-04 01:36:02

17 réponses

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 rebondir, lorsque l'utilisation est soit en tapant trop vite, ou si vous voulez enregistrer quelques opérations DOM coûteuses (comme un modèle écrivant à plusieurs endroits DOM et vous ne voulez pas qu'un cycle $digest se produise sur]}

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

Par défaut, toute modification du contenu déclenchera une mise à jour du modèle et la validation d'un formulaire. Vous pouvez remplacer ce comportement en utilisant directive ngModelOptions pour se lier uniquement à la liste spécifiée d'événements. C'est-à-dire que ng-model-options="{ updateOn: 'blur' }" mettra à jour et validera seulement après le contrôle perd le focus. Vous pouvez définir plusieurs événements à l'aide d'un liste délimitée par des espaces. C'est-à-dire ng-model-options= "{ updateOn: 'mousedown flou' } "

Et anti-rebond

Vous peut retarder la mise à jour/validation du modèle en utilisant la touche debounce avec la directive ngModelOptions. Ce délai s'appliquera également aux analyseurs, validateurs et drapeaux de modèle comme $dirty ou $pristine.

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

74
répondu pocesar 2014-05-04 23:37:54

À Partir de la version 1.3.0 cela peut facilement être fait avec $touched, ce qui est vrai après que l'utilisateur a quitté le terrain.

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

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

90
répondu user1506145 2014-10-06 07:44:52

J'ai résolu cela en développant ce que @jlmcdonald a 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);

Cela ajoutera has-focus et has-visited classes à divers éléments que l'utilisateur se concentre/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 des propriétés $ invalid etc, mais le CSS peut être utilisé pour donner à l'utilisateur un meilleur expérience.

32
répondu lambinator 2013-06-04 18:28:18

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

:focus ~ .error {
    display:none;
}

Après avoir satisfait ces deux exigences, cela masquera tout message d'erreur lié à un champ de saisie ciblé, ce que je pense qu'angularjs devrait faire de toute façon. Semble comme un oubli.

16
répondu nicholas 2014-01-27 20:19:24

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

Les classes ng-intacte et ng-touché sont définies, respectivement, avant et après que l'utilisateur a dû se concentrer sur un validée élément.

CSS

input.ng-touched.ng-invalid {
   border-color: red;
}
4
répondu Arg0n 2014-09-24 09:35:32

Concernant la solution @lambinator... Je recevais l'erreur suivante dans angular.js 1.2.4:

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

Je ne suis pas sûr si j'ai fait quelque chose de mal ou s'il s'agit d'un changement D'angle, 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
répondu melcher 2017-06-06 14:08:05

Cela peut 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 y a un problème angulaire qui en a un exemple (ainsi qu'une directive générique qui peut se lier à d'autres événements non supportés nativement par Angular):

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

Si vous ne voulez pas aller dans cette voie, votre autre option serait de configurer $watch sur le terrain, déclenchant à nouveau la validation lorsque le champ est rempli.

2
répondu jlmcdonald 2013-04-03 22:13:02

Pour aller plus loin sur les réponses données, vous pouvez simplifier le marquage d'entrée en utilisant des pseudo-classes CSS3 et en marquant uniquement les champs visités avec une classe pour retarder l'affichage des erreurs de validation jusqu'à ce que l'utilisateur perde le focus sur le champ:

(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
répondu Patrick Glandien 2013-06-05 14:14:36

Basé sur la réponse @ nicolas.. CSS pur devrait l'astuce, il affichera seulement le message d'erreur sur 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
répondu keithics 2014-03-21 06:12:03

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é (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
répondu jurgemar 2014-08-06 10:44:01

Ng-model-options dans AngularJS 1.3 (beta à ce jour) est documenté pour prendre en charge {updateOn: 'blur'}. Pour les versions antérieures, 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
répondu Terence Bandoian 2014-05-22 18:22:53

Utiliser l'état du Champ $ 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
répondu Kushal Sharma 2017-04-20 08:36:52

Vous pouvez définir dynamiquement la classe CSS has-error (en supposant que vous utilisez bootstrap) en utilisant ng-class et 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>

Contrôleur:

$scope.badEmailAddress = false;

$scope.emailBlurred = function (isValid) {
    $scope.badEmailAddress = !isValid;
};
0
répondu Mike 2014-06-08 09:32:22

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

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

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

.has-feedback > :focus {
  & {
    .form-control-focus();
  }
}
0
répondu Stefan 2014-06-23 09:32:13

OutI a 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');
            });
        }
    }
})

Tout mon contrôle d'entrée a un élément span comme élément suivant, qui est l'endroit où mon message de validation est affiché et donc la directive en tant qu'attribut est ajoutée à chaque contrôle d'entrée.

J'ai aussi (facultatif).has-focus Et has-visited classe css dans mon fichier css que vous voyez être référencé dans la directive.

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

0
répondu user3676608 2014-07-08 13:39:18

En utilisant ng-focus, vous pouvez atteindre votre objectif. vous devez fournir ng-focus dans votre champ de saisie. Et en écrivant vos dérivés ng-show, vous devez écrire une logique pas égale aussi. 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
répondu Ray 2014-09-10 14:43:00

Nous pouvons utiliser les fonctions onfocus et onblur. Serait simple et le 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
répondu Naveen Kumar 2015-11-24 07:49:09