Angularjs指令$ compile导致ng-click触发两次

时间:2016-07-14 12:01:20

标签: javascript angularjs

我为表单验证创建了一个角度验证器模块,无需在表单中包含ngMessages,一切都按预期工作。

enter image description here

但是我发现了一个我一直试图修复的错误。它与$compile有关。

所以我有一个向表单元素添加属性的指令,这是通过使用$ compile服务实现的,但是,$ compile服务似乎会导致ng-click出现不需要的行为,所以当ng-click位于里面时这个表格被称为两次火灾;

这是指令和我正在做的事情:

angular.module('app',[])

.directive('validateForm',['$compile',
        function($compile)
        {
            var addErrors = function(rules, form, ctrls, scope, config){
                //code
            };


            return {
                restrict: 'A',
                require: ['^form'],
                link: {
                    post: function(scope, element, attrs, ctrls){

                        var form = ctrls[0];
                        var config = scope.validator;

                        if(typeof config != 'object') return;

                        var rules = config['rules'];
                        var errors = [];

                        //-----
                    },
                    pre: function(scope, element, attrs, ctrls){

                        var elm = element.find('select, input, textarea').attr('validate-field','');
                        element.removeAttr("validate-form"); //remove the attribute to avoid indefinite loop
                        element.removeAttr("data-validate-form");
                        $compile(element.contents())(scope);
                    }
                }
            };
        }

    ])
 .controller('UserController',['$scope', function($scope){
  $scope.title = 'Form Validator';
  $scope.clickThings = function(value){
    alert(value); //pops up twice means ng-click fires twice
  }
}]);

表单标记:

 <div ng-controller="UserController">
 <form novalidate="" validate-form name="form" role="form">
     <div class="form-group">
          <input type="text" class="form-control" ng-model="first_name" name="first_name" />
        </div>
        <div class="form-group">
          <input type="text" class="form-control" ng-model="last_name" name="last_name" />
        </div>
        <div class="form-group">
          <input type="text" class="form-control" ng-model="email" name="email" />
        </div>
        <div class="form-group">
          <input type="password" class="form-control" ng-model="password" name="password" />
        </div>
        <div class="form-group">
          <input type="text" class="form-control" ng-model="country" name="country" />
        </div>
        <a type="button" class="btn btn-success" ng-click="clickThings('Submit Clicked')">Submit</a>
      </form>
      </div>

我创造了一个plunker: http://embed.plnkr.co/uIid4gczKxKI4rPOHqx7

2 个答案:

答案 0 :(得分:1)

在尝试了不同的事情后,我意识到已经编译的ng-click在第$compile(element.contents())(scope)被调用时被编译。

要解决此问题,只需编译已更改/受影响的元素,即 elem = element.find('select, input, textarea').attr('validate-field','');

$compile(element.contents())(scope)替换为$compile(elem)(scope);

所以我最终得到了这个:

angular.module('app',[])

.directive('validateForm',['$compile',
    function($compile)
    {
        var addErrors = function(rules, form, ctrls, scope, config){
            //code
        };

        return {
            restrict: 'A',
            require: ['^form'],
            link: {
                post: function(scope, element, attrs, ctrls){

                    var form = ctrls[0];
                    var config = scope.validator;

                    if(typeof config != 'object') return;

                    var rules = config['rules'];
                    var errors = [];

                    //-----
                },
                pre: function(scope, element, attrs, ctrls){

                    var elem = element.find('select, input, textarea').attr('validate-field','');
                    element.removeAttr("validate-form"); //remove the attribute to avoid indefinite loop
                    element.removeAttr("data-validate-form");
                    $compile(elem)(scope);
                }
            }
        };
    }

 ])
 .controller('UserController',['$scope', function($scope){
   $scope.title = 'Form Validator';
   $scope.clickThings = function(value){
   alert(value); //pops up twice means ng-click fires twice
  }
 }]);

这里的工作人员:

答案 1 :(得分:0)

在预链接功能中使用$compile的目的是什么?如果你只想在指令元素链接之前进行模板转换 ,你应该将ur代码放在指令编译中并取出$ compile。或者如果你想在链接子元素之后编译元素,你应该把你的代码放在post-link中。将$compile放在预链接中会导致子元素的子节点被链接两次。

选择:

angular.module('app',[])
  .directive('validateForm',['$compile',
            function($compile)
            {
                var addErrors = function(rules, form, ctrls, scope, config){
                    //code
                };


                return {
                    restrict: 'A',
                    require: ['^form'],

                    compile:function(element, attrs, ctrls){
                            // the code will be executed before get complied
                            var elm = element.find('select, input, textarea').attr('validate-field','');


                            return function(scope, element, attrs, ctrls){

                                 var form = ctrls[0];
                                 var config = scope.validator;

                                 if(typeof config != 'object') return;

                                 var rules = config['rules'];
                                 var errors = [];

                                 //-----
                        }

                        }
                };
            }

        ])

或者

angular.module('app',[])
  .directive('validateForm',['$compile',
            function($compile)
            {
                var addErrors = function(rules, form, ctrls, scope, config){
                    //code
                };


                return {
                    restrict: 'A',
                    require: ['^form'],
                    link: {
                        post: function(scope, element, attrs, ctrls){

                            var form = ctrls[0];
                            var config = scope.validator;

                            if(typeof config != 'object') return;

                            var rules = config['rules'];
                            var errors = [];

                            //-----
                            var elm = element.find('select, input, textarea').attr('validate-field','');
                            element.removeAttr("validate-form"); //remove the attribute to avoid indefinite loop
                            element.removeAttr("data-validate-form");
                            $compile(element.contents())(scope);
                        },
                        pre: function(scope, element, attrs, ctrls){


                        }
                    }
                };
            }

        ])

修改

只需消除$compile子句也可以。但这三种方式有一些区别。

有关更多信息,请参阅the official document