$ compile之后的ng-class奇怪行为

时间:2015-03-21 10:11:56

标签: angularjs angularjs-directive ng-class

我制定了以下指令:

(function () {
    'use strict';

    angular
        .module('app.widgets')
        .directive('zzForminput', formInput);

    function formInput($compile) {
        // Usage:
        //     <div zz-forminput></div>

        function setupDom(element) {
            var input = element.querySelector("input, textarea, select");
            var type = input.getAttribute("type");
            var name = input.getAttribute("name");
            if (type !== "checkbox" && type !== "radio") {
                input.classList.add("form-control");
            }
            var label = element.querySelector("label");
            label.classList.add("control-label");

            element.classList.add("form-group");
            return name;
        }

        function addNgClass(form, element, name, $compile, scope) {
            var isExistingNgClass = element[0].attributes["data-ng-class"] || element[0].attributes["ng-class"];
            if (!isExistingNgClass) {
                var ngClass = "{'has-error':" + form.$name + "." + name + ".$invalid && " +
                                "(" + form.$name + "." + name + ".$dirty || vm.submit), " +
                                "'has-success':" + form.$name + "." + name + ".$valid && " +
                                form.$name + "." + name + ".$dirty}";
                element.attr("data-ng-class", ngClass);
                $compile(element)(scope);
            }
        }

        function link($compile) {
            return function (scope, element, attrs, form) {
                var name = setupDom(element[0]);

                addNgClass(form, element, name, $compile, scope);
            }
        }

        return {
            restrict: 'A',
            require: '^form',
            link: link($compile)
        }
    }

}());

我使用的指令为:

<div zz-forminput>
   <label for="firstName" class="col-md-4">First Name*</label>
   <div class="col-md-8">
       <input type="text" name="firstName" id="firstName" data-ng-model="vm.userDetails.firstName" required data-ng-maxlength="100">
   </div>
</div>

Angular成功编译标记。当我在输入字段中输入任何文本时,has-success不会添加到div。但是当我清除文本框时,has-success类被应用于div。现在,当我在输入中输入一些文本时,has-success将应用于div。

请为我提供此问题的解决方案

1 个答案:

答案 0 :(得分:1)

您看到这种奇怪行为的原因是因为您如何处理托管指令的元素的内容。

内容(包括输入上的ng-model指令)会被编译两次:一次是Angular越过DOM(在编译阶段),一次是手动调用{{1 service(在你的指令的链接阶段)。这会导致$compile指令以相同的名称注册两次 - 与父窗体控制器一起注册,长话短说,会导致一些奇怪。

处理内容的正确方法是使用提供给指令的ng-model函数的transclude函数。

link

或简单地通过模板使用transclude: true, link: function(scope, element, attrs, ctrls, transclude){ transclude(scope, function(clone){ element.append(clone); // clone is the clone of the contents, prebound to scope } } ,因为您不需要做任何特殊的事情

<div ng-transclude></div>

但是你甚至不需要做任何这样的事情,因为你的指令只是在内容上放了一些类并将一些类应用到自身,你试图用transclude: true, template: '<div ng-transclude></div>` 来做,这要求你使用ng-class。而不是这样做,只需观察变化并直接应用一个类:

$compile

并且不需要link: function(scope, element, attrs, formCtrl){ var inputName = setupDom(element[0]); scope.$watch(function(){ return formCtrl[inputName].$valid && formCtrl[inputName].$dirty; }, function(v){ if (v) element.addClass("has-success"); else element.removeClass("has-success"); }); scope.$watch(function(){ return formCtrl[inputName].$invalid && (formCtrl[inputName].$dirty || formCtrl.$submitted); }, function(v){ if (v) element.addClass("has-error"); else element.removeClass("has-error"); }) } $compile

plunker