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
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.
À 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
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.
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.
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;
}
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);
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.
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>
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;
}
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']);
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>
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>
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;
};
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();
}
}
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
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>
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: