AngularJS指令进阶 -- ngModelController详解
前言
在Angular应用中,ng-model指令时不可缺少的一个部分,它用来将视图绑定到数据,是双向绑定魔法中重要的一环。ngModelController则是ng-model指令中所定义的controller。这个controller包含了一些用于数据绑定,验证,CSS更新,以及数值格式化和解析的服务。它不用来进行DOM渲染或者监听DOM事件。与DOM相关的逻辑都应该包含在其他的指令中,然后让这些指令来试用ngModelController中的数据绑定功能。
注意:本篇文章不是对NgModelController文档的说明,而是更偏向实践。下面我将全程带领大家去实现一个自定义指令,并且利用ng-model属性来做双方的数据绑定。
示例
我们的app中使用了一个自定义的指令,名字叫做timeDruation
如下
<div ng-app="HelloApp" ng-controller="HelloController">
<h1>自定义指令</h1>
<time-duration ng-model="test"></time-duration>
<h1>默认指令</h1>
<input ng-model="test">second
</div>
JS代码如下,
angular.module('HelloApp', [])
.directive('timeDuration', TimeDurationDirective);
.controller('HelloController', function($scope) {
$scope.test = 1;
});
我们的示例指令可以做这样一件事,可以指定几个常见的时间单位,并且能够输入数据。最终我们将得到对应的秒数。其功能的截图如下,
这里我们特意将test变量分别绑定到我们的自定义指令和默认指令中,以观察其效果。
自定义指令
闲话少叙,下面来看代码
先上指令的模板。从上图中可以看出,指令包含一个输入框一个下拉选择框。
<div class="time-duration">
<input ng-model='num'>
<select ng-model='unit'>
<option value="seconds">Seconds</option>
<option value="minutes">Minutes</option>
<option value="hours">Hours</option>
<option value="days">Days</option>
</select>
</div>
模板其实很简单,这里就不多说了。下面我们来看看这个指令的逻辑部分。
function TimeDurationDirective() {
var tpl = '....'; // 指令模板代码就是上面的内容,这里就不复制了。 return {
restrict: 'E',
replace: true,
template: tpl,
require: 'ngModel',
scope: {},
link: function(scope, element, attrs, ngModelController) {
var multiplierMap = {
seconds: 1,
minutes: 60,
hours: 3600,
days: 86400
};
var multiplierTypes = ['seconds', 'minutes', 'hours', 'days']; // TODO
}
};
}
指令的link方法我们暂时TODO了它。后面会逐步完善。
我先来看看这个指令的定义,其中用到了require声明。简单来说,require的作用就是为这个directive声明一个依赖关系,表明此directive依赖另一个指令的controller属性。
这里稍微说明一下require的衍生用法。
我们可以在require前加上修辞量词,比如,
return {
require: '^ngModel'
} return {
require: '?ngModel'
} return {
require: '?^ngModel'
}
1、^前缀修饰表示允许查找当前指令的父级指令,如果找不到对应指令的controller则抛出一个错误。
2、?则表示将这个require动作变成一个可选项,意思就是找不到对应指令的controller就算了,不会抛出错误。
3、当然,我们也可以联合使用这两个前缀修饰。
相对?ngModel,^ngModel我们使用的频率要更加高一点。
比如
<my-directive ng-model="my-model">
<other-directive></other-directive>
</my-directive>
这时,我们在other-directive中使用require: ^ngModel
,它将会自动查找my-directive指令声明中的controller属性。
使用NgModelController
当我们声明了require: 'ngModel'
之后,在link方法中会注入第四个参数,这个参数就是我们require的那个指令对应的controller。这里就是内置指令ngModel的指控器ngModeController了。
link: function (scope, element, attrs, ngModelCtrl) {
// TODO
}
$viewValue和$modelValue
在ngModelController中有两个很重要的属性,一个叫做$viewValue,一个叫做$modeValue。
这两者的含义官方的解释如下
$viewValue: Actual string value in the view.
$modelValue: The value in the model, that the control is bound to.
如果你对上面的官方解释有疑惑的话,我这里给出一种我个人的解释。
$viewView就是指令渲染模板所用的值,而$modelView是在控制器中流通的值。很多时候,这两个值可能是不一样的。
比如你在页面上展示了一个日期,它显示的可能是“Oct. 20 2015”这样的字符串,但是呢,这个字符串在控制器中对应的值可能是一个Javascript的Date对象的实例。
再比如,我们的这个time-duration示例中,$viewValue其实指的是指令模板中num和unit组合出来的值,而$modelValue是HelloAppController中test变量对应的值。
$formatters和$parses
除了$viewValue和$modelValue这两个属性之外,还有两个用来处理他们的方法。分别是$parses和$formatters。
前者的是作用是将$viewValue->$modelValue
,后者的作用恰好相反,是将$modelValue->$viewValue
。
time-duration指令与外部控制器以及其内部的运作如下图,
1、在外部控制器中(即这里的HelloApp的controller),我们通过ng-model="test"
将test变量传入指令time-duration中,并建立绑定关系。
2、在指令内部,$modelValue其实就是test值的一份拷贝。
3、我们通过$formatters()
方法将$modelValue转变成$viewValue。
4、然后调用$render()
方法将$viewValue渲染到directive template中。
5、当我们通过某种途径监控到指令模板中的变量发生变化之后,我们调用$setViewValue()
来更新$viewValue。
6、与(4)相对应,我们通过$parsers方法将$viewValue转化成$modelValue。
7、当$modelValue发生变化后,则会去更新HelloApp的UI。
完善指令逻辑
按照上面的流程,我们先来将$modelValue转化成$viewValue,然后在指令模板中进行渲染。
// $formatters接受一个数组
// 数组是一系列方法,用于将modelValue转化成viewValue
ngModelController.$formatters.push(function(modelValue) {
var unit = 'minutes', num = 0, i, unitName;
modelValue = parseInt(modelValue || 0); for (i = multiplierTypes.length-1; i >= 0; i--) {
unitName = multiplierTypes[i]; if (modelValue % multiplierMap[unitName] === 0) {
unit = unitName;
break;
}
} if (modelValue) {
num = modelValue / multiplierMap[unit];
} return {
unit: unit,
num: num
};
});
最后返回的对象就是$viewValue的value。(当然$viewValue还会有其他的一些属性。)
第二步,我们调用$render方法将$viewValue渲染到指令模板中去。
// $render用于将viewValue渲染到指令的模板中
ngModelController.$render = function() {
scope.unit = ngModelCtrl.$viewValue.unit;
scope.num = ngModelCtrl.$viewValue.num;
};
第三步,我们通过$watch来监控指令模板中num和unit变量。当其发生变化时,我们需要更新$viewValue。
scope.$watch('unit + num', function() {
// $setViewValue用于更新viewValue
ngModelController.$setViewValue({
unit: scope.unit,
num: scope.num
});
});
第四步,我们通过$parsers将$viewValue->$modelValue。
// $parsers接受一个数组
// 数组是一系列方法,用于将viewValue转化成modelValue
ngModelController.$parsers.push(function(viewValue) {
var unit = viewValue.unit;
var num = viewValue.num;
var multiplier; multiplier = multiplierMap[unit]; return num * multiplier;
});
总结
好了,到这一个双方的数据绑定逻辑就建立了。不知道大家都学会了吗?希望本文的内容对大家的学习或者工作能带来一定的帮助,如果有疑问大家可以留言交流。
AngularJS指令进阶 -- ngModelController详解的更多相关文章
- AngularJS指令进阶 – ngModelController详解
AngularJS指令进阶 – ngModelController详解 在自定义Angular指令时,其中有一个叫做require的字段,这个字段的作用是用于指令之间的相互交流.举个简单的例子,假如我 ...
- 【python进阶】详解元类及其应用2
前言 在上一篇文章[python进阶]详解元类及其应用1中,我们提到了关于元类的一些前置知识,介绍了类对象,动态创建类,使用type创建类,这一节我们将继续接着上文来讲~~~ 5.使⽤type创建带有 ...
- angularJS中$apply()方法详解
这篇文章主要介绍了angularJS中$apply()方法详解,需要的朋友可以参考下 对于一个在前端属于纯新手的我来说,Javascript都还是一知半解,要想直接上手angular JS,遇到的 ...
- 【python进阶】详解元类及其应用1
前言 元类在python中是很重要的一部分,我将分两次去讲解元类及其应用,此篇为详解元类及其应用第一篇,下面开始今天的说明~~~ 1. 类也是对象 在⼤多数编程语⾔中,类就是⼀组⽤来描述如何⽣成⼀个对 ...
- angularjs $scope.$apply 方法详解
myApp.controller('firstController',function($scope,$interval){ $scope.date = new Date(); setInterval ...
- AngularJS开发指南10:AngularJS依赖注入的详解
依赖注入是一种软件设计模式,用来处理代码的依赖关系. 一般来说有三种方法让函数获得它需要的依赖: 它的依赖是能被创建的,一般用new操作符就行. 能够通过全局变量查找依赖. 依赖能在需要时被导入. 前 ...
- angularjs directive中@ = &使用详解
这段时间在学习angularjs,对directive指令的绑定策略弄了好久才明白,现在做下笔记方便以后查阅,若有错误欢迎指出. 为了使新的指令作用域能访问当前的作用域的一些属性,通常会使用@.=.& ...
- AngularJs directive 'transclude' option 详解
transclude好像不是一个英语单词,有道词典里没有,百度翻译的意思是嵌入. transclude在angularjs的自定义的derective中是比较常见的一个东西,所有有必要要了解它. 我们 ...
- [转]angularjs之ui-grid 使用详解
本文转自:http://blog.csdn.net/qhkabuqiluo/article/details/52237710 最近一段时间在使用angularjs 然后就找到ui-grid 这个比较不 ...
随机推荐
- 51nod1134 最长递增子序列【动态规划】
给出长度为N的数组,找出这个数组的最长递增子序列.(递增子序列是指,子序列的元素是递增的) 例如:5 1 6 8 2 4 5 10,最长递增子序列是1 2 4 5 10. Input 第1行:1个数N ...
- 工厂模式的python实现
#1.什么是工厂模式 #2.工厂模式的分类 ''' 1. 简单工厂模式 2. 工厂方法模式 3. 抽象工厂方法模式 ''' #3.简单工厂模式的python实现 from abc import ABC ...
- Jquery语法基础
Jquery语法基础 一.Jquery一般语法格式为:$(selector).action() l 美元符号定义 jQuery (又称工厂函数) l 选择器(selector)“查询”和“查找” ...
- ExtJs之Ext.view.View
要注意MODEL的定义和实例化的代码,注释掉的是老式的不兼容4.0以上的.而下面的定义才是新推荐的. 我网上可是查的了.是书上的代码老了. <!DOCTYPE html> <html ...
- 洛谷 P1768 天路
P1768 天路 题目描述 “那是一条神奇的天路诶~,把第一个神犇送上天堂~”,XDM先生唱着这首“亲切”的歌曲,一道猥琐题目的灵感在脑中出现了. 和C_SUNSHINE大神商量后,这道猥琐的题目终于 ...
- libev与libuv的区别
参考: http://blog.csdn.net/w616589292/article/details/46475555 libuv 和 libev ,两个名字相当相近的 I/O Library,最近 ...
- hdu 4704 Sum (整数和分解+高速幂+费马小定理降幂)
题意: 给n(1<n<),求(s1+s2+s3+...+sn)mod(1e9+7). 当中si表示n由i个数相加而成的种数,如n=4,则s1=1,s2=3. ...
- 生成字符Banner
生成字符Banner http://patorjk.com/software/taag __ _______/ |_ ____ ____ ____ / ___/\ __\/ _ \ / \ / _ \ ...
- 区间最小值 线段树 (2015年 JXNU_ACS 算法组暑假第一次周赛)
区间最小值 Time Limit : 3000/1000ms (Java/Other) Memory Limit : 65535/32768K (Java/Other) Total Submiss ...
- 当前插入的线段能完整覆盖存在的几条线段 树状数组 HDU 5372 Segment Game
http://acm.hdu.edu.cn/showproblem.php? pid=5372 Segment Game Time Limit: 3000/1500 MS (Java/Others) ...