如何将控制器中的设置对象传递给服务?

Ben*_*aum 6 javascript singleton state angularjs

TL;博士;

我需要传达几个服务需要的状态,并且源于绑定到控制器范围的数据.什么是良好的'Angular zen'方式呢?

背景故事

我正在开发一个单页面应用程序,经过深思熟虑后决定使用AngularJS.这些页面的布局方式类似于:

在此输入图像描述

实际布局并不重要,类似布局的概念保持不变.我需要将绑定到范围的信息SettingsController传递给ngView需要的控制器的服务.当用户对任何滑块进行修改时,我还需要更新从控制器中的服务获得的内容.

我试过的

我想到的唯一方法就是:http://jsfiddle.net/5sNcG/我必须自己编写绑定并添加侦听器更改范围.我可能离开这里并且有一种明显的"Angular"方式 - 尽管我付出了努力但我找不到它.

/code from fiddle.
var app = angular.module("myApp",[]);

app.controller("HomeCtrl",function($scope,FooService,$interval){
    FooService.change(function(){
        console.log("HI",FooService.getFoo());
        $scope.foo = FooService.getFoo();
    });
});

app.factory("Configuration",function(){
    var config = {data:'lol'};
    var callbacks = [];
    return {
        list:function(){ return config;},
        update:function(){
            callbacks.forEach(function(x){ x();});
        },
        change:function(fn){
            callbacks.push(fn); // I never remove these, so this is a memory leak!
        }
    }
});
app.service("FooService",function(Configuration){
    return {
        getFoo: function(){
            return Configuration.list().data+" bar";    
        },change:function(fn){
            Configuration.change(fn);    
        }
    }
});
app.controller("SettingsCtrl",function($scope,Configuration){

    $scope.config = Configuration.list();
    $scope.$watch('config',function(){
        Configuration.update();
    },true);
});
Run Code Online (Sandbox Code Playgroud)

我也考虑过$rootScope广播,但这似乎更像是一个全球化的国家

无论我尝试什么,我都有一个具有全局状态的单身人士,我们都知道我不想要一个单身人士.

因为这似乎是一个相当常见的Angular用例.什么是解决这个问题的惯用方法?

sca*_*rlz 6

我过去曾遇到类似的问题,并考虑了四种可能的方法:

  • 运用 $broadcast
  • 存储设置 $rootScope
  • 观察者模式(就像你在这里一样)
  • 使用$watch从控制器

以下是我对每个问题的看法:

$广播

在我看到的AngularJS演示中,MiškoHevery谈到了$broadcast(即事件)的使用和用例.要点$broadcast是更多的意图是对那些与你正在使用的东西没有紧密结合的事件做出反应,否则一个替代方案可能更可取.关于这个主题,角度维基上的最佳实践指南建议:

仅对原子事件使用.$ broadcast(),. $ emit()和.$ on():在整个应用程序中全局相关的事件(例如用户身份验证或应用程序关闭).

在这里,由于您拥有与任何填充密切相关的设置ng-view,因此建议使用的替代方案$broadcast更可取.

$ rootScope

当你提到(并且想要避免)时,这是一个全局状态.尽管这通常是一个简单的选择,但不是我个人喜欢将设置暴露给我的整个应用程序.我个人保留$rootScope配置设置和'软'变量,如页面标题等.我不会选择使用此选项.

观察者模式

注册针对配置工厂的回调是一种可靠的方法.对于持久性回调,您可以$destroy在作用域上侦听事件,在removeConfiguration工厂上调用方法以删除回调.这可以被认为是如何$broadcast使用的一个很好的例子; 控制器关注事件并且必须对其作出反应,但事件本身并不特定于控制器/配置服务共享的数据.

$腕表

通过使用共享服务,可以将其注入任何与设置有关的控制器.现在,对配置的任何更改都会触发您的回调,或许某些视图可能只涉及一个或两个配置设置.$watch将允许您更轻松地观察仅对这些属性的更改.我无法谈论开销与注册回调,但这对我来说就像是最"有棱有角"的方式.

这是如何使用$watch以下方法实现的:

var app = angular.module("myApp",[]);

app.factory("Configuration",function(){
   var data = {
     settingOne: true,
     settingTwo: false 
   };
   return data;
})

app.controller("SettingsCtrl",function($scope, Configuration){
  // do something
})

app.controller("HomeCtrl",function($scope, Configuration){
   // detect any change to configuration settings
   $scope.$watch(function() {
     return Configuration;
   }, function(data) {
     // do something
   }, true)

   // alternatively only react to settingTwo changing
   $scope.$watch(function() {
     return Configuration.settingTwo
   }, function(data) {
     // do something
   })
})
Run Code Online (Sandbox Code Playgroud)

请注意,如果您需要稍微复杂的配置工厂,则可以转而使用getter/setter方法并将配置设置保持为私有.然后,在$watch,您应该观察方法调用而不是属性本身.

更新:

在回答时,我更喜欢$watch控制器内部的方法.在使用框架开发一段时间之后,我现在尝试$watch完全不使用控制器,而是在可能的情况下优先在值的更改点或通过杠杆作用直接调用函数ng-change.

这样做的一个原因是它增加了测试控制器的复杂性,但也许更低效:对于每个$digest循环角度调用,每个注册的$watch都将被评估,并且它可能很好地响应对值的变化现有的$watch.

而不是在这个角度推测缺点和解决方案,这里有一篇关于这个问题的非常好的文章:Angular JS - 你可能不应该在你的控制器中使用$ watch.