一. html与Controller中的双向数据绑定

html-Controller的双向数据绑定,在开发中非常常见,也是Angularjs1.x的宣传点之一,使用中并没有太多问题。

1.1数据从html流向controller

也就是从视图层流向模型层,原生html中需要使用表单元素(例如input标签)来收集用户输入信息,Angularjs中通过在表单元素上使用ng-model标签,当用户输入信息时,同步将用户输入的信息赋值给controller中的变量:

<body ng-app="myApp">
<div id="main" ng-controller="myCtrl">
<p>改变输出值:</p>
<input type="text" ng-model="testInfo.content" ng-change="showInput()">
</div>
<script src="./angular.min.js"></script>
<script>
angular.module('myApp',[])
.controller('myCtrl',['$scope',function($scope){
$scope.showInput = function() {
console.log($scope.testInfo.content);
}
}]);
</script>
</body>

在页面上输入1234567即可看到,每次在页面输入数字后,控制台输出的$scope,testInfo.content的值都和页面保持一致:

1.2 数据从controller流向html

也就是从模型层流向数据层,当controller中的数据模型变量发生变化后,Angularjs又会根据数据模型的值去改变ng-model指令绑定的表单元素的值,使用ng-bind指令也可以被动获得来自controller的数据流。

我们编写如下demo进行测试:

<body ng-app="myApp">
<div id="main" ng-controller="myCtrl">
<button ng-click="add()">+1</button>
<p>改变输出值:</p>
<input type="text" ng-model="testInfo.content">
<p>使用ng-bind绑定的标签:</p>
<p ng-bind="testInfo.content"></p>
</div>
<script src="./angular.min.js"></script>
<script>
angular.module('myApp', [])
.controller('myCtrl', ['$scope', function($scope) {
//初始化
$scope.testInfo = {
content: 0
} $scope.add = function () {
$scope.testInfo.content += 1;
console.log($scope.testInfo.content);
}
}]);
</script>
</body>

demo中,每次点击+1按钮,$scope.testInfo.content的值会增加1,我们可以看到页面上的结果:

1.3 你丫倒是刷视图啊

来看看第一个活见鬼的例子,demo跟上面很类似,只是将鼠标点击触发的方式改成了定时器自动触发

<body ng-app="myApp">
<div id="main" ng-controller="myCtrl">
<button ng-click="add()">+1</button>
<p>改变输出值:</p>
<input type="text" ng-model="testInfo.content">
<p>使用ng-bind绑定的标签:</p>
<p ng-bind="testInfo.content"></p>
</div>
<script src="./angular.min.js"></script>
<script>
angular.module('myApp', [])
.controller('myCtrl', ['$scope', function($scope) {
//初始化
$scope.testInfo = {
content: 0
} //定时自增
setInterval(function () {
$scope.testInfo.content += 1;
console.log('$scope.testInfo.content的值现在是:',$scope.testInfo.content);
},1000)
}]);
</script>
</body>

你会活见鬼地发现,数据模型一直在变,但是页面却没有刷新:

这里就是 Angularjs1.X双向数据绑定中的第一个坑 ,你会发现$scope上绑定的数据模型html中显示的内容有时候并不是实时关联的。这其实和Angularjs1.X的执行机制有关系。

如果我们自己来考虑,javascript中有一个变量的值发生了变化,现在要将这个值同步到html页面上,需要怎么做呢?我们需要获取到这个DOM元素,然后改变它的innerHTML属性,如果是表单元素就修改value。其实Angularjs也是这样做的,只不过使用了自己的封装的方法——$apply()。那么此处的问题其实就在于,在setInterval的回调函数中去修改数据模型的值时,没有触发$apply()方法来更新视图,而通过调用Angularjs封装的ng-*方法(例如ng-click点击方法)来修改视图模型时,会自动触发$apply()方法,视图也就同步刷新了。

  • 解决方案1

    使用Angularjs封装过的$interval服务来实现定时任务,感兴趣的读者可以自己看一下Angularjs源码中$intervalProvider的部分,就会发现在方法最后的地方调用了$rootScope.$apply()

  • 解决方案2

    如果依然使用javascript原生的定时方法,那么则需要在修改完视图的数据模型后,手动调用$scope.$apply()方法来将数据模型的变动同步到html页面中。

二. Controller与Directive中的双向数据绑定

除了controller与html中的双向绑定,Angularjs中还有另一个双向数据绑定,那就是controller与directive之间的绑定。绑定的形式有很多种,我们先来看一下最常见的双向绑定

2.1 directive中的双向数据绑定

在设定自定义指令的scope参数时,将属性的值设置为=就可以实现双向数据绑定,这里API的解释是:

父级controller中的指定变量会与自定义指令link函数中的变量相互影响

下面的实例中,我们将看看controller中的数据模型$scope.testInfo.content的值与自定义指令中scope.pagination如何相互影响,是否如定义所说这里的绑定真的是双向的。示例界面如下(demo源码请见附件demo.html文件):

  • 每次点击+1按钮,Scope.testInfo.content的值都会增加1
  • 每次点击show $scope.testInfo,控制台都会打印出$scope.testInfo的值
  • 每次点击标签上的数字,则会打印出自定义指令中scope.pagination的值,并将该值进行自增

接下来的测试操作,我们将按照如下的流程进行:

  1. 点击5次+1按钮,再点击5次数字标签
  2. 点击show $scope.testInfo按钮

2.2 你丫怎么又不刷新了

随着上一节的操作步骤,我们一起来见证双向数据绑定中又一次闹鬼事件:

  • 点击5次+1按钮,再点击5次数字标签

    结果为:

我们看到,第一次点击数字标签时,控制台打出了link函数中scope.pagination的值为5,这说明$scope.testInfo.content的值被传递给了自定义指令中的scope.pagination,也就是说数据从controller流向了directive。而当我们再点击4次数字标签(一共点了5次)后,从控制台可以看出,scope.pagination的值已经成为10,而页面上使用ng-bind指令获取到的结果却依旧是5。也就是说,数据从没有从directive流向controller。是不是有一种被骗的感觉?别着急,接着看。

  • 点击show $scope.testInfo按钮

    结果为:

当我们点击show $scope.testInfo时,控制台打印出了$scope.testInfo.content的值为5,这下证据坐实了,明明说好的双向数据绑定,然而当自定义指令中的scope.pagination改变时,$scope.testInfo.content并没有跟着一起改变。But!!!!我们会发现,这个show $scope.testInfo点下去以后,页面上通过ng-bind绑定的值却变成了10。也就是说,数据又从directive流回了controller

官方建议使用$watch方法来追踪scope中的变量,而当我们这样做时,会发现$watch函数仅能追踪到那些通过修改controller中的数据模型而影响link函数中变量的行为并更新视图

这里就是 Angularjs1.X双向数据绑定中的第二个坑,controller和directive中所谓的双向数据绑定,并不能追踪指定变量的所有变化,而且不是同步完成的。

其实这里的问题仍然和Angularjs的运行机制有关,解决方案如下:

  • 解决方案1

    使用自定义指令的templateUrl属性替换当前指令的模板,使用ng-click指令来绑定一个点击响应函数,在响应函数中改变scope.piganation的值。

  • 解决方案2

    在手动绑定的监听回调中,修改自定义指令作用域内的变量后,使用scope.$emit( )方法通知其父级controller,并在controller中使用$scope.$on( )方法监听同名事件,并修改对应的数据模型的值。

  • 解决方案3

    每当改变自定义指令中的变量值后,调用scope.$apply()方法,将directive中的变量值同步至controller的数据模型以及页面。

三.原理和实战总结

3.1 Angularjs中双向数据绑定的基本原理

Angularjs中的双向数据绑定,是通过一种叫做**"脏循环检查(dirty-checking)"*的机制实现的。

其基本过程是这样的,每当我们使用ng-modelng-bind指令将数据模型中的某个变量值和html页面上某个标签的内容联系起来时,Angular就会把这些变量放进一个WatchCollection的集合中,并自动帮我们来监控这些变量。每当WatchCollection中有变量出现变动时,Angular就会遍历WatchCollection来查看是否有其他监控中的变量也被影响,每当有一个变量被影响,Angular都会在遍历后再进行一次遍历,直到某一次遍历后WatchCollection中的变量都没有变化,则Angular会认为当前的改动已经稳定了,然后才会将数据模型的变化同步到DOM元素上去,也就实现了数据绑定。

我们可以把WatchCollection理解为当前页面的一种抽象,其中包含着页面上所有有可能发生变化的部分。

3.2 双向数据绑定的实践经验

想要在Angularjs项目中更加稳定地使用双向数据绑定,笔者的建议是:

Angularjs项目中,尽可能地使用Angular告诉你的方式去编写所希望实现的功能。

我们可以回顾一下上面在使用双向数据绑定发生异常时的场景:

  • 使用了原生的定时器(Angular中你应该使用$interval,$timeout服务)
  • 用类原生方法(bind)为元素添加事件监听器,并在回调函数中修改了变量的值(Angular中,你应该使用ng-click来实现点击事件的监听)
  • ...

你会发现,每当自己没有按照Angular的方式去编写代码,或者没有按照一个模块设计的初衷去使用它时,就无法确切地得到期望的结果。这是很容易理解的,如果你没有按照Angular要求的方式书写代码,凭什么期望它对你的代码做出100%正确的回应呢?至于上述两种数据绑定中出现问题的解决方案,上文已经有所提及,此处不再赘述。

许多人都听说过"尽量不要在controller中操作DOM"这句话,实际上它并不意味着你在controller中操作DOM会导致程序报错,而是在说如果你同时使用jQueryAngular两套系统来管理自己的代码,但又没有按照官方指定的方式来规避它们之间的冲突,那代码很可能会变得不稳定。想想当年腾讯电脑管家360安全卫士将你的电脑卡死的场景,你就明白这样做的结果了。

四. 小结——所谓高手

笔者曾经看过这样一段话,觉得深有感触:

所谓高手,是指那些熟知套路创意无穷的人。而高手之间的较量,归根结底都是基本功的比拼。

愿有朝一日,你也能成为高手。

Angularjs1.X进阶笔记(1)—两种不同的双向数据绑定的更多相关文章

  1. iOS开发笔记-两种单例模式的写法

    iOS开发笔记-两种单例模式的写法   单例模式是开发中最常用的写法之一,iOS的单例模式有两种官方写法,如下: 不使用GCD #import "ServiceManager.h" ...

  2. 网站开发进阶(六)JSP两种声明变量的区别

    JSP两种声明变量的区别 在JSP中用两种声明变量的方法,一种是在<%! %>内,一种是在<% %>内.他们之间有什么区别呢?我们直接看一个JSP文件来理解. 代码如下: &l ...

  3. redis笔记之两种持久化备份方式(RDB & AOF)

    Redis支持的两种持久化备份方式(RDB & AOF) redis支持两种持久化方式,一种是RDB,一种是AOF. RDB是根据指定的规则定时将内存中的数据备份到硬盘上,AOF是在每次执行命 ...

  4. java笔记线程两种方式模拟电影院卖票

    public class SellTicketDemo { public static void main(String[] args) { // 创建三个线程对象 SellTicket st1 = ...

  5. angularjs1.X进阶笔记(3)——如何重构controller

    目录 一. 结构拆分 二.基本代码优化 本篇是内部培训交流会的摘要总结. 培训PPT和示例代码已托管至我的github仓库: https://github.com/dashnowords/blogs/ ...

  6. LIS学习笔记(两种算法)O(n^2) 和 O(nlogn)

    2017-09-02 10:34:21 writer:pprp 最长上升子序列,具体分析看代码:O(n^2)的做法,dp的思想 分析:每次读一个进行扫描,如果当前读入的这个要比之前的大, 说明有可能加 ...

  7. Android进阶笔记13:RoboBinding(实现了数据绑定 Presentation Model(MVVM) 模式的Android开源框架)

    1.RoboBinding RoboBinding是一个实现了数据绑定 Presentation Model(MVVM) 模式的Android开源框架.从简单的角度看,他移除了如addXXListen ...

  8. Angularjs进阶笔记(2)-自定义指令中的数据绑定

    有关自定义指令的scope参数,网上很多文章都在讲这3种绑定方式实现的效果是什么,但几乎没有人讲到底怎么使用,本篇希望聊聊到底怎么用这个话题. 一. 自定义指令 自定义指令,是Angularjs用来实 ...

  9. Android(java)学习笔记219:开发一个多界面的应用程序之两种意图

    1.两种意图: (1)显式意图: 在代码里面用intent设置要开启Activity的字节码.class文件: (2)隐式意图: Android(java)学习笔记218:开发一个多界面的应用程序之人 ...

随机推荐

  1. Web框架之Django篇

    1.创建Project 命令: django-admin startproject mysite 2.配置 (1)模板配置 (2)静态文件配置 (3)csrf隐藏 3.路由关系 一个url对应一个函数 ...

  2. RDSS和RNSS

    RNSS英文全称Radio Navigation Satellite System,由用户接收卫星无线电导航信号,是一种卫星无线电导航业务,自主完成至少到4颗卫星的距离测量,进行用户位置,速度及航行参 ...

  3. Redis安装、命令以及设置密码遇到的问题

    一.下载Redis 如果没有 安装wget先安装wget和gcc(使用make的时候会用上) wget http://download.redis.io/releases/redis-4.0.8.ta ...

  4. SPARK-AM-TrackURL-UI-500

    HTTP ERROR 500 Problem accessing /proxy/application_1538120222810_0072/. Reason: Connection refused ...

  5. 基于js的数据结构与算法-数组

    Given an array of integers, return indices of the two numbers such that they add up to a specific ta ...

  6. QEMU KVM Libvirt手册(11): Managing Storage

    When managing a VM Guest on the VM Host Server itself, it is possible to access the complete file sy ...

  7. [Swift]LeetCode126. 单词接龙 II | Word Ladder II

    Given two words (beginWord and endWord), and a dictionary's word list, find all shortest transformat ...

  8. [Swift]LeetCode525. 连续数组 | Contiguous Array

    Given a binary array, find the maximum length of a contiguous subarray with equal number of 0 and 1. ...

  9. [Swift]LeetCode674. 最长连续递增序列 | Longest Continuous Increasing Subsequence

    Given an unsorted array of integers, find the length of longest continuous increasing subsequence (s ...

  10. zuul进阶学习(二)

    1. zuul进阶学习(二) 1.1. zuul对接apollo 1.1.1. Netflix Archaius 1.1.2. 定期拉 1.2. zuul生产管理实践 1.2.1. zuul网关参考部 ...