caoruiy‘s blog

Wisdom outweighs any wealth

Angular-API-type-ngModel.NgModelController

NgModelControllerngModel 指令提供API。控制器包含用于数据绑定,验证,CSS更新和值格式化和解析的服务。它有意的不包含处理DOM渲染或监听DOM事件的任何逻辑。这样的DOM相关逻辑应该由使用 NgModelController 的数据绑定到控制元素的其他指令来提供。 AngularJS为大多数输入元素提供了这种DOM逻辑。

Methods

$render()

视图应当如何更新,当视图需要更新的时候会被调用。使用ng-model的指令应该自行实现这个方法。自定义更细视图的方式。

$render() 方法在以下情况下被调用:
$rollbackViewValue() 被调用。如果我们将视图值回滚到最后提交的值,则调用 $render() 来更新输入控件。
– ng-model 引用的值以编程方式更改,$modelValue$viewValue 与上次不同。

因为ng-model没有做深度的观察,所以 $render() 只有在 $modelValue$viewValue 的值实际上与之前的值不同时被调用。如果 $modelValue$viewValue 是对象(而不是字符串或数字),那么如果只更改对象上的属性,则不会调用 $render()

重写该方法,以实现自定义的更新视图的方式

$isEmpty(value)

该方法用于判断输入值是否为空。

例如,使用ngModelController的指令需要判断其中是否有输入值的时候会使用该方法。该方法可用来判断值是否为undefined,”,null或者NaN。

你可以根据自己的需要重载该方法。

$setPristine()

该方法用于设置控制到原始状态。

该方法可以移除’ng-dirty’类并将控制恢复到原始状态(‘ng-pristine’类)。

$setDirty()

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

$setUntouched()

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

$setTouched()

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

$rollbackViewValue()

取消更新并重置输入元素的值,以防止更新 $modelValue,这可能是由待处理的去抖动事件引起的,或者因为输入正在等待一些未来的事件。

如果您有一个使用 ng-model-options 的输入来设置有某些特殊事件(如blur)触发的去抖动更新或更新,那么可能会有一段时间 $viewValuengModel$modelValue 不同步。

译注: ng-model-options 可以用来设置双向数据绑定的同步时机,比如一个用户输入,你不想用户抬起按键时就同步,而是等到输入框失去焦点时,在等待失去焦点这段时间,数据时不同步的。

在这种情况下,您可以使用 $rollbackViewValue() 手动取消即将来到的更新,并将输入重置为最后提交的视图值。

如果您尝试在这些去抖动/即将来到的事件触发之前以编程方式更新ngModel的 $modelValue,这可能有点难实现,因为AngularJS的脏检查机制无法判断模型是否实际更改。

$rollbackViewValue() 应该在某input有一个待处理的事件时,在以编程方式更改model之前调用。这样可以确保input输入框也被最新的model值所更新,同时所有即将来到的事件都将被取消。

$validate()

运行每个注册的验证器(第一个同步验证器,然后是异步验证器)。如果有效性变为无效,则模型将被设置为 undefined,除非 ngModelOptions.allowInvalid 为真。如果有效性更改为有效,则将模型设置为上一个可用的有效 $modelValue,即从作用域设置的最后一个解析值或最后一个值。

$commitViewValue()

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

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

$setViewValue(value, trigger);

  • value : 来自视图的值
  • trigger : 触发更新的事件

更新视图值。

当控件想要更改视图值时,应该调用此方法;通常,这是从DOM事件处理程序中完成的。例如,input 指令在输入值更改时调用它,select 指令在选择选项时选择调用它。

这是因为 input 输入事件会透明的调用该方法。

调用 $setViewValue 时,新 value 将通过$parsers$validators管道进行提交。如果没有指定特殊的ngModelOptions,那么当前值将通过 $parsers 管道直接发送进行处理。之后,调用 $validators$asyncValidators,并将该值应用于 $modelValue。最后,将值设置为 ng-model 属性中指定的表达式,并调用 $viewChangeListeners 列表中的所有注册的更改侦听器。

如果 ngModelOptions 指令与 updateOn 一起使用,并且未列出 default 触发器,所有这些操作将等待直到在DOM元素上触发某一个 updateOn 事件。如果ngModelOptions指令与此特定事件的自定义去抖动一起使用,则所有这些操作将被去抖动。请注意,一旦updateOn事件被触发,或者如果指定去抖动,一旦定时器用尽,$digest将被触发。

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

在任何情况下,传递给该方法的值应始终反映控件的当前值。例如,如果您为输入元素调用 $setViewValue,则应传递输入的DOM值。否则,控制和范围模型变得不同步。

还要注意,$setViewValue不会调用 $render 或以任何方式更改控件的DOM值。如果要以编程方式更改控件的DOM值,我们应该更新ngModel范围表达式。其新值将由模型控制器拾取,该控制器将通过 $formatters 运行它,$render 它来更新DOM,最后调用 $validate

$overrideModelOptions(options);

以编程方式覆盖当前的模型选项设置。

以前的ModelOptions值不会被修改。相反,一个新的ModelOptions对象将继承自上一个覆盖或继承在给定参数中定义的设置。

$setValidity(validationErrorKey, isValid);

  • validationErrorKey :验证器的名称。 validationErrorKey将分配给 $error [validationErrorKey]$pending [validationErrorKey](对于未实现的$asyncValidators),以便它可用于数据绑定。 validationErrorKey应该在camelCase中,并且将被转换为类名的dash-case。示例:myError将导致 ng-valid-my-errorng-invalid-my-error 类,并且可以绑定为 {{someForm.someControl.$error.myError}}
  • isValid : 当前状态是valid(true),invalid(false),pending(undefined)或skipped(null)。待处理用于未实现的$asyncValidators。当由于解析错误而导致验证器不运行,并且$asyncValidators由于任何$validators失败而不运行时,由于AngularJS会使用Skipped。

更改有效性(validity)状态,并通知表单。

此方法可以在 $parsers / $formatters 或自定义验证实现中调用。然而,在大多数情况下,使用 ngModel.$validatorsngModel.$asyncValidators 集合将自动调用 $setValidity 就足够了。

Properties

$viewValue

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

$modelValue

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

$parsers

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

从解析器返回 undefined 意味着发生解析错误。在这种情况下,除非将ngModelOptions.allowInvalid设置为true,否则不会运行$validators,并将ngModel设置为undefined。解析错误存储在 ngModel.$error.parse 中。

function parse(value) {
  if (value) {
    return value.toLowerCase();
  }
}
ngModelController.$parsers.push(parse);

$formatters

一个数组,数组里的元素是函数。

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

当绑定的ngModel表达式以编程方式更改时,作为流水线执行的函数数组。当用户交互更改控件的值时,不会调用$ formatter。

Formatters 用于格式化/转换( format / convert )$modelValue以显示在控件中。

比如,你可以在页面输入内容时,将输入字母改成大写:

div(edit-able ng-model="edit") edit-able
input(ng-model="edit")
app.directive('editAble', function(){
    return {
        require : 'ngModel',
        link : function(scope, element, attrs, ctrl){
            // UI -> model
            element.on('keyup', function(){
                scope.$apply(function(){
                    ctrl.$setViewValue(ctrl.$viewValue)
                    ctrl.$render();
                })
            })
            ctrl.$formatters.push(function f01(value){
                console.log(value)
                if(typeof value == 'string'){
                    return value.toUpperCase()
                }
            })  
            // model -> UI
            ctrl.$render = function(){
                element[0].value = ctrl.$viewValue;
            };
        }
    }
});

$formatters 只能在用户修改输入的时候调用,比如在上例中,只有修改 input 时,$formatters 才会被调用,使得 div 内的字母被最大化。修改div本身时,不会调用。

但是,当你在控制器中修改 edit 的值时,会触发 $formatters

$validators

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

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

简单的示例,输入内容必须包含数字:

form(name='myForm')
  input(ng-model="edit" hasInteger)
  .error {{myForm.$error}}
app.directive('hasInteger', function(){
    return {
        require : 'ngModel',
        link : function(scope, element, attrs, ctrl){
            var errorName = attrs.name || attrs.ngModel;
            ctrl.$validators[errorName] = function(modelValue, viewValue){
                var value = modelValue || viewValue;
                return /[0-9]+/.test(value);
            }
        }
    }
})

这里有一个问题,如何在输入框输入的同时,修改输入框内的内容??

$asyncValidators

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

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

ngModel.$asyncValidators.uniqueUsername = function(modelValue, viewValue) {
  var value = modelValue || viewValue;

  // 远程请求
  return $http.get('/api/users/' + value).
     then(function resolved() {
       // 用户名存在,说明校验失败
       return $q.reject('exists');
     }, function rejected() {
       // 用户名不存在,校验通过
       return true;
     });
};

$viewChangeListeners

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

$error

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

$pending

json对象。正在进行中的异步验证会被放在这个对象里

$untouched

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

$touched

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

$pristine

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

$dirty

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

$valid

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

$invalid

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

$name

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

点赞

发表评论

电子邮件地址不会被公开。 必填项已用*标注