AngularJS:结合到服务属性的正确方法 [英] AngularJS : The correct way of binding to a service properties

查看:193
本文介绍了AngularJS:结合到服务属性的正确方法的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在寻找如何绑定到AngularJS服务属性的最佳做法。

I’m looking for the best practice of how to bind to a service property in AngularJS.

我已通过多个实施例的工作,以了解如何结合性质在正在使用AngularJS创建的服务。

I have worked through multiple examples to understand how to bind to properties in a service that is created using AngularJS.

下面我对如何绑定到一个服务性质的两个例子;他们都工作。第一例子使用基本绑定和第二实施例中使用$范围。$观看绑定到服务属性

Below I have two examples of how to bind to properties in a service; they both work. The first example uses basic bindings and the second example used $scope.$watch to bind to the service properties

在服务绑定到属性或时要么是这些例子preferred那里会建议我不知道的是另一种选择?

Are either of these example preferred when binding to properties in a service or is there another option that I’m not aware of that would be recommended?

这些例子的premise是服务应该更新其属性LASTUPDATED和来电每5秒。一旦服务属性更新视图应该反映这些变化。这两个例子成功运行;我不知道是否有这样做的更好的方法。

The premise of these examples is that the service should updated its properties "lastUpdated" and "calls" every 5 seconds. Once the service properties are updated the view should reflect these changes. Both these example work successfully; I wonder if there is a better way of doing it.

基本绑定

以下code可以在这里浏览和跑: http://plnkr.co/edit/d3c16z

The following code can be view and ran here: 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>

另一种方法我解决了绑定服务属性是使用$范围。$腕表在控制器中。

The other way I solved binding to service properties is to use $scope.$watch in the controller.

$范围。$看

以下code可以在这里浏览和跑: http://plnkr.co/edit/dSBlC9

The following code can be view and ran here: 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。$播出。$在控制器,但我已经创建了一个使用$广播/ $关于在第一广播是不是其他的例子由控制器抓获,但被广播其它呼叫控制器被触发。如果你是知道的一种方式来解决$ rootscope。$广播问题,请提供一个答案。

I’m aware that I can use $rootscope.$broadcast in the service and $root.$on in the controller, but in other examples that I’ve created that use $broadcast/$on the first broadcast is not captured by the controller, but additional calls that are broadcasted are triggered in the controller. If you are aware of a way to solve $rootscope.$broadcast problem, please provide an answer.

但重申我刚才提到的,我想知道如何绑定到一个服务性质的最佳实践。

But to restate what I mentioned earlier, I would like to know the best practice of how to bind to a service properties.



更新

这个问题最初是问及在2013年四月在回答2014年5月,吉尔·伯曼提供了一个新的答案,这是我改变了正确的答案。由于吉尔伯曼答案已经很少了,票,我关心的是人们阅读这个问题将不理会他赞成其他答案的答案多了很多选票。在你做出什么是最好的答案决定,我强烈建议吉尔·伯曼的答案。

This question was originally asked and answered in April 2013. In May 2014, Gil Birman provided a new answer, which I changed as the correct answer. Since Gil Birman answer has very few up-votes, my concern is that people reading this question will disregard his answer in favor of other answers with many more votes. Before you make a decision on what's the best answer, I highly recommend Gil Birman's answer.

推荐答案

考虑第二种方法的一些利弊

Consider some pros and cons of the second approach:


  • 0 {{} LASTUPDATED} 而不是 {{} timerData.lastUpdated} ,这可能很容易被 {{} timer.lastUpdated} ,我可能会说是更具可读性(但我们不要争论......我让这一点中性评级,所以你自己决定)

  • 0 {{lastUpdated}} instead of {{timerData.lastUpdated}}, which could just as easily be {{timer.lastUpdated}}, which I might argue is more readable (but let's not argue... I'm giving this point a neutral rating so you decide for yourself)

+1 可以方便的控制作为一种API为标记,例如,如果数据模型以某种方式结构的变化可以(理论上)更新控制器的 API映射的不接触HTML部分。

+1 It may be convenient that the controller acts as a sort of API for the markup such that if somehow the structure of the data model changes you can (in theory) update the controller's API mappings without touching the html partial.

1 然而,理论并不总是练习,我通常会发现自己不得不修改标记的的更改时,要求控制器逻辑,反正的。所以写的API额外的努力,否定了它的优势。

-1 However, theory isn't always practice and I usually find myself having to modify markup and controller logic when changes are called for, anyway. So the extra effort of writing the API negates it's advantage.

1 此外,这种方法是不是很干。

-1 Furthermore, this approach isn't very DRY.

1 如果您希望将数据绑定到 NG-模型您code变得少干,你必须重新包装 $ scope.scalar_values​​ 控制器作出新的REST调用。

-1 If you want to bind the data to ng-model your code become even less DRY as you have to re-package the $scope.scalar_values in the controller to make a new REST call.

-0.1 有一个微小的性能击中创造额外守望者(S)。此外,如果数据的属性连接到不需要在一个特定的控制器要监视他们将创建为深观察家额外开销模型

-0.1 There's a tiny performance hit creating extra watcher(s). Also, if data properties are attached to the model that don't need to be watched in a particular controller they will create additional overhead for the deep watchers.

1 如果有多个控制器需要相同的数据模型?这意味着,你有多个API与每一个模型的变化更新。

-1 What if multiple controllers need the same data models? That means that you have multiple API's to update with every model change.

$ scope.timerData = Timer.data; 开始听起来诱人浩浩荡荡一下吧......让我们下潜得更深一些,到最后一点...什么一种模式的变化是,我们在谈论什么?在后端(服务器)的模型?或其中创建和模型只生活在前端?在这两种情况下,本质上是对的数据映射API 的属于在前端服务层的,(角工厂或服务)。 (请注意,您的第一个例子 - 我的preference--不具有这样的API中的服务层的,这是很好的,因为它足够简单,它并不需要它。)

$scope.timerData = Timer.data; is starting to sound mighty tempting right about now... Let's dive a little deeper into that last point... What kind of model changes were we talking about? A model on the back-end (server)? Or a model which is created and lives only in the front-end? In either case, what is essentially the data mapping API belongs in the front-end service layer, (an angular factory or service). (Note that your first example--my preference-- doesn't have such an API in the service layer, which is fine because it's simple enough it doesn't need it.)

结论,一切都不必去耦。并尽可能完全从数据模型解耦的标记,缺点大于优点。

In conclusion, everything does not have to be decoupled. And as far as decoupling the markup entirely from the data model, the drawbacks outweigh the advantages.

控制器,一般不应该与 $范围散落= injectable.data.scalar 的。相反,他们应该 $ =范围injectable.data 洒的, promise.then(..) s和 $ scope.complexClickAction =功能(){...}

Controllers, in general shouldn't be littered with $scope = injectable.data.scalar's. Rather, they should be sprinkled with $scope = injectable.data's, promise.then(..)'s, and $scope.complexClickAction = function() {..}'s

作为一种替代的方式来实现数据解耦,从而查看封装,即的唯一地方真的很有意义脱钩从模型视图与指令。但即使在那里,千万不要 $观看标值控制器链接功能。这不会节省时间或使code任何更容易维护,也没有可读性。它甚至不会使测试更加简单,因为在角强大的测试通常反正测试得到的DOM 的。相反,在指令中仅使用 $观看由创建器纳克要求您的数据API 的对象的形式,与青睐-bind

As an alternative approach to achieve data-decoupling and thus view-encapsulation, the only place that it really makes sense to decouple the view from the model is with a directive. But even there, don't $watch scalar values in the controller or link functions. That won't save time or make the code any more maintainable nor readable. It won't even make testing easier since robust tests in angular usually test the resulting DOM anyway. Rather, in a directive demand your data API in object form, and favor using just the $watchers created by 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>


更新:我终于回来这个问题,补充一点,我不认为这两种方法是错误的。本来我已经写了约什 - 戴维·米勒的回答是不正确的,但是回想他的观点是完全有效的,特别是他对分离关注点。


UPDATE: I've finally come back to this question to add that I don't think that either approach is "wrong". Originally I had written that Josh David Miller's answer was incorrect, but in retrospect his points are completely valid, especially his point about separation of concerns.

关注一边(而切向相关的)分离,还有另外一个原因的防御复制的,我没有考虑到。这个问题大多与读取数据涉及直接的服务。但是,如果你的团队的开发人员决定该控制器需要在一些琐碎的方式来转换数据的视图显示它之前? (控制器是否应在所有转换数据是另一个讨论。)如果她不作对象的副本第一,她可能会无意中导致回归在消耗相同的数据的另一个视图组件。

Separation of concerns aside (but tangentially related), there's another reason for defensive copying that I failed to consider. This question mostly deals with reading data directly from a service. But what if a developer on your team decides that the controller needs to transform the data in some trivial way before the view displays it? (Whether controllers should transform data at all is another discussion.) If she doesn't make a copy of the object first she might unwittingly cause regressions in another view component which consumes the same data.

这是什么问题,真正的亮点是典型的角度应用程序(和任何真正的JavaScript应用)建筑缺点:关注紧耦合和对象可变性。我最近变得迷恋架构与阵营的应用的不可变的数据结构。这样解决了以下两个问题奇妙

What this question really highlights are architectural shortcomings of the typical angular application (and really any JavaScript application): tight coupling of concerns, and object mutability. I have recently become enamored with architecting application with React and immutable data structures. Doing so solves the following two problems wonderfully:


  1. 关注分离:一个组件通过道具消耗所有它的数据,并有小到没有对全局单(如角服务)的依赖,什么都不知道发生了什么事的上方的它的视图层次。

  1. Separation of concerns: A component consumes all of it's data via props and has little-to-no reliance on global singletons (such as Angular services), and knows nothing about what happened above it in the view hierarchy.

可变性:所有道具都是不可变免去不知情的数据突变的风险

Mutability: All props are immutable which eliminates the risk of unwitting data mutation.

角2.0的轨道上,现在在很大程度上借用作出反应,以实现上述两点。

Angular 2.0 is now on track to borrow heavily from React to achieve the two points above.

这篇关于AngularJS:结合到服务属性的正确方法的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆