angularjs中ngModelController学习
来源:互联网 发布:vb.net crc16校验 编辑:程序博客网 时间:2024/06/08 12:11
我们首先看看ngModelController内部的签名是怎么样的?
我们首先看看下面的例子1:
<!doctype html><html ng-app="form-example2"><head><link href="../bootstrap/css/bootstrap.min.css" rel="stylesheet" media="screen"><script src="framework/angular-1.3.0.14/angular.js"></script><script src="FormCustom.js"></script><style type="text/css">div[contentEditable] {cursor: pointer;background-color: #D0D0D0;}</style></head><body><div> <!--这里的contentEditable就是我们自定义的指令--><div contentEditable="true" ng-model="content" title="Click to edit">Some</div><pre>model = {{content}}</pre></div></body></html>我们来看看
angular.module('form-example2', []).directive('contenteditable', function() {//我们定义了一个指令contenteditablereturn {require : 'ngModel',//ng-model指令是一个特殊的指令,他提供更底层的API来处理控制器中的数据,当我们在指令中使用ng-model时候可以访问一个特殊的API,这个API用来处理数据的绑定,验证,CSS更新等不实际操作DOM的事情。ng-model//控制器会随着ngModel被一直注入到指令中,其中包含了一些方法,为了访问ngModelController必须使用require设置。注意:这个指令没有隔离作用域,如果给这个指令设置隔离作用域将导致内部ngModel无法更新外部ngModel//对应值:angularjs会在本地作用域以外查询值link : function(scope, elm, attrs, ctrl) {// view -> model更新Modelelm.bind('keyup', function() {scope.$apply(function() {ctrl.$setViewValue(elm.text());//为了设置作用域中的视图值,需要调用ngModel.$setViewValue函数});});// model -> view更新view(控制器中定义这个方法可以定义视图具体的渲染方式)。这个方法会在$parser流水线完成后被调用。但是因为这个方法会破坏ng标准工作方式,因此要谨慎使用ctrl.$render = function() {elm.html(ctrl.$viewValue);//$viewValue属性保存着更新视图所需要的实际的字符串。$modelValue和$viewValue可能是相同的,取决于$parser流水线是否对其进行了操作};// load init value from DOM//从DOM中获取初始的值ctrl.$setViewValue(elm.html());}};});其中link函数第四个参数就是我们的ngModelController,其中包含了$setViewValue,$viewValue,$render等方法。其中$viewValue保存着更新视图所需要的实际的字符串;而$render方法可以定义视图具体渲染的方式;$setViewValue接受一个参数value,其代表着我们想要赋值给ngModel实例的实际值,这个方法会更新控制器上本地的$viewValue,然后把值传递给每一个$parser函数。当值被解析,且$parser流水线中所有的函数都被调用完成后就会被负值为$modelValue属性,并且传递给指令中ng-model属性提供的表达式。最后,所有的步骤完成后,$viewChangeListeners中所有的监听器就会被调用。注意:单独调用$setViewValue不会唤起一个新的digest循环,因此如果想要更新指令,需要在设置$viewValue后手动调用digest!
注意:$setViewValue方法适合于在自定义指令中监听自定义事件,如具有回调函数的jQuery插件,我们会希望在回调时候设置$viewValue进行digest循环!
我们再来学习一下下面的例子2:
<!doctype html><html ng-app="form-example1"><head><meta http-equiv="content-type" content="text/html; charset=utf-8" /><link rel="stylesheet" href="css/bootstrap-3.0.0/css/bootstrap.css" media="screen"><script src="framework/angular-1.3.0.14/angular.js"></script><script src="FormValidation.js"></script></head><body><div><form name="myForm" class="css-form" novalidate><!--下面是整数--><div>整数(0-10):<input type="number" ng-model="size" name="size" min="0" max="10" integer/>{{size}}<br/><span ng-show="myForm.size.$error.integer">不是合法的整数!</span><span ng-show="myForm.size.$error.min || myForm.size.$error.max">数值必须位于0到10之间!</span></div><!--下面必须是浮点数--><div>浮点数:<input type="text" ng-model="length" name="length" smart-float />{{length}}<br/><span ng-show="myForm.length.$error.float">不是合法的浮点数!</span></div><!--下面是远程校验--><div>远程校验:<input type="text" ng-model="remote" name="remote" remote-validation />{{remote}}<br/><span ng-show="myForm.remote.$error.remote">非法数据!</span></div></form></div></body></html>这个例子我们可以学习在form中如何实现验证的,如上面例子的myForm.length.$error.float,myForm.remote.$error.remote等。我们再来看看下面的自定义指令部分:
var app = angular.module('form-example1', []);var INTEGER_REGEXP = /^\-?\d*$/;//整数正则app.directive('integer', function() {return {require : 'ngModel',link : function(scope, elm, attrs, ctrl) {//为parsers这个数组头部添加一个函数ctrl.$parsers.unshift(function(viewValue) {if (INTEGER_REGEXP.test(viewValue)) {//调用ngModelController的$setValidity方法ctrl.$setValidity('integer', true);return viewValue;//如果正常就返回viewValue,否则就返回undefined表示不合法} else {ctrl.$setValidity('integer', false);return undefined;}});}};});var FLOAT_REGEXP = /^\-?\d+((\.|\,)\d+)?$/;app.directive('smartFloat', function() {return {require : 'ngModel',link : function(scope, elm, attrs, ctrl) {ctrl.$parsers.unshift(function(viewValue) {if (FLOAT_REGEXP.test(viewValue)) {ctrl.$setValidity('float', true);//如果满足替换掉逗号返回return parseFloat(viewValue.replace(',','.'));} else {ctrl.$setValidity('float', false);return undefined;}});}};});app.directive('remoteValidation', function($http) {return {require : 'ngModel',link : function(scope, elm, attrs, ctrl) {elm.bind('keyup', function() { $http({method: 'GET', url: 'FormValidation.jsp'}). success(function(data, status, headers, config) { if(parseInt(data)==0){ ctrl.$setValidity('remote',true); }else{ ctrl.$setValidity('remote',false); } }). error(function(data, status, headers, config) { ctrl.$setValidity('remote', false); });});}};});我们可以看到这里使用了ngModelController的$parsers集合(ngModel从DOM中读取的值会被传入到$parsers函数,并依次调用其中的解析器,这是为了对值进行处理和修饰),同时把验证函数放入到这个集合中,这个验证函数唯一的参数就是指令所有的元素的输入内容!同时我们可以学习到这里使用了$setValidity方法,如下面这种方式:
ctrl.$setValidity('integer', true);我们看看在页面中是如何判断是否显示相应的提示信息的:
<span ng-show="myForm.size.$error.integer">不是合法的整数!</span>
很显然每一个form中的元素都具有一个$error对象,保存着没有通过验证的验证器名称以及对应的错误信息。从顶部图片中,我们可以看到$viewChangeListeners保存的是由函数组成的数组,通过$viewChangeListeners可以在无需使用$watch的情况下实现类似的行为。
如果要深入理解ngModleController,建议阅读在Angular指令中使用NgModelController做数据绑定一文,其仔细论述了$modelValue,$viewValue,$parsers,$formatters等知识。详细内容见下图:
1.在外部控制器中(即这里的HelloApp的controller),我们通过ng-model="test"将test变量传入指令time-duration中,并建立绑定关系。
<time-duration ng-model="test"></time-duration>2.在指令内部,$modelValue其实就是test值的一份拷贝。
angular.module('HelloApp').controller('HelloController', function($scope) { $scope.test = 1;});3.我们通过$formatters()方法将$modelValue转变成$viewValue。
ngModelCtrl.$formatters.push(function(modelValue) { var unit = 'minutes', num = 0, i, unitName; modelValue = parseInt(modelValue || 0); // Figure out the largest unit of time the model value // fits into. For example, 3600 is 1 hour, but 1800 is 30 minutes. //从天->小时->分钟等进行转换,获取modelview适合的最大的时间单位 for (i = multiplierTypes.length-1; i >= 0; i--) { unitName = multiplierTypes[i]; //获取单位名称['seconds', 'minutes', 'hours', 'days'] if (modelValue % multiplierMap[unitName] === 0) { unit = unitName; break; } } //获取如多少天,多少小时,多少分钟等 if (modelValue) { num = modelValue / multiplierMap[unit] } //返回一个对象,有最大的时间单位名称+最大时间单位数量 return { unit: unit, num: num }; });4.然后调用$render()方法将$viewValue渲染到directive template中。
// $render用于将viewValue渲染到指令的模板中 ngModelCtrl.$render = function() { scope.unit = ngModelCtrl.$viewValue.unit; scope.num = ngModelCtrl.$viewValue.num; }; }5.当我们通过某种途径监控到指令模板中的变量发生变化之后,我们调用$setViewValue()来更新$viewValue。(当我们修改了数字或者单位的时候就会调用$setViewValue)
//$watch是一个scope函数,用于监听模型变化,当你的模型部分发生变化时它会通知你。 //$watch(watchExpression, listener, objectEquality); //具体可以参考:http://yuankeqiang.lofter.com/post/8de51_1454f93 scope.$watch('unit + num', function() { // 如果当数据模型unit和num发生变化后,通过$setViewValue用于更新viewValue ngModelCtrl.$setViewValue({ unit: scope.unit, num: scope.num }); });6.与(4)相对应,我们通过$parsers方法将$viewValue转化成$modelValue。
// $parsers接受一个数组,数组是一系列方法,用于将viewValue转化成modelValue ngModelCtrl.$parsers.push(function(viewValue) { var unit = viewValue.unit, num = viewValue.num, multiplier; multiplier = multiplierMap[unit]; //获取相乘后的结果数 return num * multiplier; });7.当$modelValue发生变化后,则会去更新HelloApp的UI。所有整个内容如下:
angular.module('HelloApp', []);//这里是模块名称function TimeDurationDirective() { var tpl = "\ <div class='time-duration'> \ <input type='text' 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>"; return { restrict: 'E', template: tpl, require: 'ngModel',//timeDuration指令依赖于ng-model指令 replace: true,//实现替换time-duration scope: {}, // 这个指令有一个独立的作用域对象,也就是有一个独立的scope对象 link: function(scope, iElement, iAttrs, ngModelCtrl) { var multiplierMap = {seconds: 1, minutes: 60, hours: 3600, days: 86400}; var multiplierTypes = ['seconds', 'minutes', 'hours', 'days']// $formatters接受一个数组,数组是一系列方法,用于将modelValue转化成viewValue ngModelCtrl.$formatters.push(function(modelValue) { var unit = 'minutes', num = 0, i, unitName; modelValue = parseInt(modelValue || 0); // Figure out the largest unit of time the model value // fits into. For example, 3600 is 1 hour, but 1800 is 30 minutes. //从天->小时->分钟等进行转换,获取modelview适合的最大的时间单位 for (i = multiplierTypes.length-1; i >= 0; i--) { unitName = multiplierTypes[i]; //获取单位名称['seconds', 'minutes', 'hours', 'days'] if (modelValue % multiplierMap[unitName] === 0) { unit = unitName; break; } } //获取如多少天,多少小时,多少分钟等 if (modelValue) { num = modelValue / multiplierMap[unit] } //返回一个对象,有最大的时间单位名称+最大时间单位数量 return { unit: unit, num: num }; }); // $parsers接受一个数组,数组是一系列方法,用于将viewValue转化成modelValue ngModelCtrl.$parsers.push(function(viewValue) { var unit = viewValue.unit, num = viewValue.num, multiplier; multiplier = multiplierMap[unit]; //获取相乘后的结果数 return num * multiplier; }); //$watch是一个scope函数,用于监听模型变化,当你的模型部分发生变化时它会通知你。 //$watch(watchExpression, listener, objectEquality); //具体可以参考:http://yuankeqiang.lofter.com/post/8de51_1454f93 scope.$watch('unit + num', function() { // 如果当数据模型unit和num发生变化后,通过$setViewValue用于更新viewValue ngModelCtrl.$setViewValue({ unit: scope.unit, num: scope.num }); }); // $render用于将viewValue渲染到指令的模板中 ngModelCtrl.$render = function() { scope.unit = ngModelCtrl.$viewValue.unit; scope.num = ngModelCtrl.$viewValue.num; }; } };};//这里定义了一个指令timeDurationangular.module('HelloApp').directive('timeDuration', TimeDurationDirective);//这里定义了一个控制器HelloControllerangular.module('HelloApp').controller('HelloController', function($scope) { $scope.test = 1;});注意:这个例子详细的论述了ngModelController中$viewValue,$modelValue,$parsers,$formatters等内容
0 0
- angularjs中ngModelController学习
- angularjs中ngModelController学习
- AngularJS指令进阶 -- ngModelController详解
- AngularJS指令进阶 – ngModelController详解
- AngularJS指令进阶 – ngModelController详解
- AngularJS指令进阶 – ngModelController详解
- AngularJS指令进阶 – ngModelController详解
- 在自定义指令中使用NgModelController(Using NgModelController with Custom Directives)
- 在Angular指令中使用NgModelController做数据绑定
- ngModelController详解
- 实践中学习AngularJS中的表单
- AngularJS学习
- AngularJS 学习
- angularJS学习
- AngularJs学习
- angularjs学习
- angularjs 学习
- angularJS学习
- Activity的生命周期以及启动模式
- Javaweb学习之JSP基础
- Oracle性能优化-读懂执行计划
- 高级Swing界面JTree编程
- 禁止解析 php
- angularjs中ngModelController学习
- php给客户端写接口记录
- ubuntu 中怎么截图?ubuntu 12.10 截图工具及快捷键设置
- 数据库中视图的作用
- 7. Reverse Integer 反转int
- 用sqoop将oracle数据导入Hbase
- CSS3 选择器 文字阴影
- PHP里还有这些好用的技巧
- 将文件转为byte[],通过ByteArrayOutputStream 实现