如何在Angular Bootstrap Popover中显示Angular表单验证错误?

16

我对AngularJS还很陌生。我想在元素右侧的Angular Bootstrap Popover中显示表单错误。我尝试创建指令,并在元素更改类时得到一个元素。但是我不知道下一步该怎么做。

(function(angular) {
  'use strict';
var app=angular.module('formExample', [])
  .controller('ExampleController', ['$scope', function($scope) {
    $scope.master = {};

    $scope.update = function(user) {
      $scope.master = angular.copy(user);
    };

    $scope.reset = function(form) {
      if (form) {
        form.$setPristine();
        form.$setUntouched();
      }
      $scope.user = angular.copy($scope.master);
    };

    $scope.reset();
  }]);
app.directive("alert", function(){
    return {
        restrict: 'C',
        priority: -1000,
        link: function(scope, ele, attrs, ctrl){
          scope.$watch(function() {console.log(ele.attr('class')); })
          if (ctrl) {
            console.log("applying custom behaviour to input: ", ele.attr('id'));
            // ... awesomeness here
          }
        }
    };
});
})(window.angular);

我只想显示错误消息。

  1. 当用户点击保存按钮时(所有表单字段的错误消息)
  2. 元素失去焦点时(仅针对失去焦点的元素)

这是我尝试获取消息的plnkr

更新

不知何故,我显示了angular bootstrap弹出窗口和关闭按钮,用于关闭弹出窗口。

在当前plunker中,我有两个问题:

  1. 我想在我的弹出窗口模板中显示相应于打开它的元素的错误消息。我需要这个模板,因为我需要一个关闭按钮。
  2. 一旦我关闭了弹出窗口,如果该字段为空并且用户单击提交,则下次不会打开弹出窗口。我想在每次提交时显示错误消息。

根据我的经验,uib-popover 不适合处理此类动态内容(例如错误验证)。我曾在一个项目中尝试过类似的方法,结果并不值得使用 uib-popover 的麻烦。相反,我们只需使用 CSS 和 ng-class 模拟气泡的感觉。然后,我们的指令只需要根据表单处理何时隐藏/显示“气泡”。 - ryanyuyu
请看下面我的回答。 - Tjaart van der Walt
2个回答

2
试试将您的模板放置在这样的位置:
<script type="text/ng-template" id="myPopoverTemplate.html">
  <div class="gmePopover">
    <div class="popover-header">
      <button type="button" class="close" popover-toggle><span aria-hidden="true">&times;</span></button>
    </div>
    <div class="popover-content">
        somecontent
    </div>
  </div>
</script>

这里有一个可用的 Plunker:链接

更新:

您可以使用 angularjs foreach 循环遍历表单中的所有错误,然后在此基础上显示弹出窗口。类似于这样:链接

<script type="text/javascript">
  var app=angular.module('testApp', ['ngAnimate', 'ngSanitize'], function($httpProvider) {});
  app.controller("PopoverDemoCtrl", function($scope, $http, $window) {
    $scope.validate = function() {
        var _popover;
        var error = $scope.testForm.$error;
        angular.forEach(error.required, function(field){
            var message = 'This field (' + field.$name + ') is required';
            _popover = $('#' + field.$name).popover({
              trigger: 'manual',
              title: '<span class="text-info"><strong>title</strong></span>'+
            '<button type="button" id="close" class="close" onclick="$(&quot;#' + field.$name + '&quot;).popover(&quot;hide&quot;);">&times;</button>',
              content: message,
              html: true
            });

            return $('#' + field.$name).popover("show")
        });
    };
  });
</script>

我需要相对于父元素的弹出内容。我不知道我的更新后的 Plunker 与你的有什么区别。 - svk
区别在于:我将myPopoverTemplate.html放置在ng-template中。我不确定你所说的“相对于父元素”的意思。一个简化的例子会有所帮助,以便我们提供帮助。 - jomsk1e
好的,例如,在给定的 Plunker 中,考虑第一个文本框是用户名,第二个文本框是密码。如果用户单击提交按钮,而没有输入任何值(即两个文本框都为空),那么我们必须在第一个弹出窗口中显示“请输入用户名”,并在第二个弹出窗口中显示“请输入密码”。 - svk
有没有办法可以找出已经打开弹出窗口的元素。当我打印错误对象时,如果元素与某个模式不匹配,则对于同一元素,我会得到两个错误对象。在这种情况下,我必须阻止元素打开另一个弹出窗口。即所需的不仅是验证。 - svk
尚未测试,但我相信您想要的是 $yourElement.data()['bs.popover'].tip().hasClass('in')。如果弹出窗口可见,则返回 true。 - jomsk1e

2
您可以创建一个指令来拦截FormController$setSubmitted方法。
您可以在此处找到有关该方法的更多信息。
请在此处找到可工作的示例。
当该指令拦截到$setSubmitted方法时,我们可以通知另一个指令,在Bootstrap弹出窗口中显示验证错误。
我基于以下假设工作(请随时纠正我):
  • 您将使用form标签
  • 在您的form标签上,您将使用ng-submit="nameOfForm.$valid && vm.onSubmit()"
该解决方案包含两个指令:
submitNotifypopoverValidation
submitNotify在表单提交时通知popoverValidationpopoverValidation指令会显示表单错误(如果有)。

指令1:submitNotify

directive('submitNotify', function () {
    return {
        restrict: 'A',
        require: 'form',
        controller: function SubmitNotify() { },
        link: function (scope, element, attrs, form) {                
            var $setSubmitted = form.$setSubmitted;
            form.$setSubmitted = function () {
                $setSubmitted.bind(form)();
                scope.$broadcast('onSubmitNotify');
            };
        }
    };
})

解释:

  • 仅能用作属性指令
  • 需要一个 form 标签或 ngForm

链接函数:

链接函数使用回调函数替换了 $setSubmitted 函数。回调函数通知 popoverValidation 指令表单已被提交。

指令2:popoverValidation

directive('popoverValidation', [function () {
    return {
        restrict: 'A',
        require: ['ngModel', '^submitNotify'],
        link: function (scope, element, attrs, require) {
            scope.$on('onSubmitNotify', function () {
                var ngModel = require[0];
                if (!ngModel.$valid) {
                    showPopover(ngModel.$error);
                }
            });

            function showPopover( $error) {
                var options = {
                    content: getValidationErrorsHtml($error),
                    html: true,
                    template: '<div class="popover" role="tooltip"><div class="arrow"></div><h3 class="popover-title"></h3><div class="popover-content popover-content-errors"></div></div>',
                    title: '<span class="text-info"><strong>Error</strong></span><button type="button" data-dismiss="popover" class="close">&times;</button>',
                    trigger: 'manual'
                }
                $(element).popover(options);
                $(element).on('shown.bs.popover', hidePopover);
                $(element).popover('show');                    
            }

            function hidePopover() {
                $(this).next('.popover').find('button[data-dismiss="popover"]').click(function (e) {
                    $(element).popover('hide');
                });
            }

            function getValidationErrorsHtml($error) {
                var errors = [];

                if ($error.required) {
                    errors.push(requiredErrorMessage());
                }

                if ($error.email) {
                    errors.push(invalidEmailAddress());
                }

                var errorHtml = '<ul class="list-group">';

                for (var i = 0; i < errors.length; i++) {
                    errorHtml += '<li class="list-group-item">' + errors[i] + '</li>';
                }

                errorHtml += '</ul>';

                return errorHtml;
            }

            function requiredErrorMessage() {
                return 'This field is required';
            }

            function invalidEmailAddress() {
                return 'Please enter a valid email address';
            }
        }
    };
}]);

说明:

  • 只能用作属性指令
  • 需要在父级form上添加submitNotify标签

链接函数:

  • popoverValidation指令被通知表单已提交
  • 它检查ng-model绑定的属性是否有效
  • 如果无效,则显示弹出窗口

完整HTML:

<form name="myForm" ng-controller="MyFormController as vm" ng-submit="myForm.$valid && vm.onSubmit()" submit-notify="" novalidate>
    <div class="panel panel-primary">
        <div class="panel-heading">Form Validation with Popovers</div>
        <div class="panel-body">
            <div class="form-group">
                <label>First name</label>
                <input type="text" name="firstName" class="form-control" required ng-model="person.firstName" popover-validation="" />
            </div>
            <div class="form-group">
                <label>Surname</label>
                <input type="text" name="surname" class="form-control" required ng-model="person.surname" popover-validation="" />
            </div>
            <div class="form-group">
                <label>Email</label>
                <input type="email" name="email" class="form-control" ng-model="person.email" popover-validation="" />
            </div>
        </div>
        <div class="panel-footer">
            <button type="submit" class="btn btn-success">Submit</button>
        </div>
    </div>
</form>

一些CSS:

<style type="text/css">
    .popover-content-errors {
        padding:0px;
    }

    .popover-content-errors .list-group {
        margin-bottom:0px
    }

    .popover-content-errors .list-group-item {
        border-left:none;
        white-space:nowrap;
    }

    .popover-content-errors .list-group-item:first-child {
        border-top:none;
    }

    .popover-content-errors .list-group-item:last-child {
        border-bottom:none;
    }
</style>

MyFormController

controller('MyFormController', ['$scope', function ($scope) {
    var self = this;
    $scope.person = {
        email:'john.doe.com'
    }
    self.onSubmit = function () {   
        console.log('MyFormController.onSubmit');
    };
}]);

我没有完全浏览。但是我刚刚进入了Plunker并输入了正确的电子邮件地址。错误仍未消失。 - svk

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接