Angularjs1.X中两种不同的双向数据绑定
聊聊 Angularjs1.x中那些活见鬼的事情。
一. html与Controller中的双向数据绑定
html-Controller的双向数据绑定,在开发中非常常见,也是Angularjs1.x的宣传点之一,使用中并没有太多问题。
1.1数据从html流向controller
也就是从 视图层 流向 模型层 ,原生html中需要使用表单元素(例如 input 标签)来收集用户输入信息,Angularjs中通过在表单元素上使用 ng-model 标签,当用户输入信息时,同步将用户输入的信息赋值给controller中的变量:
<body ng-app="myApp"> <div ng-controller="myCtrl"> <p>改变输出值:</p> <input type="text" ng-model="testInfo.content" ng-change="showInput()"> </div> <script src="https://www.jb51.net/article/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 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="https://www.jb51.net/article/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 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="https://www.jb51.net/article/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中的双向数据绑定