angular中的$scope是页面(view)和数据(model)之间的桥梁,它链接了页面元素和model,也是angular双向绑定机制的核心。

而ngModel是angular用来处理表单(form)的最重要的指令,它链接了页面表单中的可交互元素和位于$scope之上的model,它会自动把ngModel所指向的model值渲染到form表单的可交互元素上,同时也会根据用户在form表单的输入或交互来更新此model值。

在源码中,model值的格式化、解析、验证都是由ngModel指令所对应的控制器ngModelController来实现的。

在笔者所维护的国内ng群中,经常被问到一个问题:

    为什么我的ng-model=“xxx”值不能在页面显示了呢?

对于ngModel的这类问题主要分为两类:

  • model值不满足表单验证条件,所以angular不会渲染它
  • 由于JavaScript特殊的原型链继承机制,对$scope中属性的赋值并不能更新到父$scope

在本节中,我们将会详细分析此类问题,借此深入剖析ngModel的工作原理。

验证引起的model值不显示

我们先来看一个修改商品数量的例子,要求为必须输入1-100的个数;

下面是对应的html代码:

<body class="container"><div ng-controller="DemoCtrl as demo"><div ng-form="form" class="form-horizontal"><div class="form-group" ng-class="{'has-error': form.amount.$invalid }"><label for="amount">Amount</label><!-- 这个input将工作不正常 --><input id="amount" name="amount" type="number" ng-model="demo.amount" class="form-control" placeholder="1 - 100" min="1" max="100"/></div></div></div>
</body>

javascript代码:

angular.module("com.ngbook.demo", []).controller("DemoCtrl", [function(){var vm = this;vm.amount = 0;return vm;
}]);

在代码中我们已经为ngModel变量amount赋值了整数“0”,可是界面显示效果仍然显示”1 – 100”的placeholder(如下图)。

下面是关于angular number组件ngModel转换函数代码:

var NUMBER_REGEXP = /^\s*(\-|\+)?(\d+|(\d*(\.\d*)))\s*$/;function numberInputType(scope, element, attr, ctrl, $sniffer, $browser) {textInputType(scope, element, attr, ctrl, $sniffer, $browser);ctrl.$parsers.push(function(value) {var empty = ctrl.$isEmpty(value);if (empty || NUMBER_REGEXP.test(value)) {ctrl.$setValidity('number', true);return value === '' ? null : (empty ? value : parseFloat(value));} else {ctrl.$setValidity('number', false);return undefined;}});addNativeHtml5Validators(ctrl, 'number', numberBadFlags, null, ctrl.$$validityState);ctrl.$formatters.push(function(value) {return ctrl.$isEmpty(value) ? '' : '' + value;});if (attr.min) {var minValidator = function(value) {var min = parseFloat(attr.min);return validate(ctrl, 'min', ctrl.$isEmpty(value) || value >= min, value);};ctrl.$parsers.push(minValidator);ctrl.$formatters.push(minValidator);}if (attr.max) {var maxValidator = function(value) {var max = parseFloat(attr.max);return validate(ctrl, 'max', ctrl.$isEmpty(value) || value <= max, value);};ctrl.$parsers.push(maxValidator);ctrl.$formatters.push(maxValidator);}ctrl.$formatters.push(function(value) {return validate(ctrl, 'number', ctrl.$isEmpty(value) || isNumber(value), value);});
}

ngModel作为angular双向绑定中的重要组成部分,负责view控件交互数据到$scope上model的同步。当然这里存在一些差异,view上的显示和输入都是字符串类型,而在model上的数据则是有特定类型的,如常用的int、float、Date、Array、Object等。ngModel为了实现数据到model的类型转换,在ngModelController中提供了两个管道数组$formatters和$parsers,它们分别是将model的数据转换为view交互控件显示的值和将交互控件得到的view值转换为model数据,它们都是一个数组对象,在ngModel启动数据转换时,会以UNIX管道式传递执行这一些列的转换。我们也可以手动的添加$formatters和$parsers的转换函数(unshift、push),当然在这里也是做数据验证的最佳时机,能够转换意味应该是合法的数据。

在number组件代码中,我们清晰看见:依次添加了对数字验证转换、最小值合法性验证、最大值合法验证。首先会启动$parsers转换,如果在转换过程中出现不合法验证则会设置ngModelController.$setValidity验证错误,则返回undefined。对于model数据到交互控件显示,同样也会经过$formatters转换管道,对于没有通过验证的逻辑,同样也会ngModelController.$setValidity设置验证错误,返回undefined,因此这不合法的model数据不会显示在交互控件上。

原型链继承问题

JavaScript中每个对象都会链接到一个原型对象,并且他可以从中继承属性。即使通过字面量创建的对象也会链接到Object.prototype,它是JavaScript中的标配对象。JavaScript的原型链继承相对于其他语言常见的继承,是一种另类的继承,它是实施于对象上的动态继承方式,而非常见的实施与类型class之上的静态继承体系。JavaScript的这种继承方式很灵活,一个对象可以被多个对象继承,而且他们共享同一实例对象,但理解起来显得格外复杂,从JavaScript原型和原型链可以看出它的复杂性。在Javascript中,每个函数都有一个原型属性prototype指向自身的原型,而由这个函数创建的对象也有一个proto属性指向这个原型,而函数的原型是一个对象,所以这个对象也会有一个proto指向自己的原型,这样逐层深入直到Object对象的原型,这样就形成了原型链。下面的是JavaScript原型继承基础原型和原型链展示图。

函数是由Function函数创建的对象,因此函数也有一个proto属性指向Function函数的原型。需要注意的是,真正形成原型链的是每个对象的proto属性,而不是函数的prototype属性。更多的内容关于原型和原型链的知识,请参考《Javascript模式》这本书。

JavaScript的原型链连接只在属性检索的时候才会启用,如果我们尝试去获取对象的某个属性值,但该对象没有此属性名,则JavaScript会试着从原型对象中获取该属性值。如果那个对象也没有该属性名,那么在继续从它的原型中寻找,依次类推,直到Object.prototype,如果仍然没有找到该属性值,则返回结果为undefined。不幸的是,这种原型链连接检索,只会在属性检索的的时候启用,并不会在更新属性值时启用,因此当我们对于基础类型(非引用对象上的属性,换句通俗的话来说,就是不会出现“.”运算符)的属性更新的时候,它并不能更新父对象的属性,替代方式是在自身对象上创建了该属性。这也是angular中对于基础类型的属性,不能在子controller中被修改的原因,导致在子controller中ngModel的更新并不会反应在父controller上。

下边是关于该问题的一个简化例子:

HTML:

<div ng-controller="ParentCtrl"><div class="form-group"><h4>Parent Controller:</h4><pre></pre><input type="text" ng-model="greet" class="form-control" /></div><div ng-controller="ChildCtrl"><div class="form-group"><h4>Child controller:</h4><pre></pre><input type="text" ng-model="greet" class="form-control" /></div></div>
</div>

JavaScript:

angular.module("com.ngbook.demo", []).controller("ParentCtrl", ["$scope", function($scope) {$scope.greet = "hello angular!";}]).controller("ChildCtrl", angular.noop);

从初始化显示效果中,我们能看出子$scope之继承了来自父$scope的greet属性,都显示为”hello angular!“。如果我们尝试利用父controller提供了input控件改变父$scope的greet属性,你也能看见子controller区域的显示也会被及时更新。对于ngController默认会使用原型链继承其父对象的属性,所有的$scope的根$scope或称祖$scope是来自ngApp节点创建的$rootScope,换句话说,$rootScope是万物之源,所有的$scope都直接或者间接继承至它。

当我们尝试去改变输入框的greet属性的时,则发生了下面的情况:子controller区域发生了更新,父controller区域却无法更新。因为上面所说的JavaScript的原型链检索并不对更新启用,对于基础类型JavaScript在自身对象(这里是子$scope)上创建了一个同名的变量。你也想可以从下面angular调试插件batarang截图中看出来。一旦利用子controller的input控件修改了greet属性,再次之后我再次尝试修改父controller区域的greet属性,子controller区别不会在像初始化时候那样及时同步了,它们之间完全独立了,各自拥有了自己的greet属性。

batarang插件截图

经过上面的例子分析,相信作为读者的你已经能够理解这类由于继承链引用问题导致的ngModel不能更新问题了,请记住:这是JavaScript原型继承的issue,并不是angular的issue。

那么我们在子controller中如何更新父controller的属性值呢?这个问题已经很简单了,issue的问题在于没有启用原型链的检索,那么如果我们将ngModel的属性变为引用对象,换句话说:在ngModel的属性值中加了“.”,那么在JavaScript的原型链检索就会启动了。

HTML:

<div ng-controller="ParentCtrl"><div class="form-group"><h4>Parent Controller:</h4><pre></pre><input type="text" ng-model="vm.greet" class="form-control" /></div><div ng-controller="ChildCtrl"><div class="form-group"><h4>Child controller:</h4><pre></pre><input type="text" ng-model="vm.greet" class="form-control" /></div></div>
</div>

JavaScript:

angular.module("com.ngbook.demo", []).controller("ParentCtrl", ["$scope", function($scope) {$scope.vm = {greet: "hello angular!"};}]).controller("ChildCtrl", angular.noop);

jsbin demo: http://jsbin.com/metufi/1/edit?html,js,output

这里在ngModel属性值多引入了“vm”变量,这个时候,不管我们尝试修改greet值,整个页面都会得到相应的同步。关于这个问题,作者更推荐使用angular 1.2后的controller as vm的方式解决,更多的信息请阅读《使用controller as vm方式.md》一节。

ngModel 值不更新/显示相关推荐

  1. python wx模块下choice列表框值怎么更新_wx python

    一.静态文本控件 wx.StaticText(parent, id, label, pos=wx.DefaultPosition,    size=wx.DefaultSize, style=0, n ...

  2. 更新显示当前歌曲的名称 winform 0130

    更新显示当前歌曲的名称 winform 0130 利用label控件显示内容 设置属性为公开的 在播放的时候更新一下label控件的文本值即可

  3. asp.net GridView手写事件,包括取主键、取值、更新、选择、删除

    图片是本网站后台的友情链接管理页面: 前两者的代码比较固定,一般都是:  代码如下 复制代码 protected void gvFriendLink_RowEditing(object sender, ...

  4. Android Framework 电源子系统(04)核心方法updatePowerStateLocked分析-2 循环处理  更新显示设备状态

    该系列文章总纲链接:专题分纲目录 Android Framework 电源子系统 本章关键点总结 & 说明: 本章节主要关注➕ updatePowerStateLocked 方法中 循环处理 ...

  5. Java黑皮书课后题第3章:*3.1(代数:解一元二次方程)可以使用下面的公式求一元二次方程ax2+bx+c=0,编写程序提示用户输入a b c的值,并显示基于判断式的结果

    *3.1(代数:解一元二次方程)可以使用下面的公式求一元二次方程ax2+bx+c=0,编写程序提示用户输入a b c的值,并显示基于判断式的结果 题目 题目描述 运行示例 破题 代码 题目 题目描述 ...

  6. 以A表中的值快速更新B表中记录的方法

    1.问题描述 有两张表,A表记录了某些实体的新属性,B表记录了每个实体的旧属性,现在打算用A中的属性值去更新B中相同实体的旧属性,如下图所示: 类似这样的需求,怎样做比较高效呢? 2.制作模拟数据   ...

  7. php中多选提交如何获取,php中checkbox值获取,显示,多选值获取

    php教程中checkbox值获取,显示,多选值获取 最简单checkbox获取值代码 checkbox demo checkbox demo demonstrates checkboxes =&qu ...

  8. 【HTML】关于height值设置和显示不同的原因以及font-size对象为中文和英文的区别

    关于height值设置和显示不同的原因 height: 100px; CSS中把高度设置为100px,但是实际测量发现高度为125px 这个是因为:笔记本默认显示大小为125%,所以100的125%就 ...

  9. Adobe Acrobat DC卸载更新出现问题,重装更新显示找不到源而反复出现window installer 安装失败(开机弹窗,升级不了)解决方法,亲测有效!!!

    @ Adobe Acrobat DC卸载更新出现问题,重装更新显示找不到源而反复出现window installer 安装失败(开机弹窗,升级不了)解决方法,亲测有效!!! 某天电脑使用Adobe退出 ...

最新文章

  1. AI一分钟 | 娃哈哈要造智能汽车?世界顶级机器学习科学家黄恒加盟京东
  2. 设计模式--策略(Strategy)模式
  3. matlab 二值化_撸了一份 ostu二值化,需要的小伙伴请拿走
  4. python scapy sniffer停止抓包_如果没有收到数据包,如何告诉scapy sniff()停止?
  5. Nacos命名空间配置_每个人用各自自己的命名空间---SpringCloud Alibaba_若依微服务框架改造---工作笔记001
  6. java+@api_java 常用API
  7. 10 个最酷的 Linux 单行命令
  8. 周学习进度---05
  9. 编程军规 —— Java 篇
  10. 道士下山与黎万强的摄影展
  11. 首都师范 博弈论 5 2 1帕累托最优
  12. python 正则表达式 r_python 正则表达式
  13. NS2:添加一个新的流量发生器(poisson分布)
  14. 麻省理工公开课人工智能笔记六
  15. c语言如何将变量设为0,C语言中的全局变量如果没有指定初值,则其初值自动设置为0。...
  16. 百瑞BARROT BR2262e蓝牙模块的使用
  17. 线性回归使用的基本条件
  18. LSM树——Log-Structured Merge-Tree数据结构、LSM树设计思想、LSM的数据写入操作、LSM的数据查询操作
  19. R统计-微生物群落结构差异分析及结果解读
  20. 均值滤波计算_均值滤波器

热门文章

  1. 97页PPT,读懂自动驾驶全产业链发展!
  2. 麦肯锡发布调研,揭开“那些引入人工智能的企业都怎么了 ”
  3. “万维网之父”发文阐述其下一个网络时代:将数据与应用分离,互联网去中心化正在路上...
  4. 为什么 AI 芯片时代必然到来——从TPU开始的几十倍性能之旅
  5. 七大科技巨头的最新人工智能布局
  6. 人民日报三问人工智能,给法律制度带来哪些挑战?
  7. 中科院自动化所介绍深度强化学习进展:从AlphaGo到AlphaGo Zero
  8. 道歉无用!被 Linux “拉黑”的明尼苏达大学还在努力重获信任
  9. X-Forwarded-For 会少记录一次代理服务器的IP
  10. python3—廖雪峰之练习(三)