在angular中的指令内添加属性指令

时间:2015-02-05 19:41:39

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

我正在以角度创建验证指令,我需要在指令绑定的元素中添加工具提示。

通过网络阅读我发现这个解决方案设置了一个高优先级和终端指令,但由于我使用的是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);
                }
            };
        },
}

但它不适合我。它会引发以下错误:

  

错误:[$ compile:ctreq]无法找到指令'validator'所需的控制器'ngModel'!

这是我正在使用该指令的HTML:

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

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

感谢。

2 个答案:

答案 0 :(得分:5)

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

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

terminal - 如果设置为true,则当前优先级将是将执行的最后一组指令(当前优先级的任何指令仍将执行,因为未定义相同优先级的执行顺序)。请注意,指令模板中使用的表达式和其他指令也将被排除在执行之外。

<强>演示

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

现在在你的指令中,只需使用工具提示属性就可以设置工具提示元素。仅编译工具提示元素并将其附加到目标元素after(您可以使用css ofcourse管理定位)。当您有验证失败时,只需获取tooltip element reference(这是对工具提示元素的引用,而不是复制每次使用选择器时都可以选择的引用)并执行$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>

答案 1 :(得分:0)

你不应该在你的指令中创建一个新的隔离范围:这将搞乱其他指令(在这种情况下不会共享ngModel)。

return {
    restrict: 'A',
    require: 'ngModel',
    compile: function compile(element, attrs) {
        element.attr('tooltip', '{{validationMessage}');
        element.removeAttr("validator");
        return {
            post: function postLink(scope, element) {
              $compile(element)(scope);
            }
        };
    },
}

我邀请您查看Angular-UI库,特别是他们如何实现ui.validate指令:http://angular-ui.github.io/ui-utils/

相关问题