我正在寻找有关如何绑定到AngularJS中的服务属性的最佳实践。
我已经研究了多个示例,以了解如何绑定到使用AngularJS创建的服务中的属性。
下面有两个关于如何绑定到服务中的属性的示例。他们都工作。第一个示例使用基本绑定,第二个示例使用$ scope。$ watch绑定到服务属性
当绑定到服务中的属性时,这些示例是首选还是建议使用其他我不知道的选项?
这些示例的前提是服务应每5秒更新其属性“ lastUpdated”和“ calls”。服务属性更新后,视图应反映这些更改。这两个示例都可以成功运行;我想知道是否有更好的方法。
基本绑定
可以在下面的代码中查看和运行以下代码:http : //plnkr.co/edit/d3c16z
<html> <body ng-app="ServiceNotification" > <div ng-controller="TimerCtrl1" style="border-style:dotted"> TimerCtrl1 <br/> Last Updated: {{timerData.lastUpdated}}<br/> Last Updated: {{timerData.calls}}<br/> </div> <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.5/angular.js"></script> <script type="text/javascript"> var app = angular.module("ServiceNotification", []); function TimerCtrl1($scope, Timer) { $scope.timerData = Timer.data; }; app.factory("Timer", function ($timeout) { var data = { lastUpdated: new Date(), calls: 0 }; var updateTimer = function () { data.lastUpdated = new Date(); data.calls += 1; console.log("updateTimer: " + data.lastUpdated); $timeout(updateTimer, 5000); }; updateTimer(); return { data: data }; }); </script> </body> </html>
解决绑定服务属性的另一种方法是在控制器中使用$ scope。$ watch。
$ scope。$ watch
可以在此处查看和运行以下代码:http : //plnkr.co/edit/dSBlC9
<html> <body ng-app="ServiceNotification"> <div style="border-style:dotted" ng-controller="TimerCtrl1"> TimerCtrl1<br/> Last Updated: {{lastUpdated}}<br/> Last Updated: {{calls}}<br/> </div> <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.5/angular.js"></script> <script type="text/javascript"> var app = angular.module("ServiceNotification", []); function TimerCtrl1($scope, Timer) { $scope.$watch(function () { return Timer.data.lastUpdated; }, function (value) { console.log("In $watch - lastUpdated:" + value); $scope.lastUpdated = value; } ); $scope.$watch(function () { return Timer.data.calls; }, function (value) { console.log("In $watch - calls:" + value); $scope.calls = value; } ); }; app.factory("Timer", function ($timeout) { var data = { lastUpdated: new Date(), calls: 0 }; var updateTimer = function () { data.lastUpdated = new Date(); data.calls += 1; console.log("updateTimer: " + data.lastUpdated); $timeout(updateTimer, 5000); }; updateTimer(); return { data: data }; }); </script> </body> </html>
我知道我可以在服务中使用$ rootscope。$ broadcast并在控制器中使用$ root。$ on,但是在我创建的其他示例中,第一次广播中使用$ broadcast / $并不能捕获控制器,但是在控制器中触发了广播的其他呼叫。如果您知道解决$ rootscope。$ broadcast问题的方法,请提供答案。
但是要重申我之前提到的内容,我想知道如何绑定到服务属性的最佳实践。
更新资料
最初在2013年4月提出并回答了这个问题。2014年5月,Gil Birman提供了一个新答案,我将其更改为正确答案。由于吉尔·伯曼(Gil Birman)的回答几乎没有票数,因此我担心的是,阅读此问题的人会无视他的回答,而赞成其他票数更多的回答。在您决定最佳答案之前,我强烈建议您使用Gil Birman的答案。
考虑 第二种方法的 一些 优缺点 :
0 {{lastUpdated}}而不是{{timerData.lastUpdated}},这很容易被接受{{timer.lastUpdated}},我可能会认为它更具可读性(但请不要争论……我给这一点是中性评级,以便您自己决定)
{{lastUpdated}}
{{timerData.lastUpdated}}
{{timer.lastUpdated}}
+1 控制器作为标记的一种API可能很方便,这样,如果数据模型的结构发生某种变化,您就可以(理论上)更新控制器的 API映射, 而无需触摸html局部。
-1 然而,理论并不总是练习,我经常发现自己不得不修改标记 和 控制器逻辑更改时呼吁, 反正 。因此,编写API的额外努力抵消了它的优势。
-1 而且,这种方法不是很干。
-1 如果要将数据绑定到ng-model代码,则DRY变得更少,因为必须将其重新打包$scope.scalar_values在控制器中以进行新的REST调用。
ng-model
$scope.scalar_values
-0.1 创建额外的观察器对性能 几乎 没有影响。此外,如果不需要在特定控制器中监视的数据属性附加到模型,它们将为深度监视者增加额外的开销。
-1 如果多个控制器需要相同的数据模型怎么办?这意味着您需要在每个模型更改时更新多个API。
$scope.timerData = Timer.data;正在开始发出巨大的诱惑……让我们更深入地探讨最后一点……我们在谈论什么样的模型更改?后端(服务器)上的模型?还是仅在前端创建并创建的模型?在这两种情况下, 数据映射API 本质上都属于 前端服务层 (角度工厂或服务)。(请注意,您的第一个示例(我的偏好)在 服务层 中没有这样的API ,这很好,因为它很简单,不需要它。)
$scope.timerData = Timer.data;
总而言之 ,一切都不必分离。就将标记与数据模型完全脱钩而言,弊大于利。
一般而言,控制器 不应乱用$scope = injectable.data.scalar。相反,他们应该被洒$scope = injectable.data的,promise.then(..)‘s和$scope.complexClickAction = function() {..}的
$scope = injectable.data.scalar
$scope = injectable.data
promise.then(..)
$scope.complexClickAction = function() {..}
作为实现数据解耦和视图封装的另一种方法, 将视图与模型分离的 唯一 有意义的 地方是 使用伪指令 。但即使在那儿,也不要$watch在controlleror link函数中标量值。那将不会节省时间,也不会使代码更具可维护性和可读性。它甚至都不会使测试变得容易,因为 角度的强大测试通常会测试最终的DOM 。相反,在指令中,要求您以对象形式提供 数据API ,并且只喜欢使用$watch由创建的er ng-bind。
$watch
controller
link
ng-bind
示例 http://plnkr.co/edit/MVeU1GKRTN4bqA3h9Yio
<body ng-app="ServiceNotification"> <div style="border-style:dotted" ng-controller="TimerCtrl1"> TimerCtrl1<br/> Bad:<br/> Last Updated: {{lastUpdated}}<br/> Last Updated: {{calls}}<br/> Good:<br/> Last Updated: {{data.lastUpdated}}<br/> Last Updated: {{data.calls}}<br/> </div> <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.5/angular.js"></script> <script type="text/javascript"> var app = angular.module("ServiceNotification", []); function TimerCtrl1($scope, Timer) { $scope.data = Timer.data; $scope.lastUpdated = Timer.data.lastUpdated; $scope.calls = Timer.data.calls; }; app.factory("Timer", function ($timeout) { var data = { lastUpdated: new Date(), calls: 0 }; var updateTimer = function () { data.lastUpdated = new Date(); data.calls += 1; console.log("updateTimer: " + data.lastUpdated); $timeout(updateTimer, 500); }; updateTimer(); return { data: data }; }); </script> </body>
更新 :我终于回到这个问题,补充说,我认为这两种方法都不是“错误的”。最初我写的是乔什·戴维·米勒(Josh David Miller)的回答是错误的,但回想起来,他的观点是完全正确的,尤其是他关于关注点分离的观点。
除了关注点分离(但与切向相关)外,我没有考虑 防御性复制 的另一个原因。这个问题主要涉及直接从服务读取数据。但是,如果团队中的开发人员决定控制器需要在视图显示之前以某种简单的方式转换数据呢?(控制器是否应该完全转换数据是另一个讨论。)如果她不首先复制该对象,则可能会不经意地导致另一个使用相同数据的视图组件发生回归。
这个问题真正突出的是典型的角度应用程序(以及实际上任何JavaScript应用程序)的体系结构缺陷:关注点紧密耦合以及对象可变性。最近,我迷上了使用React 和 不可变数据结构设计应用程序的方法。这样做可以很好地解决以下两个问题:
关注点分离 :通过道具的组件消耗所有它的数据,并拥有小到无的全球单身人士(如角服务)的依赖,并一无所知发生什么 上面 视图层次结构中它。
可变性 :所有道具都是不可变的,从而消除了不知不觉间数据突变的风险。
Angular 2.0现在有望从React大量借鉴以实现上述两点。