【问题标题】:How to show form input errors using AngularJS UI Bootstrap tooltip?如何使用 AngularJS UI Bootstrap 工具提示显示表单输入错误?
【发布时间】:2014-07-13 18:53:16
【问题描述】:

例如,我的表单是showing form input errors

如果有一些错误,我需要在输入标签附近显示红色徽章(带有“悬停以显示错误”)。如果用户将鼠标悬停在这个红色徽章上 - 他将看到使用 AngularJS UI Bootstrap tooltip 的错误列表。 我不想将错误列表放入 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 工具提示编写这样的验证工具提示指令?

或者您能否建议另一种方法来维护验证错误消息?

【问题讨论】:

标签: angularjs angularjs-directive tooltip angular-ui-bootstrap


【解决方案1】:

Demo Fiddle

验证工具提示指令

validationTooltip 是主要指令。它具有以下职责:

  1. 通过嵌入的内容定义工具提示模板
  2. 跟踪验证表达式,以便在每个摘要循环中对它们进行评估。
  3. 公开一个控制器 API 以允许 valiationMessage 指令注册自己
  4. 在指令上提供一个“目标”属性,以指定徽章(以及相关的工具提示)将绑定到哪个表单字段

补充说明

工具提示模板使用来自链接函数的嵌入函数将模板绑定到指令的范围。模板可以绑定到的范围内有两个属性:

  1. $form:绑定到父作用域中定义的表单模型。即 $scope.myForm
  2. $field:绑定到父作用域中的 form.name 模型。即 $scope.myForm.myInput

这允许模板绑定到验证属性,例如 $valid、$invalid、$pristine、$dirty 和 $error,而无需直接引用表单名称或输入字段的名称。例如,以下所有表达式都是有效的绑定表达式:

$form 属性:

  • `$form.$valid`
  • `$form.$invalid`
  • `$form.$dirty`
  • `$form.$pristine`
  • `$form.$error.required` 等等...

$字段属性:

  • `$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. 添加一个或多个表单字段 - 每个字段都有一个名称属性和一个 ng-model 指令。
  3. 声明一个&lt;validation-tooltip&gt; 元素,其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>

【讨论】:

  • 我尝试使用您的指令,但它返回给我:控制器 'form',指令 'validationTooltip' 需要,找不到!会是什么?
  • 检查您的 HTML 格式是否正确,是否缺少任何结束标记。 validationTooltip 和 validationMessage 指令必须在以前的标签内
  • 如果我们想在输入字段而不是标签上显示错误工具提示呢?有可能吗?
【解决方案2】:

@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的tooltip,有效时设置tooltip文本为''。

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

【讨论】:

  • 当你想显示一个 HTML 列表来显示输入的错误时,这会很快变得复杂,例如密码的安全标准。
  • 这很好,但它只会在焦点上显示工具提示,例如如果您有 5 个字母并删除了 2 个,则在您再次模糊并聚焦该字段之前不会显示任何工具提示。
【解决方案3】:

来自@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>

【讨论】:

    【解决方案4】:

    您实际上可以只使用 tooltip-enable 属性:

    <div class="showtooltip" tooltip-placement="left" tooltip-enable="$isValid" tooltip="tooltip message"></div>
    

    【讨论】:

      【解决方案5】:

      我的目标是利用 ng-messages 和 ui-bootstrap popover 来获得验证反馈。我更喜欢弹出框而不是工具提示,因为它更清楚地显示了帮助块样式。

      代码如下:

      <!-- 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 指令正常工作。

      依赖关系:

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2020-03-31
        • 1970-01-01
        • 1970-01-01
        • 2016-06-15
        • 1970-01-01
        • 2014-09-06
        • 2015-08-11
        • 1970-01-01
        相关资源
        最近更新 更多