directive二

本章主要是讲指令与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值决定。parsers属性有事件监听,当ng-model数据在dom端发生变化时候,就会触发事件,执行管道函数。比较常见的就是用户输入/编辑值。
**$formatters:**和$parsers的作用相反,它是model值到view值的转化器,其view值由对应的管道函数中的return值决定。formatters属性会有实时监听事件,当我们ng-model的数据以编程的方式改变时候,就会触发事件,此时会以管道形式执行所有函数,包括传参及其返回值操作。比如我们在通过js改变了值,此时就会触发事件。
$validators: 是一个json对象

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

当$setViewValue(value)被赋值给$modelValue之前,会经过$parsers管道,经过$parsers管道时,就会经过这个$validators管道。其中,validateName是验证器的名称,其中的参数modelValue和viewValue就是$modelValue和$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()方法,不会触发digestdigest。 **render()????*在视图需要被更新的时候调用。由于ngModel的对比机制,$render()只在$modelValue和$viewValue都发生了实际的改变, 才会会被调用。也就是说,$render函数负责将模型值同步到视图上。

来看一下指令和控制器及其内部运作如图:
angular之directive(二)
这里讲一下具体的过程。
一般而言,html上通过ng-model绑定后,就会传入到$modelValue,如下面的ng-model=“data”。

<div ng-app="myApp" ng-controller="myCtrl">
    <input ng-model="data" type="text" test/>
    <span ng-click="changeModel()">{{data}}</span>
</div>

看一下js代码:

.controller('myCtrl', function($scope){
   $scope.data = 'yellow'; 
   
   $scope.changeModel = function() {
           $scope.data = 'yellow'; 
    }
})
.directive('test', function(){
    return {
        require: 'ngModel',
        link: function(scope, elem, attrs, ngModel){
            ngModel.$formatters.push(function(value){
                //formats the value for display when ng-model is changed
                return 'brown'; 
            });
            ngModel.$parsers.push(function(value){
                //formats the value for ng-model when input value is changed
                return 'green'; 
            });
            
        }
    };
});

看一下页面效果:
初始页面:
angular之directive(二)
一旦修改input框的值,就能被$parsers捕捉到,$parsers返回值就对应了$modelValue的值,从而改变{{data}},但是并没有改变页面上input框的值。效果:
angular之directive(二)
一旦点击了span的值,触发changeModel()之后,$scope.data发生了变化,就能被$formatters捕捉到,$formatters的返回值对应了$viewValue的值,此时页面就会变成初始页面。


还有一些常用属性

属性 说明
$error json对象。就是所有验证失败的验证名和失败信息组成的json对象。
$pending json对象。正在进行中的异步验证会被放在这个对象里。
$untouched 布尔值。如果元素还没有失去过焦点,那这个值就是true。
$touched 布尔值。如果元素已经失去过焦点,那这个值就是false。
$pristine 布尔值。如果元素还没有和用户发生过交互,那这个值就是true。
$dirty 布尔值。如果元素已经和用户发生过交互,那这个值就是true。
$valid 布尔值。这个也很常用,就是当所有验证(异步同步),都通过的时候,它就是true。
$invalid 布尔值。这个也很常用,就是当所有验证(异步同步),其中有一个或一个以上验证失败,它就是true。
$name 字符串。很简单,就是获取元素的name属性。
$isEmpty 当我们需要判断input的value值是否为空的时候,可以使用这个方法。

相关文章:

  • 2021-05-20
  • 2021-07-04
  • 2022-02-16
  • 2022-12-23
  • 2022-12-23
  • 2021-11-28
  • 2022-12-23
  • 2022-12-23
猜你喜欢
  • 2021-07-03
  • 2022-12-23
  • 2021-09-13
  • 2021-06-03
  • 2021-06-04
  • 2021-06-09
  • 2021-07-24
相关资源
相似解决方案