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.
UPDATE:
Angular est désormais livré avec un événement de flou personnalisé: https://docs.angularjs.org/api/ng/directive/ngBlur
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.
À 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
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.
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.
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);
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;
}
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.
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']);
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>
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;
}
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>
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>
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>
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:
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.
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();
}
}
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;
};