AngularJS:如何正确地重新编译一个元素指令,该指令的输入字段与ngModel和输入指令绑定在一起,而不会出现内存泄漏

AngularJS:如何正确地重新编译一个元素指令,该指令的输入字段与ngModel和输入指令绑定在一起,而不会出现内存泄漏,angularjs,validation,input,angularjs-directive,angularjs-scope,Angularjs,Validation,Input,Angularjs Directive,Angularjs Scope,背景 我查看并没有发现类似的内容,因此我一直在尝试创建一个可重用的输入包装器元素指令,该指令可以放入一个规则的角度形式中,与ngModel和表单控制器正确交互,并且能够采用与常规输入相同的验证指令(例如,required、ng minlength等)和任何其他自定义指令,并将它们应用于它包含的子输入元素 基本上,该指令是一种更高级的常规输入形式(提供bootstrap 3验证高亮显示、文本框内的验证图标等) 问题: 应该可以使用传递到包装器指令的指令列表动态地修饰子输入 重新编译指令有助于将指令

背景 我查看并没有发现类似的内容,因此我一直在尝试创建一个可重用的输入包装器元素指令,该指令可以放入一个规则的角度形式中,与ngModel和表单控制器正确交互,并且能够采用与常规输入相同的验证指令(例如,required、ng minlength等)和任何其他自定义指令,并将它们应用于它包含的子输入元素

基本上,该指令是一种更高级的常规输入形式(提供bootstrap 3验证高亮显示、文本框内的验证图标等)

问题: 应该可以使用传递到包装器指令的指令列表动态地修饰子输入

  • 重新编译指令有助于将指令附加/分离到 子输入,但主作用域上的观察者以某种方式保持 增加(内存泄漏)?我相信我需要摧毁的范围 指令,并更新它来解决这个问题,但我没有 找到了一种不引起摘要错误的方法
  • 这很可能与#1有关,但表单本身也显示了符号 由于清理不当。与错误对应的$error字段 将显示与“必需”相关的多个错误对象 注入几个不同的指令,显示每次 指令重新编译后,它会在父窗体上留下一些残留物
  • 完整演示:

    包装器指令:

    .directive("validatableInput", ['$compile', function($compile) {
      return {
        restrict: 'E',
        require: ["^form","?ngModel"],
        replace: false,
        templateUrl: "validation-input-template.htm",
        scope: {
          label: '@',
          id: '@',
          type: '@',
          bindModel: '=',
          name: '@',
          validationModel: '=',
          directives: '&'
        },
        link: function(scope, elem, attrs, controllers) {
          var inputElem = elem.find('input'), 
              newInputElem = null,
              formController = controllers[0];
    
          scope.type = scope.type || 'text';
          scope.$watch('directives()', function(newDirectives, oldDirectives) {
               newInputElem = inputElem.clone();
              for (var directive in newDirectives) {
                newInputElem.attr(directive, newDirectives[directive] ? newDirectives[directive] : directive);
              }
              //  formController.$removeControl(scope.name);
              elem.find('input').replaceWith(newInputElem);
    
              $compile(elem.contents())(scope);
    
          }, true);
    
        }
      };
    }]);
    
    <script type='text/ng-template' id='validation-input-template.htm'>
        <div class="row">
          <div class="form-group has-feedback" data-ng-class="{'has-success':focus && validationModel.$valid ,'has-error':validationModel.$dirty && validationModel.$invalid , 'has-warning':validationModel.$pending}">
            <label class="control-label" for='{{id}}-input'>{{label}}</label>
            <input type="{{type}}" class="form-control" id="{{id}}-input" data-ng-model="bindModel" ng-focus="focus=true;" name="{{name}}">
        </div>
      </script>
    
    <form name='aForm'>
              <validatable-input label='User Name' bind-model='main.userName' name='uname' validation-model='aForm.uname' id='some-id-2' directives='main.injectedDirectives'> </validatable-input>
              <validatable-input label='ID Number' type='text' bind-model='main.idNumber' name='idNum' validation-model='aForm.idNum' id='some-id-3' directives='{"required":"", "digit-only":"","prime":""}'> </validatable-input>
    </form>
    
    .controller("MainController", ['$scope', function($scope) {
      this.LIST = {
        'required': '',
        'ng-minlength': '3',
        'ng-maxlength': '8',
        'digit-only': '',
        'even':'',
         'ng-model-options':'{ debounce: 2000 }'
      };
      this.injectedDirectives = {};
    
      this.injectDirective = function() {
        for (var key in this.LIST) {
          if (this.LIST.hasOwnProperty(key) && !this.injectedDirectives.hasOwnProperty(key)) {
            this.injectedDirectives[key] = this.LIST[key];
            return;
          }
        }
        this.injectedDirectives= {};
      };
    }]); 
    
    指令模板(删除不相关部分):

    .directive("validatableInput", ['$compile', function($compile) {
      return {
        restrict: 'E',
        require: ["^form","?ngModel"],
        replace: false,
        templateUrl: "validation-input-template.htm",
        scope: {
          label: '@',
          id: '@',
          type: '@',
          bindModel: '=',
          name: '@',
          validationModel: '=',
          directives: '&'
        },
        link: function(scope, elem, attrs, controllers) {
          var inputElem = elem.find('input'), 
              newInputElem = null,
              formController = controllers[0];
    
          scope.type = scope.type || 'text';
          scope.$watch('directives()', function(newDirectives, oldDirectives) {
               newInputElem = inputElem.clone();
              for (var directive in newDirectives) {
                newInputElem.attr(directive, newDirectives[directive] ? newDirectives[directive] : directive);
              }
              //  formController.$removeControl(scope.name);
              elem.find('input').replaceWith(newInputElem);
    
              $compile(elem.contents())(scope);
    
          }, true);
    
        }
      };
    }]);
    
    <script type='text/ng-template' id='validation-input-template.htm'>
        <div class="row">
          <div class="form-group has-feedback" data-ng-class="{'has-success':focus && validationModel.$valid ,'has-error':validationModel.$dirty && validationModel.$invalid , 'has-warning':validationModel.$pending}">
            <label class="control-label" for='{{id}}-input'>{{label}}</label>
            <input type="{{type}}" class="form-control" id="{{id}}-input" data-ng-model="bindModel" ng-focus="focus=true;" name="{{name}}">
        </div>
      </script>
    
    <form name='aForm'>
              <validatable-input label='User Name' bind-model='main.userName' name='uname' validation-model='aForm.uname' id='some-id-2' directives='main.injectedDirectives'> </validatable-input>
              <validatable-input label='ID Number' type='text' bind-model='main.idNumber' name='idNum' validation-model='aForm.idNum' id='some-id-3' directives='{"required":"", "digit-only":"","prime":""}'> </validatable-input>
    </form>
    
    .controller("MainController", ['$scope', function($scope) {
      this.LIST = {
        'required': '',
        'ng-minlength': '3',
        'ng-maxlength': '8',
        'digit-only': '',
        'even':'',
         'ng-model-options':'{ debounce: 2000 }'
      };
      this.injectedDirectives = {};
    
      this.injectDirective = function() {
        for (var key in this.LIST) {
          if (this.LIST.hasOwnProperty(key) && !this.injectedDirectives.hasOwnProperty(key)) {
            this.injectedDirectives[key] = this.LIST[key];
            return;
          }
        }
        this.injectedDirectives= {};
      };
    }]); 
    
    可能的重复可能的重复