简体   繁体   English

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

[英]Add attribute directive inside directive in angular

I'm creating a validation directive in angular and I need to add a tooltip to the element the directive is bound to. 我正在以角度创建验证指令,我需要在指令绑定的元素中添加工具提示。

Reading thru the web I found this solution setting a high priority and terminal to the directive, but since I'm using ngModel this doesn't work for me. 通过网络阅读我发现这个解决方案设置了一个高优先级和终端指令,但由于我使用的是ngModel,这对我不起作用。 This is what I'm doing right now: 这就是我现在正在做的事情:

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

But it's not working for me. 但这对我不起作用。 It throws the following error: 它会引发以下错误:

Error: [$compile:ctreq] Controller 'ngModel', required by directive 'validator', can't be found! 错误:[$ compile:ctreq]无法找到指令'validator'所需的控制器'ngModel'!

This is the HTML where I'm using the directive: 这是我正在使用该指令的HTML:

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

Any ideas on how can I solve this? 关于如何解决这个问题的任何想法?

Thanks. 谢谢。

The reason is because of the combination of your directive priority with terminal option. 原因是您的指令priorityterminal选项的组合。 It means that ngModel directive will not render at all. 这意味着ngModel指令根本不会呈现。 Since your directive priority ( 1000 ) is greater than ng-model's( 0 ) and presence of terminal option will not render any other directive with lower priority (than 1000). 由于您的指令优先级( 1000 )大于ng-model( 0 ),并且terminal选项的存在不会呈现任何其他具有较低优先级(超过1000)的指令。 So some possible options are : 所以一些可能的选择是:

  • remove the terminal option from your directive or 从指令中删除终端选项或
  • reduce the priority of your directive to 0 or -1 (to be less than or equal to ngModel) or 将指令的优先级降低到0或-1(小于或等于ngModel)或
  • remove ng-model requirement from the directive and possibly use a 2-way binding say ngModel:"=" (based on what suits your requirement). 从指令中删除ng-model要求,并可能使用双向绑定,例如ngModel:"=" (基于适合您的要求)。
  • Instead of adding tooltip attribute and recompiling the element, you could use transclusion in your directive and have a directive template. 您可以在指令中使用transclusion并使用指令模板,而不是添加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). terminal - 如果设置为true,则当前优先级将是将执行的最后一组指令(当前优先级的任何指令仍将执行,因为未定义相同优先级的执行顺序)。 Note that expressions and other directives used in the directive's template will also be excluded from execution. 请注意,指令模板中使用的表达式和其他指令也将从执行中排除。

demo 演示

 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> 

As explained in my comments you do not need to recompile the element and all these stuffs, just set up an element and append it after the target element (in your specific case, the input). 正如我在评论中所解释的那样,您不需要重新编译元素和所有这些内容,只需设置一个元素并将其附加到目标元素之后(在您的特定情况下,输入)。

Here is a modified version of validation directive (i have not implemented any validation specifics which i believe you should be able to wire up easily). 这是验证指令的修改版本(我没有实现任何验证细节,我相信你应该能够轻松连接)。

So what you need is to set up custom trigger for tooltip which you can do by using the $tooltipprovider . 所以你需要的是为工具提示设置自定义触发器,你可以使用$tooltipprovider So set up an event pair when you want to show/hide tooltip. 因此,当您想要显示/隐藏工具提示时,请设置一个事件对。

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

And now in your directive just set up your tooltip element as you like with tooltip attributes on it. 现在,在您的指令中,只需使用工具提示属性设置工具提示元素即可。 compile only the tooltip element and append it after the target element (you can manage positioning with css ofcourse). 仅编译工具提示元素,将其追加after的目标元素(可以ofcourse管理与CSS定位)。 And when you have validation failure, just get the tooltip element reference (which is reference to the tooltip element, instead of copying the reference you could as well select every time using the selector) and do $tooltipEl.triggerHandler('show-validation') and to hide it $tooltipEl.triggerHandler('show-validation') . 当你有验证失败时,只需获取tooltip element reference (这是对工具提示元素的引用,而不是复制每次使用选择器时都可以选择的引用)并执行$tooltipEl.triggerHandler('show-validation')和隐藏$tooltipEl.triggerHandler('show-validation')

Sample Implementation which shows the tooltip after 2 sec and hides it after 5 sec (since validation is not in the scope of this question you should be able to wire it up): 示例实现,显示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 Plnkr

Inline Demo 内联演示

 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> 

You should not create a new isolated scope in your directive: this will mess up with the others directives (and in this case will not share ngModel). 您不应该在指令中创建一个新的隔离范围:这将搞乱其他指令(在这种情况下不会共享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);
            }
        };
    },
}

I invite you to check the Angular-UI library and especially how they have implemented their ui.validate directive: http://angular-ui.github.io/ui-utils/ 我邀请您查看Angular-UI库,特别是他们如何实现他们的ui.validate指令: http ://angular-ui.github.io/ui-utils/

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM