AngularJS:使用另一个指令编译元素更改行为

时间:2013-12-11 15:57:56

标签: angularjs angularjs-directive

当我编译包含该指令的元素时,我发现指令的行为不一致。在我的情况下,我有一个指令,验证密码是否与另一个密码字段匹配。该指令如下所示:

app.directive('passwordMatches', function() {
return {
    require: 'ngModel',
    restrict: 'A',
    scope: {
        otherPasswordFieldValue: '=passwordMatches'
    },
    link: function (scope, elem, attrs, ngModelController) {
        function validate(value) {
            return value === scope.otherPasswordFieldValue;
        }

        //For DOM -> model validation
        ngModelController.$parsers.unshift(function (value) {
            var valid = validate(value);
            ngModelController.$setValidity('password-matches', valid);
            return valid ? value : undefined;
        });

        //For model -> DOM validation
        ngModelController.$formatters.unshift(function (value) {
            ngModelController.$setValidity('password-matches', validate(value));
            return value;
        });

        scope.$watch(function() { return scope.otherPasswordFieldValue }, function () {
            var valid = validate(ngModelController.$viewValue);
            ngModelController.$setValidity('password-matches', valid);
        });
      }
    };
});

这一切都很好。但是我有另一个经常在同一个元素上使用的指令。该指令的细节并不重要,因为我已经证明问题的根本原因是第二个指令编译元素。一旦我添加此指令,行为就会改变。在没有编译元素的情况下,我的passwordMatches指令工作正常(如果我输入的内容与其他字段不匹配,则字段变为无效,我可以输入我想要的任何内容)。

但是一旦我编译元素,我就可以输入我想要的内容,直到我使字段匹配并且它在此之前表现正常。但是,一旦两个字段中的值匹配,如果我键入任何内容以使它们不匹配,则该字段将完全消隐。查看此内容的最简单方法是使用此jsbin:http://jsbin.com/IkuMECEf/12/edit。要重现,请键入" foo"在第一个字段中,然后尝试键入" fooo" (三个o)在第二个领域。只要你键入第三个" o"这个领域被淘汰了。如果你注释掉$ compile,它就可以了。

谢谢!

1 个答案:

答案 0 :(得分:0)

第二个指令是编译已由Angular编译的dom元素。第二次编译添加了第二个$watch,解析器等,因为所有指令的链接函数都被再次调用(here's a good detailed look at $compile)为了确认这一点,您可以在console.log内放置$watch并且你会看到(使用第二个指令)它会为每次更改触发两次 - 因为重复$watch(删除第二个指令并且它只触发一次 - 如预期的那样)。第二个编译步骤不仅会导致您遇到的问题,还可能导致其他问题。

如果必须重新编译Angular元素,则首先需要删除现有元素。

这是一种方法(评论中的解释):

compile: function(compileElement) {
  compileElement.removeAttr('another-directive');
  return function(scope, element) {

    // Create an "uncompiled" element using a copy of the current element's html 
    newe = angular.element(element.html());

    // Remember where we were
    parent= element.parent();

    // Deleting the current "compiled" element
    element.remove();

    // Add the uncompiled copy
    parent.append(news);

    // Compile the copy
    $compile(newe)(scope);   
  };

updated punker