angular指令心得(ng-model)

来源:互联网 发布:js获取当前时间显示 编辑:程序博客网 时间:2024/05/21 09:37

angular指令心得(ng-model)

在项目中编写指令,常常会依赖其他的指令来实现想要达到的功能,其中最常用到的便是ng-model,它为我们明确了需要绑定的属性,虽然在指令中可以通过通过使用独立作用域的”=”来进行双向绑定,但使用ng-model更能简化指令的传值,符合angular的使用习惯。


指令依赖

angular中编写自定义指令是通过require属性来指定查找依赖指令的controller,并传入link函数中进行调用.

.directive("...",function(){    return {        ...        require:'ngModel',        link:function(scope,elem,attr,ngModelCtr){            ...        }        ...    }})
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

所依赖指令的控制器被传入link的第四个参数中,可以依赖多个指令,此时第四个参数则是个数组。

.directive("...",function(){    return {        ...        require:['ngModel','^?form',...],        link:function(scope,elem,attr,ctrls){            ...        }        ...    }})
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

在查找指令的过程中,如果未找到指令,则会抛出一个编译错误。 
可以通过加前缀来控制其查找的操作。

前缀作用^向上查找指令,未找到则报错。?未找到,取消报错,将null传到link的第四个参数。

ng-model

使用ng-model前,让我们先简单了解一下ng-model的工作原理。

angular是一个mvvm的框架,其主要就体现在ng-model身上 
这里写图片描述 
angular将model和view之间的联系切断,自己内部通过ng-model去实现ViewModel层,具体的细节不表,我们就看下我们要用的部分。

这里写图片描述

在每个使用ng-model的地方,都会创建一个ngModelController实例,这个实例负责管理存储在模型(由model指定)中的值与元素显示值之间的数据绑定。

ngModelController包含有$formatters和$parsers数组,会在每次更新数据绑定是调用。

当我们从页面或通过$setViewValue改变绑定的属性时,会遍历执行$parsers数组里面的方法,而当我们直接在js里面通过赋值语句修改时,则会调用$formatters数组。

// line number:25332// model -> value  // Note: we cannot use a normal scope.$watch as we want to detect the following:  // 1. scope value is 'a'  // 2. user enters 'b'  // 3. ng-change kicks in and reverts scope value to 'a'  //    -> scope value did not change since the last digest as  //       ng-change executes in apply phase  // 4. view should be changed back to 'a'  $scope.$watch(function ngModelWatch() {    var modelValue = ngModelGet($scope);    // if scope model value and ngModel value are out of sync    // TODO(perf): why not move this to the action fn?    if (modelValue !== ctrl.$modelValue &&       // checks for NaN is needed to allow setting the model to NaN when there's an asyncValidator       (ctrl.$modelValue === ctrl.$modelValue || modelValue === modelValue)    ) {      ctrl.$modelValue = ctrl.$$rawModelValue = modelValue;      parserValid = undefined;      var formatters = ctrl.$formatters,          idx = formatters.length;      var viewValue = modelValue;      while (idx--) {        viewValue = formatters[idx](viewValue);      }      if (ctrl.$viewValue !== viewValue) {        ctrl.$viewValue = ctrl.$$lastCommittedViewValue = viewValue;        ctrl.$render();        ctrl.$$runValidators(modelValue, viewValue, noop);      }    }    return modelValue;  });}];
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39

上面是angular调用$formatters的地方,它监控了model值的变化,负责model->view。


实践

<html lang="en" ><head>    <meta charset="utf-8">    <meta http-equiv="X-UA-Compatible" content="IE=edge">    <title>angular-ngModel-test</title>    <script type="text/javascript" src="http://cdn.bootcss.com/angular.js/1.5.0-beta.1/angular.js"></script>    <script type="text/javascript">        angular.module("testModule",[])            .controller("testCtr",function($scope){            })            .directive("testDire",function(){                return {                    restrict:"A",                    require:"ngModel",                    link:function(scope,elem,attr,ngModelCtr){                    }                }            })     </script></head><body ng-app="testModule" ng-controller="testCtr">    <input type="text" test-dire ng-model="say"/>    <h1>        {{say}}    </h1></body></html>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33

$formatters & $parsers


$formatters

上面搭建了一个基本的架子,现在我们通过来通过js改变模型值,在$formatters里面加入一个格式化方法来显示到页面上。

改动:

<!-- html --><body ng-app="testModule" ng-controller="testCtr">    <input type="text" test-dire ng-model="say"/>    <button ng-click="joke()">窗前明月光</button>    <h1>        {{say}}    </h1</body>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
//js.directive("testDire",function(){    return {        restrict:"A",        require:"ngModel",        link:function(scope,elem,attr,ngModelCtr){            ngModelCtr.$formatters.push(function(modelValue){                if(typeof modelValue != "undefined"){                    //返回字符串给view,不改变模型值                    return "李白睡的香";                 }            })        }    }}) 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

效果图: 
这里写图片描述

这里有个很奇怪的现象,两个绑定model的地方显示的值不一样,这是因为input是从ngModelController中获得的返回值显示出来的,这个值在通过$formatters数组时被我拦截下来,返回了另外一个字符串。


$parsers

我们还可以对界面(view)输入后的值进行处理。

改动:

//js.directive("testDire",function(){    return {        restrict:"A",        require:"ngModel",        link:function(scope,elem,attr,ngModelCtr){            ...            ngModelCtr.$parsers.push(function(viewValue){                if(typeof viewValue != "undefined"){                    return "我说:"+viewValue;                }            })        }    }})
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

效果图: 
这里写图片描述


$render & $setViewValue


$render

有的时候,$formatters数组里面也许会有不止一个方法,这时候为了保证一个最终的显示效果,就应该用到$render方法。 
TIP:$render总会在$formatters完成之后调用。

重新赋值$render方法,在改变模型值后修改字体颜色。

改动:

//js.directive("testDire",function(){    return {        restrict:"A",        require:"ngModel",        link:function(scope,elem,attr,ngModelCtr){            ...            var h1Elem = document.getElementsByTagName("h1")[0];            ngModelCtr.$render = function(){                if(typeof scope.say != "undefined"){                    h1Elem.style.color = "red";                }            }        }    }})
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

效果图: 
这里写图片描述

结果可以看到,颜色成功改变了,但是输入框没有变化。 
造成这个的原因是因为我们重写了$render方法,相当于改写了ng-model从model到view的dom操作,用自己的方式去实现


$setViewValue

的作用是为了在指令中改变ng-model绑定的值,适用于使用了独立作用域的指令(在没有双向绑定属性的情况下,无法通过scope进行赋值)。

下面在加载自执行的时候通过指令改变模型值。

改动:

//js.directive("testDire",function(){    return {        restrict:"A",        require:"ngModel",        link:function(scope,elem,attr,ngModelCtr){            ...            ngModelCtr.$setViewValue("hello");        }    }})
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

效果图: 
这里写图片描述


总结

在指令中,我们可以用通过简单的依赖ng-model去实现angular各种其他插件的兼容(如一些日期控件,富文本编辑器等),更能简化我们的指令代码,避免很多复杂的绑定逻辑,特别要注意的是$formatters,$passers,$render这些都会在初始化时去执行一遍,因此应该根据需要做好判断操

http://blog.csdn.net/qq_17371033/article/details/49248791

原创粉丝点击