Par exemple, j'ai le formulaire où je suis montrant les erreurs de saisie du formulaire .
Je dois montrer un badge rouge (avec 'survoler pour afficher les erreurs') près de l'étiquette d'entrée s'il y a des erreurs. Si l'utilisateur survole ce badge rouge - il verra la liste des erreurs en utilisant AngularJS UI Bootstrap tooltip . Je ne veux pas mettre la liste des erreurs dans tooltip-html -unsafe attribut, car il n'est pas pratique de modifier et de maintenir.
Ce code est plus déclaratif:
<validation-tooltip ng-show="appForm.number.$invalid && appForm.number.$dirty">
<ul>
<li ng-show="appForm.number.$error.required">this field is required</li>
<li ng-show="appForm.number.$error.number">should be number</li>
<li ng-show="appForm.number.$error.min">minimum - 5</li>
<li ng-show="appForm.number.$error.max">miximum - 20</li>
</ul>
</validation-tooltip>
que ce code:
<span tooltip-html-unsafe="{{<ul><li>This field is required;</li><li>...</li></ul>}}">hover to show errors</span>
Comment puis-je écrire une telle directive de validation-info-bulle en utilisant AngularJS UI Bootstrap tooltip?
Ou pouvez-vous suggérer une autre approche pour conserver les messages d'erreur de validation?
Le validationTooltip est la directive principale. Il a les responsabilités suivantes:
- Définissez le modèle d'info-bulle à travers son contenu inclus
- Gardez une trace des expressions de validation afin qu'elles puissent être évaluées à chaque cycle de résumé.
- Exposer une API de contrôleur pour permettre aux directives valiationMessage de s'enregistrer
- Fournissez un attribut "cible" sur la directive pour spécifier le champ de formulaire auquel le badge (et l'info-bulle associée) sera lié.
Notes supplémentaires
Le modèle d'infobulle utilise la fonction de transclusion de la fonction de lien pour lier le modèle à la portée de la directive. Le modèle peut se lier à deux propriétés à portée:
- $ form : lié au modèle de formulaire défini dans la portée parent. c'est-à-dire $ scope.myForm
- Champ $ : lié au modèle form.name dans la portée parent. c'est-à-dire $ scope.myForm.myInput
Cela permet au modèle de se lier à des propriétés de validation telles que $ valid, $ invalid, $ pristine, $ dirty et $ error sans se référer directement au nom du formulaire ou au nom du champ de saisie. Par exemple, toutes les expressions suivantes sont des expressions de liaison valides:
Propriétés de $ form:
Propriétés du champ $:
Mise en œuvre de la directive
app.directive('validationTooltip', function ($timeout) {
return {
restrict: 'E',
transclude: true,
require: '^form',
scope: {},
template: '<span class="label label-danger span1" ng-show="errorCount > 0">hover to show err</span>',
controller: function ($scope) {
var expressions = [];
$scope.errorCount = 0;
this.$addExpression = function (expr) {
expressions.Push(expr);
}
$scope.$watch(function () {
var count = 0;
angular.forEach(expressions, function (expr) {
if ($scope.$eval(expr)) {
++count;
}
});
return count;
}, function (newVal) {
$scope.errorCount = newVal;
});
},
link: function (scope, element, attr, formController, transcludeFn) {
scope.$form = formController;
transcludeFn(scope, function (clone) {
var badge = element.find('.label');
var tooltip = angular.element('<div class="validationMessageTemplate tooltip-danger" />');
tooltip.append(clone);
element.append(tooltip);
$timeout(function () {
scope.$field = formController[attr.target];
badge.tooltip({
placement: 'right',
html: true,
title: clone
});
});
});
}
}
});
La directive validationMessage garde une trace des messages de validation à afficher dans l'info-bulle. Il utilise ng-if
pour définir l'expression à évaluer. Si il n'y a pas ng-if
trouvé sur l'élément, puis l'expression prend simplement la valeur true (toujours affichée).
app.directive('validationMessage', function () {
return {
restrict: 'A',
priority: 1000,
require: '^validationTooltip',
link: function (scope, element, attr, ctrl) {
ctrl.$addExpression(attr.ngIf || true );
}
}
});
- Ajouter un formulaire avec un attribut de nom
- Ajoutez un ou plusieurs champs de formulaire - chacun avec un attribut de nom et une directive ng-model.
- Déclarez un
<validation-tooltip>
élément avec un attributtarget
faisant référence au nom d'un des champs du formulaire.- Appliquer le
validation-message
directive à chaque message avec une optionng-if
expression de liaison.
<div ng-class="{'form-group': true, 'has-error':form.number.$invalid}">
<div class="row">
<div class="col-md-4">
<label for="number">Number</label>
<validation-tooltip target="number">
<ul class="list-unstyled">
<li validation-message ng-if="$field.$error.required">this field is required </li>
<li validation-message ng-if="$field.$error.number">should be number</li>
<li validation-message ng-if="$field.$error.min">minimum - 5</li>
<li validation-message ng-if="$field.$error.max">miximum - 20</li>
</ul>
</validation-tooltip>
</div>
</div>
<div class="row">
<div class="col-md-4">
<input type="number" min="5" max="20" ng-model="number" name="number" class="form-control" required />
</div>
</div>
</div>
@pixelbits est une excellente réponse. Je l'ai utilisé à la place:
<div class="form-group" ng-class="{ 'has-error': form.name.$dirty && form.name.$invalid }">
<label for="name" class="col-sm-4 control-label">What's your name?</label>
<div class="col-sm-6">
<input class="form-control has-feedback" id="name" name="name"
required
ng-minlength="4"
ng-model="formData.name"
tooltip="{{form.name.$valid ? '' : 'How clients see your name. Min 4 chars.'}}" tooltip-trigger="focus"
tooltip-placement="below">
<span class="glyphicon glyphicon-ok-sign text-success form-control-feedback" aria-hidden="true"
ng-show="form.name.$valid"></span>
</div>
</div>
La technique est l'infobulle de ui-bootstrap et définit le texte de l'infobulle sur '' lorsqu'il est valide.
Grande réponse de @pixelbits. J'ai utilisé ses directives et les ai légèrement modifiées pour permettre à l'infobulle de s'afficher sur l'entrée réelle comme certains utilisateurs l'ont demandé.
angular.module('app')
.directive('validationTooltip', ['$timeout', function ($timeout) {
function toggleTooltip(scope) {
if (!scope.tooltipInstance) {
return;
}
$timeout(function() {
if (scope.errorCount > 0 && (scope.showWhen == undefined || scope.showWhen())) {
scope.tooltipInstance.enable();
scope.tooltipInstance.show();
} else {
scope.tooltipInstance.disable();
scope.tooltipInstance.hide();
}
});
}
return {
restrict: 'E',
transclude: true,
require: '^form',
scope: {
showWhen: '&',
placement: '@',
},
template: '<div></div>',
controller: ['$scope', function ($scope) {
var expressions = [];
$scope.errorCount = 0;
this.$addExpression = function (expr) {
expressions.Push(expr);
}
$scope.$watch(function () {
var count = 0;
angular.forEach(expressions, function (expr) {
if ($scope.$eval(expr)) {
++count;
}
});
return count;
}, function (newVal) {
$scope.errorCount = newVal;
toggleTooltip($scope);
});
}],
link: function (scope, element, attr, formController, transcludeFn) {
scope.$form = formController;
transcludeFn(scope, function (clone) {
var tooltip = angular.element('<div class="validationMessageTemplate" style="display: none;"/>');
tooltip.append(clone);
element.append(tooltip);
$timeout(function () {
scope.$field = formController[attr.target];
var targetElm = $('[name=' + attr.target + ']');
targetElm.tooltip({
placement: scope.placement != null ? scope.placement : 'bottom',
html: true,
title: clone,
});
scope.tooltipInstance = targetElm.data('bs.tooltip');
toggleTooltip(scope);
if (scope.showWhen) {
scope.$watch(scope.showWhen, function () {
toggleTooltip(scope);
});
}
});
});
}
}
}]);
Le changement majeur est que la directive utilise jQuery pour trouver l'élément cible (qui devrait être une entrée) via l'attribut name
et initialise l'info-bulle sur cet élément plutôt que sur l'élément de la directive. J'ai également ajouté une propriété showWhen
à la portée car vous ne souhaitez pas toujours que votre info-bulle s'affiche lorsque l'entrée n'est pas valide (voir l'exemple ci-dessous).
La directive validationMessage est inchangée
angular.module('app').directive('validationMessage', function () {
return {
restrict: 'A',
priority: 1000,
require: '^validationTooltip',
link: function (scope, element, attr, ctrl) {
ctrl.$addExpression(attr.ngIf || true);
}
}
});
L'utilisation en Html est également similaire, avec juste l'ajout de showWhen
si vous voulez:
<div class="form-group" ng-class="{ 'has-error' : aForm.note.$invalid && (aForm.note.$dirty) }">
<label class="col-md-3 control-label">Note</label>
<div class="col-md-15">
<textarea
name="note"
class="form-control"
data-ng-model="foo.Note"
ng-required="bar.NoteRequired"></textarea>
<validation-tooltip target="note" show-when="aForm.note.$invalid && (aForm.note.$dirty)">
<ul class="validation-list">
<li validation-message ng-if="$field.$error.required">Note required</li>
</ul>
</validation-tooltip>
</div>
</div>
Mon objectif était de tirer parti à la fois des messages ng et du popover ui-bootstrap pour les commentaires de validation. Je préfère le popover à l'infobulle car il affiche plus clairement les styles de bloc d'aide.
Voici le code:
<!-- Routing Number -->
<div class="form-group-sm" ng-class="{ 'has-error' : form.routingNumber.$invalid && !form.routingNumber.$pristine }">
<label class="control-label col-sm-4" for="routing-number">Routing #</label>
<div class="col-sm-8">
<input class="form-control input-sm text-box"
id="routing-number"
name="routingNumber"
ng-model="entity.ROUTINGNUM"
popover-class="help-block"
popover-is-open="form.routingNumber.$invalid"
popover-trigger="none"
required
uib-popover-template="'routing-number-validators'"
string-to-number
type="number" />
</div>
<!-- Validators -->
<script type="text/ng-template" id="routing-number-validators">
<div ng-messages="form.routingNumber.$error">
<div ng-messages-include="/app/modules/_core/views/validationMessages.html"></div>
</div>
</script>
</div>
Voici le validationMessages.html
<span ng-message="required">Required</span>
<span ng-message="max">Too high</span>
<span ng-message="min">Too low</span>
<span ng-message="minlength">Too short</span>
<span ng-message="maxlength">Too long</span>
<span ng-message="email">Invalid email</span>
Remarque: J'ai dû mettre à niveau vers jQuery 2.1.4 pour que la directive uib-popover-template fonctionne.
Dépendances:
vous pouvez simplement utiliser la propriété tooltip-enable:
<div class="showtooltip" tooltip-placement="left" tooltip-enable="$isValid" tooltip="tooltip message"></div>