angular指令中使用ngModelController

在这篇文章中 angular学习笔记(三十)-指令(10)-require和controller 说到了通过require属性和controller参数来让指令与指令之间互相交互.

本篇主要介绍的是指令与ngModel指令的交互.也就是说,ngModel指令虽然是内置的,但它也有自己的controller属性,其它指令也可以通过require来得到ngModel指令的controller属性的实例来与ngModel指令进行交互.

ngModelController用在什么场合呢?我们知道,ngModel提供了数据绑定,验证,样式更新,数据格式化,编译功能,但是它故意没有提供和逻辑相关的处理,比如视图的重新渲染和监听dom事件.这些和逻辑处理相关的dom,就应该使用ngModelController来进行数据绑定.

ngModelController的方法和属性很多...无法一一列举,有些也很少用,这里会重点讲一下常用的几个(带有*的):

方法:

*1. $render()

这个方法会在视图需要被更新的时候调用. 比如以下这些场景:

  • $rollbackViewValue() 被调用. 如果我们把视图值回滚到数据模型的值时,$render()会被调用.关于$rollbackViewValue()这个方法,详见此文:angular-1.3 之ng-model-options指令
  • ng-model绑定的值被程序改变了,并且$modelValue和$viewValue都和上一次不同了.

由于ng-model没有深度对比模型的变化.什么叫没有深度对比模型的变化: 也就是angular学习笔记(十四)-$watch(1)这里提到的第三个参数ifDeep,ng-model内置的对比机制,相当于这里的ifDeep是false,不进行深度对比.所以$render()只在$modelValue和$viewValue都发生了实际的改变, 才会会被调用.什么叫实际的改变? 就是说,如果$modelValue或者$viewValue是一个对象,而不是一个字符串或者数字,那么,这个对象中的一个属性值发生了变化,这不算真正的变化,因为它对象的引用地址没有发生变化,它指向的还是同一个对象.

*2. $isEmpty(value)

当我们需要判断input的value值是否为空的时候,可以使用这个方法.

value是必须要传的,注意它判断的是value值是否为空,而不是ngModel绑定的那个数据模型的值.其实可以就当它是个判断是否为空的方法,传入一个参数,判断这个参数是否为空,你传入任何值都可以.只是说,一般情况下都会把ngModel绑定的那个值传给它.

你可以自己在指令里重写这个方法,来定义自己所需要的‘是否为空‘的概念.比如用在一个类型为checkbox的input元素上,因为当checkbox的值为false的时候,$isEmpty()的结果就是empty.

如果值是undefined,null,‘‘,或者NaN,则返回true,否则返回false.

3. $setValidity(validationErrorKey, isValid);

4. $setPristine()

把元素设置到原始状态.移除元素的ng-dirty类名,添加ng-pristine类名.

5. $setDirty()

把元素设置到脏值模式.移除元素的ng-pristine类型,添加ng-dirty类名.

参考文献: https://docs.angularjs.org/api/ng/type/ngModel.NgModelController

6.$setUntouched()

把元素设置到没有触碰过的状态.移除ng-touched类名.添加ng-untouched类名.

7.$setTouched()

把元素设置到触碰过的状态.移除ng-untouched类名,添加ng-touched类名.

8.$rollbackViewValue()

参考:angular-1.3 之ng-model-options指令

9.$validate()

10.$commitViewValue()

把一个未发生的更新提交给$modelValue.

在使用ng-model-options指令的时候,input元素可能正在等待某个事件的触发,来同步一个将要发生的更新.这个方法很少用,因为ngModelController通常在事件响应中自动处理了这件事.

*11.$setViewValue(value, trigger)

更新视图值.当一个input的指令元素想要改变视图值的时候,这个方法会被调用.这通常是dom元素内部的事件来处理的.最典型的例子就是在input中输入值,会改变Hello后面的视图的值.原因就是input的输入事件会调用$setViewValue方法.类似的还有select元素.

如果value是一个对象,而不是一个字符串或者数值,那我们应该在传入$setViewValue之前先拷贝一份.因为ngModel不会深度监测对象的变化,它只看对象的引用地址是否发生了变化.如果你仅仅改变了对象的某个属性,ngModel不会意识到它已经改变了,也不会去经过$parsers和$validators管道.

因此,当对象被传入到$setViewValue函数里以后,你不能再改变它的属性值,否则可能引起当前scope下的模型值被错误地改变.

当$setViewValue被调用时,新的value将会通过$parsers和$validators管道后被提交. 如果没有配置ngModelOptions,那么value直接进入处理流程,最后它被应用到$modelValue和ng-model绑定的属性表达式上.

还有一点,所有添加在$viewChangeListeners这个数组里的函数,都会被执行.

在使用了ngModelOptions的情况下,上面的说法不适用.上面说到的这些行为都会被等待直到dom元素的updateOn事件触发.同样,如果定义了debounce延迟,那么这些行为也会在延迟时间到了以后才发生.

需要注意,执行$setViewValue()方法,不会触发$digest.

这里的trigger是做什么的,不太明白...

属性:

*1.$viewValue

指令元素的视图中实际的值.注意,它一定等于 $setViewValue(value)的value值

*2.$modelValue

ngModel绑定的数据模型的模型值.它不一定等于 $setViewValue(value)的value值,在$setViewValue(value, trigger)里面提到的使用了ngModelOptions时,比如虽然调用了$setViewValue,但是因为设置了ngModelOptions的debounce属性,所以它会延迟,等到同步的时候,value值才会被设置到$modelValue上.

3.$parsers

一个数组.数组里的元素是函数. $setViewValue(value)被赋值给$modelValue之前,value值首先会经过$parsers里的所有函数,每次将返回值传递给下一个函数.最后才被赋值到$modelValue.在这个过程中就包括了验证和转换.对于验证这个步骤,它会使用$setValidity这个方法,验证失败的将返回undefined.

4.$formatters

一个数组.数组里的元素是函数. 和$parsers一样,它也是管道.当模型值发生变化的时候被调用.模型值会倒着调用数组中的函数,然后把返回值传给下一个函数,最后返回的值就会被传递给dom元素.用来在视图中格式化模型值:

一个将小写转换为大写的格式化方法:

function formatter(value) {
  if (value) {
    return value.toUpperCase();
  }
}
ngModel.$formatters.push(formatter);

*5. $validators

一个json对象.

{
   validateName: function(modelValue,viewValue){
       return ...
   }
}

当$setViewValue(value)被赋值给$modelValue之前,会经过$parsers管道,经过$parsers管道时,就会经过这个$validators管道.其中validateName是验证的名字,函数是这个验证的方法,其中的参数modelValue和viewValue就是$modelValue和$viewValue,如果返回值是true,则通过validateName

验证,如果返回值是false,则没有通过validateName验证,如果没有通过validateName验证,$error.validateName就会为true.这就是angular内部验证表单项的原理.

eg: 自定义一个验证规则,输入内容中必须包含数字

<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);
};

*6.$asyncValidators

一个json对象.用来处理异步验证(比如一个http请求).

{
   validateName: function(modelValue,viewValue){
       return promise
   }
}

其中validateName是验证的名字,函数是这个验证的方法,其中的参数modelValue和viewValue就是$modelValue和$viewValue,返回值必须是一个promise对象,如果这个promise对象传递给它下一个.then方法失败通知,则不通过validateName验证,如果这个promise对象传递给它下一个.then方法成功通知,则表示通过validateName验证.当异步验证开始执行的时候,所有的异步验证都是平行并发的.只有当所有的验证都通过时,数据模型才会被同步更新.只要有一个异步验证没有完成,这个验证名就会被放到ngModelController的$pending属性中.另外,所有的异步验证都只会在所有的同步验证通过以后才开始.

核心代码:

<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() {
                        //请求失败
                })
            };
        }
    }
});

异步验证比较重要,所以我会另外开一篇文章来举例详解:angular中的表单数据异步验证

7.$viewChangeListeners

一个数组,数组中的元素都是函数.这些函数在视图发生改变的时候被执行,不带有什么参数,也不需要返回值.在第11条方法里说到,在不使用ngModelOptions延迟时调用$setViewValue的时候,他们就会执行.

*8.$error

json对象. 这个很简单,用到很多次了.就是所有验证失败的验证名和失败信息组成的json对象.

*9.$pending

json对象. 第6个属性里提到过的,正在进行中的异步验证会被放在这个对象里

10.$untouched

布尔值.如果元素还没有失去过焦点,那这个值就是true.

11.$touched

布尔值.如果元素已经失去过焦点,那这个值就是false.

12.$pristine

布尔值.如果元素还没有和用户发生过交互,那这个值就是true.

13.$dirty

布尔值.如果元素已经和用户发生过交互,那这个值就是true.

*14.$valid

布尔值.这个也很常用,就是当所有验证(异步同步),都通过的时候,它就是true

*15.$invalid

布尔值.这个也很常用,就是当所有验证(异步同步),其中有一个或一个以上验证失败,它就是true.

16.$name

字符串.很简单,就是获取元素的name属性.

注意上面说到的这些属性:

我们这篇文章说的是ngModelController,所以说这些属性是ngModelController的属性,但是其中有一部分一般不在ngModelController里面用,比如:$error,$pending,$valid,$invalid,等,这些属性,我们通常是这样用的:

    <div class="alert alert-danger" role="alert" ng-show="myForm.myWidget.$error.uniqueUsername">
      <strong>Oh!</strong> 已经存在的用户名!
    </div>
    <div class="panel-body">
      {{myForm.myWidget.$pending}}
    </div>

但是,你心里要知道,其实他们也是ngModelController的属性哦~

未完待续...

时间: 2024-12-13 03:08:45

angular指令中使用ngModelController的相关文章

angular js权威指南笔记三--向指令中传递数据

给指令添加属性,这个属性会成为指令内部作用域的属性 有好几种途径可以设置指令内部作用域中属性的值.最简单的方法就是使用由所属控制器提供的已经存在的作用域. 但是会导致很多其他问题.如果控制器被移除,或者在控制器的作用域中也定义了一个叫 相同的属性名,我们就被迫要修改代码 AngularJS 允许通过创建新的子作用域或者隔离作用域来解决这个常见问题 <div my-directive some-property="someProperty with @ binding">&l

[angular] 篇 指令中的scope

scope参数有以下几种方式: 1,不填,默认为scope:false 2,scope:true 3,scope:{} 4, scope:{ name: '=', age: '@', say: '&' } 下面我来分别说明他们之间的用法: 第一种:看代码 <script> angular module('exampleApp', []) .controller('scopeCtrl', function ($scope) { // do something }); .directive

Angular.js中的指令——易懂全解析

<span style="font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);"> </span><span style="font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);">angular.js中,指令是最基础的

angular 指令 要点解析

指令可以删繁就简前端的js代码,杜绝重复的js代码和html代码. 下面就对指令的重要属性进行罗列 一.restrict  =  'AEC'  分别指该指令标识位于attribute属性.element元素.class. 二.scope  = true/false/{name:'=/@/&'}    默认为false,如果设置为true 则说明指令内部独立作用域. scope: { lover: '=', name: '@', say: '&' } 1.{name:'@inputName'

AngularJS指令中的compile与link函数解析

AngularJS指令中的compile与link函数解析 通常大家在使用ng中的指令的时候,用的链接函数最多的是link属性,下面这篇文章将告诉大家complie,pre-link,post-link的用法与区别. 原文地址 angularjs里的指令非常神奇,允许你创建非常语义化以及高度重用的组件,可以理解为web components的先驱者. 网上已经有很多介绍怎么使用指令的文章以及相关书籍,相互比较的话,很少有介绍compile与link的区别,更别说pre-link与post-lin

angularjs指令中的compile详解

篇文章主要介绍了angularjs指令中的compile与link函数详解,本文同时诉大家complie,pre-link,post-link的用法与区别等内容,需要的朋友可以参考下 通常大家在使用ng中的指令的时候,用的链接函数最多的是link属性,下面这篇文章将告诉大家complie,pre-link,post-link的用法与区别. angularjs里的指令非常神奇,允许你创建非常语义化以及高度重用的组件,可以理解为web components的先驱者. 网上已经有很多介绍怎么使用指令的

angularjs指令中的compile与link函数详解补充

通常大家在使用ng中的指令的时候,用的链接函数最多的是link属性,下面这篇文章将告诉大家complie,pre-link,post-link的用法与区别. angularjs里的指令非常神奇,允许你创建非常语义化以及高度重用的组件,可以理解为web components的先驱者. 网上已经有很多介绍怎么使用指令的文章以及相关书籍,相互比较的话,很少有介绍compile与link的区别,更别说pre-link与post-link了. 大部分教程只是简单的说下compile会在ng内部用到,而且建

【转】angular指令入坑

独立作用域和函数参数 通过使用本地作用域属性,你可以传递一个外部的函数参数(如定义在控制器$scope中的函数)到指令.这些使用&就可以完成.下面是一个例子,定义一个叫做add的本地作用域属性用来保存传入函数的引用: angular.module('directivesModule').directive('isolatedScopeWithController', function () {    return {        restrict: 'EA',        scope: { 

【转】angularjs指令中的compile与link函数详解

这篇文章主要介绍了angularjs指令中的compile与link函数详解,本文同时诉大家complie,pre-link,post-link的用法与区别等内容,需要的朋友可以参考下 通常大家在使用ng中的指令的时候,用的链接函数最多的是link属性,下面这篇文章将告诉大家complie,pre-link,post-link的用法与区别. angularjs里的指令非常神奇,允许你创建非常语义化以及高度重用的组件,可以理解为web components的先驱者. 网上已经有很多介绍怎么使用指令