Как показать ошибки ввода формы с помощью всплывающей подсказки AngularJS UI Bootstrap?

Например, у меня есть форма, в которой я показываю ошибки ввода формы.

Мне нужно показать красный значок (с надписью «наведите указатель мыши, чтобы показать ошибки») рядом с меткой ввода, если есть какие-то ошибки. Если пользователь наведет курсор на этот красный значок - он увидит список ошибок с помощью всплывающей подсказки AngularJS UI Bootstrap. Я не хочу помещать список ошибок в атрибут tooltip-html-unsafe, потому что его неудобно редактировать и поддерживать.

Этот код более декларативен:

<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>

чем этот код:

<span tooltip-html-unsafe="{{<ul><li>This field is required;</li><li>...</li></ul>}}">hover to show errors</span>

Как я могу написать такую ​​директиву-подсказку с помощью всплывающей подсказки AngularJS UI Bootstrap?

Или, может быть, вы можете предложить другой подход для поддержки сообщений об ошибках проверки?


person webvitaly    schedule 13.07.2014    source источник
comment
Вы можете использовать эту библиотеку проверки jonsamwell.com/dynamic-angularjs-validation и создать собственный модификатор элемента для установки всплывающей подсказки, когда компонент недействителен   -  person Jon    schedule 13.07.2014
comment
Вы также можете использовать xtForm в качестве директивы, готовой к использованию brentmckendrick.com/code/xtform   -  person Leo Caseiro    schedule 29.09.2015


Ответы (5)


Демо-скрипт

Директива всплывающей подсказки для проверки

ValidationTooltip - это основная директива. Он имеет следующие обязанности:

  1. Определите шаблон подсказки с помощью включенного содержимого
  2. Следите за выражениями проверки, чтобы их можно было оценивать в каждом цикле дайджеста.
  3. Предоставьте API-интерфейс контроллера, чтобы разрешить директивам valiationMessage регистрироваться.
  4. Укажите атрибут target в директиве, чтобы указать, к какому полю формы будет привязан значок (и соответствующая всплывающая подсказка).

Дополнительные примечания

Шаблон всплывающей подсказки использует функцию включения из функции ссылки для привязки шаблона к области действия директивы. В области действия есть два свойства, к которым может быть привязан шаблон:

  1. $ form: привязан к модели формы, определенной в родительской области. то есть $ scope.myForm
  2. $ field: привязано к модели form.name в родительской области. то есть $ scope.myForm.myInput

Это позволяет шаблону связываться со свойствами проверки, такими как $ valid, $ invalid, $ pristine, $ dirty и $ error, без прямой ссылки на имя формы или имя поля ввода. Например, все следующие выражения являются допустимыми выражениями привязки:

Свойства $ формы:

  • `$ form. $ valid`
  • `$ form. $ invalid`
  • `$ form. $ dirty`
  • `$ form. $ нетронутая`
  • `$ form. $ error.required` и т. д.

Свойства $ field:

  • `$ field. $ valid`
  • `$ field. $ invalid`
  • `$ field. $ dirty`
  • `$ field. $ pristine`
  • `$ field. $ error.required` и т. д.

Реализация директивы

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
                    });

                });
            });
        }
    }
});

Директива сообщения проверки

Директива validationMessage отслеживает сообщения проверки, отображаемые во всплывающей подсказке. Он использует ng-if для определения выражения для оценки. Если в элементе не найдено ng-if, тогда выражение просто оценивается как истинное (всегда отображается).

app.directive('validationMessage', function () {
    return {
        restrict: 'A',
        priority: 1000,
        require: '^validationTooltip',
        link: function (scope, element, attr, ctrl) {
            ctrl.$addExpression(attr.ngIf || true );
        }
    }
});

Использование в HTML

  1. Добавить форму с атрибутом имени
  2. Добавьте одно или несколько полей формы - каждое с атрибутом name и директивой ng-model.
  3. Объявите элемент <validation-tooltip> с атрибутом target, относящимся к имени одного из полей формы.
  4. Примените директиву validation-message к каждому сообщению с необязательным ng-if выражением привязки.
<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>
person pixelbits    schedule 16.07.2014
comment
Я пытался использовать ваши директивы, но он возвращает мне следующее: «Форма контроллера», требуемая директивой «validationTooltip», не может быть найдена! Что это может быть? - person Alejandro Sanz Díaz; 31.10.2014
comment
Проверьте, правильно ли сформирован ваш HTML-код и нет ли закрывающих тегов. Директивы validationTooltip и validationMessage должны находиться внутри предыдущего тега. - person pixelbits; 02.11.2014
comment
как насчет того, чтобы показывать всплывающую подсказку об ошибке в поле ввода вместо метки? Является ли это возможным? - person Gayan; 20.11.2014

Ответ @pixelbits отличный. Вместо этого я использовал это:

  <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>

Техника - всплывающая подсказка ui-bootstrap и установка текста всплывающей подсказки на '', если она допустима.

http://jsbin.com/ditekuvipa/2/edit

person Michael Cole    schedule 12.04.2015
comment
Это быстро усложняется, когда вы хотите показать список HTML, показывающий, что не так с вводом, например критерии безопасности для пароля. - person felixfbecker; 29.06.2015
comment
Это хорошо, но всплывающая подсказка будет отображаться только в фокусе, например, если у вас есть 5 букв и вы удалите 2, всплывающая подсказка не будет отображаться, пока вы снова не размываете и не сфокусируете поле. - person rjh; 04.08.2016

Отличный ответ от @pixelbits. Я использовал его директивы и немного изменил их, чтобы всплывающая подсказка отображалась поверх фактического ввода, как того требовали некоторые пользователи.

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);
                        });
                    }
                });
            });
        }
    }
}]);

Основное изменение заключается в том, что директива использует jQuery для поиска целевого элемента (который должен быть входом) через атрибут name и инициализирует всплывающую подсказку для этого элемента, а не для элемента директивы. Я также добавил свойство showWhen в область видимости, так как вы не всегда можете захотеть, чтобы всплывающая подсказка отображалась, когда ввод недопустим (см. Пример ниже).

Директива validationMessage не изменилась

angular.module('app').directive('validationMessage', function () {
    return {
        restrict: 'A',
        priority: 1000,
        require: '^validationTooltip',
        link: function (scope, element, attr, ctrl) {
            ctrl.$addExpression(attr.ngIf || true);
        }
    }
});

Использование в Html также аналогично, только с добавлением showWhen, если вы хотите:

<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>
person jstromwick    schedule 20.11.2015

вы можете просто использовать свойство tooltip-enable:

<div class="showtooltip" tooltip-placement="left" tooltip-enable="$isValid" tooltip="tooltip message"></div>
person Greg LBD    schedule 01.12.2015

Моя цель состояла в том, чтобы использовать как ng-messages, так и всплывающее окно ui-bootstrap для обратной связи с проверкой. Я предпочитаю всплывающее окно, а не всплывающую подсказку, поскольку оно более четко отображает стили блока справки.

Вот код:

<!-- 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>

Вот 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>

Примечание. Мне пришлось перейти на jQuery 2.1.4, чтобы заставить работать директиву uib-popover-template.

Зависимости:

person Nathan Agersea    schedule 11.10.2016