前面的“表单控制”那章,实际上讲的就是 FormController ,只是那里是从 scope 中获取到的引用。现在从指令定义的角度,来更清楚地了解 FormController 及 NgModelController 是如何配合工作的。
先说一下, form 和 ngForm 是官方定义的两个指令,但是它们其实是同一个东西。前者只允许以标签形式使用,而后者允许 EAC 的形式。DOM 结构中, form 标签不能嵌套,但是 ng 的指令没有这个限制。不管是 form 还是 ngForm ,它们的 controller 都被命名成了 form 。 所以 require 这个参数不要写错了。
FormController 的几个成员是很好理解的:
  <div ng-controller="TestCtrl">
    <div ng-form test>
      <input ng-model="a" type="email" />
      <button ng-click="do()">查看</button>
    </div>
  </div>
  var app = angular.module('Demo', [], angular.noop);
  app.directive('test', function(){
    var link = function($scope, $element, $attrs, $ctrl){
      $scope.do = function(){
        //$ctrl.$setDirty();
        console.log($ctrl.$pristine); //form是否没被动过
        console.log($ctrl.$dirty); //form是否被动过
        console.log($ctrl.$valid); //form是否被检验通过
        console.log($ctrl.$invalid); //form是否有错误
        console.log($ctrl.$error); //form中有错误的字段
      }
    }
    return {compile: function(){return link},
            require: 'form',
            restrict: 'A'}
  });
  app.controller('TestCtrl', function($scope){
  });
$error 这个属性,是一个对象, key 是错误名, value 部分是一个列表,其成员是对应的 NgModelController 的实例。
FormController 可以自由增减它包含的那些,类似于 NgModelController 的实例。在 DOM 结构上,有 ng-model 的 input 节点的 NgMoelController 会被自动添加。
这两个手动使用机会应该不会很多。被添加的实例也可以手动实现所有的 NgModelController 的方法
  <div ng-controller="TestCtrl">
    <bb />
    <div ng-form test>
      <input ng-model="a" type="email" />
      <button ng-click="add()">添加</button>
    </div>
  </div>
 1   var app = angular.module('Demo', [], angular.noop);
 2
 3   app.directive('test', function(){
 4     var link = function($scope, $element, $attrs, $ctrl){
 5       $scope.add = function(){
 6         $ctrl.$addControl($scope.bb);
 7         console.log($ctrl);
 8       }
 9     }
10
11     return {compile: function(){return link},
12             require: 'form',
13             restrict: 'A'}
14   });
15
16   app.directive('bb', function(){
17     var controller = function($scope, $element, $attrs, $transclude){
18       $scope.bb = this;
19       this.$name = 'bb';
20     }
21
22     return {compile: angular.noop,
23             restrict: 'E',
24             controller: controller}
25   });
26
27   app.controller('TestCtrl', function($scope){
28   });
整合 FormController 和 NgModelController 就很容易扩展各种类型的字段:
  <div ng-controller="TestCtrl">
    <form name="f">
      <input type="my" ng-model="a" />
      <button ng-click="show()">查看</button>
    </form>
  </div>
 1   var app = angular.module('Demo', [], angular.noop);
 2
 3   app.directive('input', function(){
 4     var link = function($scope, $element, $attrs, $ctrl){
 5       console.log($attrs.type);
 6       var validator = function(v){
 7         if(v == '123'){
 8           $ctrl.$setValidity('my', true);
 9           return v;
10         } else {
11           $ctrl.$setValidity('my', false);
12           return undefined;
13         }
14       }
15
16       $ctrl.$formatters.push(validator);
17       $ctrl.$parsers.push(validator);
18     }
19
20     return {compile: function(){return link},
21             require: 'ngModel',
22             restrict: 'E'}
23   });
24
25   app.controller('TestCtrl', function($scope){
26       $scope.show = function(){
27         console.log($scope.f);
28       }
29   });
虽然官方原来定义了几种 type ,但这不妨碍我们继续扩展新的类型。如果新的 type 参数值不在官方的定义列表里,那会按 text 类型先做处理,这其实什么影响都没有。剩下的,就是写我们自己的验证逻辑就行了。
上面的代码是参见官方的做法,使用格式化的过程,同时在里面做有效性检查。