我已经通过多个示例来了解如何绑定到使用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",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",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}},我可能认为是更可读(但我们不要争辩…我给这点中性评级,所以你自己决定)
> 1可能方便的是,控制器充当标记的一种API,使得如果某种方式数据模型的结构更改,您可以(理论上)更新控制器的API映射,而不触及html partial。
> -1然而,理论并不总是实践,我通常发现自己必须修改标记和控制器逻辑,当改变被要求,无论如何。因此,编写API的额外工作否定了它的优势。
> -1此外,这种方法不是很干。
> -1如果要将数据绑定到ng-model,您的代码变得更加干燥,因为您必须在控制器中重新打包$ scope.scalar_values以进行新的REST调用。
> -0.1创建额外的观察者有一个很小的性能损失。此外,如果数据属性附加到模型,不需要在特定的控制器中观看,他们将为深度观察者创建额外的开销。
> -1如果多个控制器需要相同的数据模型怎么办?这意味着您有多个API可以更新每个模型更改。
$ scope.timerData = Timer.data;现在开始听起来很强大的诱惑…让我们深入一点点最后一点…我们在谈论什么样的模型变化?一个模型在后端(服务器)?还是一个只在前端创建和生活的模型?在任一情况下,基本上数据映射API属于前端服务层(角度工厂或服务)。 (请注意,您的第一个示例 – 我的首选项 – 在服务层没有这样的API,这是很好,因为它很简单,它不需要它)。
总之,一切都不必去耦合。并且,只要将标记完全与数据模型去耦合,这些缺点就超过了优点。
控制器,一般不应该散布与$ scope = injectable.data.scalar的。相反,它们应该被注入$ scope = injectable.data的,promise.then(..)的和$ scope.complexClickAction = function(){..}的
作为一种实现数据解耦和视图封装的替代方法,将视图与模型分离的唯一方法是使用指令。但即使有,在控制器或链接函数中不要$ watch标量值。这不会节省时间或使代码更可维护也不可读。它甚至不会使测试更容易,因为强大的角度测试通常测试生成的DOM。相反,在一个指令要求你的数据API对象形式,并赞成只使用由ng-bind创建的$ watchers。
例
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",Timer) { $scope.data = Timer.data; $scope.lastUpdated = Timer.data.lastUpdated; $scope.calls = Timer.data.calls; }; app.factory("Timer",500); }; updateTimer(); return { data: data }; }); </script> </body>
更新:我终于回到这个问题补充说,我不认为任何一种方法是“错误的”。最初我写的约书亚大卫米勒的回答是不正确的,但回想起来他的观点是完全有效的,特别是他的观点分离的关注。
将问题分开(但是切向相关),还有一个我没有考虑的防御性复制的原因。这个问题主要处理直接从服务读取数据。但是如果你的团队中的开发人员决定控制器需要在视图显示之前以一些微不足道的方式转换数据呢? (控制器是否应该转换数据是另一个讨论。)如果她没有首先创建对象的副本,她可能会在另一个视图组件中无意中导致消耗相同数据的回归。
这个问题真正突出的是典型的角度应用(和真正的任何JavaScript应用程序)的架构缺点:紧密耦合的关注和对象的可变性。我最近变得迷恋与React和不可变的数据结构的架构应用程序。这样做很好地解决了以下两个问题:
>分离关注:一个组件通过props消耗它的所有数据,并且几乎没有依赖全局单例(例如Angular服务),并且不知道它在视图层次结构中发生了什么。
>可变性:所有道具是不变的,消除了不知不觉的数据突变的风险。
Angular 2.0现在正在进行,从React大量借用来实现上述两点。