javascript - 在 Angular Directive(指令)中添加属性指令

标签 javascript html angularjs angularjs-directive angular-ui-bootstrap

我正在以 Angular 创建验证指令,我需要向指令绑定(bind)到的元素添加工具提示。

通过网络阅读我发现此解决方案为指令设置了高优先级和终端,但由于我使用的是 ngModel 这对我不起作用。这就是我现在正在做的:

return {
        restrict: 'A',
        require: 'ngModel',
        replace: false,
        terminal: true,
        priority: 1000,
        scope: {
            model: '=ngModel',
            initialValidity: '=initialValidity',
            validCallback: '&',
            invalidCallback: '&'
        },
        compile: function compile(element, attrs) {
            element.attr('tooltip', '{{validationMessage}');
            element.removeAttr("validator");
            return {
                post: function postLink(scope, element) {
                  $compile(element)(scope);
                }
            };
        },
}

但这对我不起作用。它抛出以下错误:

Error: [$compile:ctreq] Controller 'ngModel', required by directive 'validator', can't be found!

这是我使用指令的 HTML:

<input id="username" name="username" data-ng-model="user.username" type="text" class="form-control" validator="required, backendWatchUsername" placeholder="johndoe" tabindex="1" >

关于如何解决这个问题的任何想法?

谢谢。

最佳答案

原因是因为您的指令 priority terminal 的组合 选项。这意味着 ngModel 指令根本不会呈现。由于您的指令优先级 (1000) 大于 ng-model 的 (0) 并且 terminal 选项的存在不会呈现任何其他具有较低的指令优先级(高于 1000)。所以一些可能的选择是:

  • 从您的指令中删除终端选项或
  • 将指令的优先级降低到 0 或 -1(小于或等于 ngModel)或
  • 从指令中删除 ng-model 要求,并可能使用双向绑定(bind),例如 ngModel:"="(根据您的要求)。
  • 无需添加 tooltip 属性并重新编译元素,您可以在指令中使用嵌入并拥有一个指令模板。

terminal - If set to true then the current priority will be the last set of directives which will execute (any directives at the current priority will still execute as the order of execution on same priority is undefined). Note that expressions and other directives used in the directive's template will also be excluded from execution.

演示

angular.module('app', []).directive('validator', function($compile) {
  return {
    restrict: 'A',
    require: 'ngModel',
    replace: false,
    terminal: true,

    scope: {
      model: '=ngModel',
      initialValidity: '=initialValidity',
      validCallback: '&',
      invalidCallback: '&'
    },
    compile: function compile(element, attrs) {
      element.attr('tooltip', '{{validationMessage}');
      element.removeAttr("validator");
      return {
        post: function postLink(scope, element) {

          $compile(element)(scope);

        }
      };
    },
  }
})
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="app">
  <input validator ng-model="test">
</div>

正如我在评论中所解释的那样,您不需要重新编译元素和所有这些东西,只需设置一个元素并将其附加到目标元素之后(在您的特定情况下,输入)。

这是验证指令的修改版本(我没有实现任何验证细节,我相信你应该能够轻松连接)。

因此,您需要为工具提示设置自定义触发器,您可以使用 $tooltipprovider .所以当你想显示/隐藏工具提示时设置一个事件对。

.config(function($tooltipProvider){
    $tooltipProvider.setTriggers({'show-validation':'hide-validation'});
});

现在在您的指令中,只需根据您的喜好设置工具提示元素,并在其上设置工具提示属性。仅编译工具提示元素并将其附加到目标元素的之后(您当然可以使用 css 管理定位)。当您验证失败时,只需获取 tooltip element reference(这是对 tooltip 元素的引用,而不是复制引用,您也可以每次使用选择器选择)并执行 $tooltipEl.triggerHandler('show-validation') 并隐藏它 $tooltipEl.triggerHandler('show-validation')

在 2 秒后显示工具提示并在 5 秒后隐藏它的示例实现(因为验证不在这个问题的范围内,你应该能够连接它):

.directive('validator', function($compile, $timeout){

  var tooltiptemplate = '<span class="validation" tooltip="{{validationMessage}}" tooltip-trigger="show-validation" tooltip-placement="bottom"></span>';
  var tooltipEvents = {true:'show-validation', false:'hide-validation'};

  return {
        restrict: 'A',
        require: 'ngModel',
        replace: false,
        priority: 1000,
        scope: {
            model: '=ngModel',
            initialValidity: '=initialValidity',
            validCallback: '&',
            invalidCallback: '&'
        },
        compile: function compile(element, attrs) {


            return {
                post: function postLink(scope, element) {

                  var $tooltipEl= getTooltip();


                  init();

                  function init(){
                   scope.$on('$destroy', destroy);
                   scope.validationMessage ="Whoops!!!";

                   $timeout(function(){
                    toggleValidationMessage(true);
                   },2000);

                   $timeout(function(){
                     toggleValidationMessage(false);
                   },5000);
                 }

                 function toggleValidationMessage(show){
                   $tooltipEl.triggerHandler(tooltipEvents[show]);
                 }



                 function getTooltip(){
                     var elm = $compile(angular.element(tooltiptemplate))(scope);
                     element.after(elm);
                     return elm;
                 }

                 function destroy(){
                    $tooltipEl= null;
                 }

                }
            };
        },
  }

});

Plnkr

内联演示

var app = angular.module('plunker', ['ui.bootstrap']);

app.controller('MainCtrl', function($scope) {
  $scope.user = {
    username: 'jack'
  };
}).directive('validator', function($compile, $timeout) {

  var tooltiptemplate = '<span class="validation" tooltip="{{model}}" tooltip-trigger="show-validation" tooltip-placement="bottom"></span>';
  var tooltipEvents = {
    true: 'show-validation',
    false: 'hide-validation'
  };

  return {
    restrict: 'A',
    require: 'ngModel',
    replace: false,
    priority: 1000,
    scope: {
      model: '=ngModel',
      initialValidity: '=initialValidity',
      validCallback: '&',
      invalidCallback: '&'
    },
    compile: function compile(element, attrs) {


      return {
        post: function postLink(scope, element) {

          var $tooltipEl = getTooltip();


          init();

          function init() {
            scope.$on('$destroy', destroy);
            scope.validationMessage = "Whoops!!!";

            $timeout(function() {
              toggleValidationMessage(true);
            }, 2000);

            $timeout(function() {
              toggleValidationMessage(false);
            }, 5000);
          }

          function toggleValidationMessage(show) {
            $tooltipEl.triggerHandler(tooltipEvents[show]);
          }



          function getTooltip() {
            var elm = $compile(angular.element(tooltiptemplate))(scope);
            element.after(elm);
            return elm;
          }

          function destroy() {
            elm = null;
          }

        }
      };
    },
  }

}).config(function($tooltipProvider) {
  $tooltipProvider.setTriggers({
    'show-validation': 'hide-validation'
  });
});
/* Put your css in here */

.validation {
  display: block;
}
<!DOCTYPE html>
<html ng-app="plunker">

<head>
  <meta charset="utf-8" />
  <title>AngularJS Plunker</title>
  <link data-require="bootstrap-css@3.1.*" data-semver="3.1.1" rel="stylesheet" href="//netdna.bootstrapcdn.com/bootstrap/3.1.1/css/bootstrap.min.css" />
  <script>
    document.write('<base href="' + document.location + '" />');
  </script>

  <script data-require="angular.js@1.3.x" src="https://code.angularjs.org/1.3.12/angular.js" data-semver="1.3.12"></script>
  <script data-require="ui-bootstrap@*" data-semver="0.12.0" src="http://angular-ui.github.io/bootstrap/ui-bootstrap-tpls-0.12.0.min.js"></script>

</head>

<body ng-controller="MainCtrl">
  <br/>
  <br/>{{user.username}}
  <input id="username" name="username" data-ng-model="user.username" type="text" class="form-control" validator="required, backendWatchUsername" placeholder="johndoe" tabindex="1">

</body>

</html>

关于javascript - 在 Angular Directive(指令)中添加属性指令,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28352641/

相关文章:

javascript - 文字视差效果

html - 在 Outlook 中查看时,表格的右边框未显示在电子邮件模板中

javascript - 如何检测输入字段上的编程值变化

html - 表上的 DIV(位置 : absolute, z 索引:1000)- 不起作用

javascript - Angularjs 中的幻灯片无法按预期工作

javascript - highcharts-more.js[dynamic] 文件上的 drawRectangle 错误

javascript - 对 json 数据进行分类并将其存储在单独的数组中

Javascript - 动态从数组中获取元素

angularjs - 带有全局管道的 Angular 2 RC5 问题

javascript - 如何在 Angular Js 中使用 name 或 id 属性验证表单