ngModel。$解析器不在指令中工作

时间:2016-03-14 07:18:08

标签: angularjs

我想动态地将指令添加到表单元素(input,select,textarea ...)

<div ng-app="app" ng-controller="ctrl">
    <form form-validator>
    <input type="text" ng-model="model1">
    <input type="text" ng-model="model2">
    <input type="text" ng-model="model3">
    <textarea ng-model="model4"></textarea>
    <select>
       <option ng-model="model5" value="1">Name</option>
    </select>
    </form>
</div>

指令

var app=angular.module('app',[])
.controller('ctrl',function($scope){

})
.directive('formValidator',function($compile){
    return{
        restrict: 'A',
        link: function(scope, elem, attrs){
            //is it possible to do this with one line of code?
            elem.find('input').attr('validate-field','');
            elem.find('select').attr('validate-field','');
            elem.find('textarea').attr('validate-field','');
            $compile(elem.contents())(scope);
        }
    }
})
.directive('validateField',function(){
    return{
        restrict: 'A',
        require:['^ngModel'],
        link: function(scope, element, attr, ctrls){
            var valid = false;
            var ngModel = ctrls[0];
            alert('before validation');
            ngModel.$parsers.unshift(function (value){ 
               alert('validating');
               valid = validator(value);
               ngModel.$setValidity('required', valid, ctrls);
               return valid ? value : undefined;
            });
        }
    }
});

上面的代码能够将validate-field属性添加到表单元素中:

<input type="text" ng-model="model1" validate-field>

问题是ngModel.$parsers.unshift未被调用,alert('before validation');被调用,但alert('validating');未被调用。

我错过了什么?

2 个答案:

答案 0 :(得分:2)

目前发生的事情是formValidator指令链接函数在编译完所有子validateField指令后进行评估。因为link: function(){ .. }函数被认为与postLink函数相同,后者在所有子scope链接就绪后被调用。你的情况也是如此。

所以我说要调用你的链接函数以确保在指令编译之前它会添加validate-fields。为此,您需要使用preLink函数。

.directive('formValidator', function($compile) {
    return {
      restrict: 'A',
      link: {
        pre: function(scope, elem, attrs) {
          elem.find('input').attr('validate-field', '');
          elem.find('select').attr('validate-field', '');
          elem.find('textarea').attr('validate-field', '');
          $compile(elem.contents())(scope);
        }
      }
    }
})

Plunkr Here

更好的版本只能编译一次父指令

  .directive('formValidator', function($compile) {
    return {
      restrict: 'A',
      compile: function(elem, attrs) {
        elem.find('input').attr('validate-field', '');
        elem.find('select').attr('validate-field', '');
        elem.find('textarea').attr('validate-field', '');
        //removed to avoid infinite directive compile
        elem.removeAttr('form-validator');
        var linkFn = $compile(elem);
        return function(scope, element, attr) {
          linkFn(scope);
        }
      }
    }
  })

Check here

答案 1 :(得分:1)

也许,您想要使用$formatters

plunker上的示例。

&#13;
&#13;
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<!DOCTYPE html>
<html>

  <head>
    <script data-require="angular.js@2.0.0" data-semver="1.4.7" src="https://code.angularjs.org/1.4.7/angular.js"></script>
    <link rel="stylesheet" href="style.css" />
    <script src="script.js"></script>
  </head>

  <body>
    <h1>Angular Directive</h1>
    <div ng-app="app" ng-controller="ctrl">
      <form form-validator="">
        <input type="text" ng-model="model1" />
        <input type="text" ng-model="model2" />
        <input type="text" ng-model="model3" />
        <textarea ng-model="model4"></textarea>
        <select ng-model="model5">
          <option value="1">Name</option>
        </select>
        <button ng-click="model1='A'"> set model 1 value A</button>
      </form>
    </div>
    <script>
      
    var app=angular.module('app',[])
    .controller('ctrl',function($scope){

     })
     .directive('formValidator',function($compile){
    return{
        restrict: 'A',
        priority:10000,
        link: function(scope, elem, attrs){
            //is it possible to do this with one line of code?
            elem.find('input').attr('validate-field','');
            elem.find('select').attr('validate-field','');
            elem.find('textarea').attr('validate-field','');
            $compile(elem.contents())(scope);
        }
    }
   })
   .directive('validateField',function(){
    return{
        restrict: 'A',
        require:'ngModel',
        link: function(scope, element, attr, ngModel){
            var valid = false;
            console.log('before validation',ngModel);
            ngModel.$formatters.unshift(function (value){ 
               console.log('validating');
               //valid = validator(value);
               ngModel.$setValidity('required', valid, ngModel);
               return valid ? value : undefined;
            });
             ngModel.$parsers.unshift(function (value){ 
               console.log('$parsers');
               valid = true;
               ngModel.$setValidity('required', valid, ngModel);
               return valid ? value : undefined;
            });
        }
    }
  });
    </script>
  </body>

</html>
&#13;
&#13;
&#13;