directive指令二 require:'^ngModel'
本章主要是讲指令与ngModel的交互。
在angular有一个内置指令叫ngModel,它是angular用来处理表单的最重要的指令。在源码中,页面上的model值的格式化、解析、验证都是由ngModel指令所对应的控制器ngModelController来实现的。ngModelController提供了很多方法和属性,我们可以通过自定义指令的require:'ngModel'来获取内置指令ngModel的ngModelController,进而在link函数里对操作ngModelController的方法和属性。
接下来具体讲一下ngModelController的方法和属性,以及它们的具体用法。
1.两个核心属性$viewValue和$modelValue
$viewValue是指令渲染模板所用的值,而$modelValue是在控制器中流通的值。很多时候这两个值是不一样的。比如说,当页面上展示了一个日期,显示的可能是"2018/04/23",但是这个字符串在控制器中对应的值可能是一个js的Date对象的实例。
2.三个核心管道$formatters $parses $validators 和$asyncValidators
angular除了提供$viewValue和\modelValue之外,还提供了两个用来处理它们的方法,分别是$parses和$formatters。
$parsers:由view值到model值的转换器,用户输入的变化会触发其中的管道函数,页面上的model值由管道函数中的return值决定。
$formatters:和$parsers的作用相反,它是model值到view值的转化器,其view值由对应的管道函数中的return值决定。
$validators: 是一个json对象
{ validateName: function(modelValue,viewValue){ return ... } }
当$setViewValue(value)被赋值给$modelValue之前,会经过$parsers管道,经过$parsers管道时,就会经过这个$validators管道。其中,validateName是验证器的名称,其中的参数modelValue和viewValue就是和viewValue,如果返回值是true,则通过validateName。
自定义一个验证器:
<div class="alert alert-danger" role="alert" ng-show="myForm.myWidget.$error.validCharacters"> <strong>Oh!</strong> 不符合自定义的验证规则! </div>
ngModel.$validators.validCharacters = function(modelValue, viewValue) { var value = modelValue || viewValue; return /[0-9]+/.test(value); };
$asyncValidators:也是一个json对象,不过可以用来处理异步验证。
<input validate-name type="text" name="myWidget" ng-model="userContent" ng-model-options="{updateOn:'blur'}" class="form-control" required uniqueUsername> <div class="alert alert-danger" role="alert" ng-show="myForm.myWidget.$error.uniqueUsername"> <strong>Oh!</strong> 已经存在的用户名! </div>
app.directive('validateName',function($http,$q){ return { restrict:'A', require:'?^ngModel', link:function(scope,iele,iattr,ctrl){ ctrl.$asyncValidators.uniqueUsername = function(modelValue, viewValue) { var value = modelValue || viewValue; // 异步验证用户名是否已经存在 return $http.get('/api/users/' + value). then(function resolved(res) { if(res.data){ //用户名已经存在,验证失败,给下一个promise传递失败通知. return $q.reject('res.data'); } else { //用户名不存在,验证成功. return true } }, function rejected() { //请求失败 }) }; } } });
3.两个核心方法$setViewValue()和$render()
$setViewValue(): 更新视图值。原因是每个input的输入事件都会调用这个方法。但是如果value是一个对象,而不是字符串或数值,那么我们在用该方法传入值之前应该先拷贝一份,因为ngModel不会深入检测对象的变化,它只看对象的引用是否发生变化。如果只是改变了对象的某个属性,也不会去经过$parsers和$validators管道。执行$setViewValue()方法,不会触发$digest。
$render():在视图需要被更新的时候调用。由于ngModel的对比机制,$render()只在$modelValue和$viewValue都发生了实际的改变, 才会会被调用。也就是说,$render函数负责将模型值同步到视图上。
来看一下指令和控制器及其内部运作如图:
还有一些常用属性
属性 | 说明 |
---|---|
$error | json对象。就是所有验证失败的验证名和失败信息组成的json对象。 |
$pending | json对象。正在进行中的异步验证会被放在这个对象里。 |
$untouched | 布尔值。如果元素还没有失去过焦点,那这个值就是true。 |
$touched | 布尔值。如果元素已经失去过焦点,那这个值就是false。 |
$pristine | 布尔值。如果元素还没有和用户发生过交互,那这个值就是true。 |
$dirty | 布尔值。如果元素已经和用户发生过交互,那这个值就是true。 |
$valid | 布尔值。这个也很常用,就是当所有验证(异步同步),都通过的时候,它就是true。 |
$invalid | 布尔值。这个也很常用,就是当所有验证(异步同步),其中有一个或一个以上验证失败,它就是true。 |
$name | 字符串。很简单,就是获取元素的name属性。 |
$isEmpty | 当我们需要判断input的value值是否为空的时候,可以使用这个方法。 |