Angular JS - 使控制器和视图可以全局访问服务

Rob*_*ian 30 javascript angularjs

假设我们有以下服务:

myApp.factory('FooService', function () { ...
Run Code Online (Sandbox Code Playgroud)

然后,从控制器,我会说:

myApp.controller('FooCtrl', ['$scope', 'FooService', function ($scope, FooService) { ...
Run Code Online (Sandbox Code Playgroud)

这个由两部分组成的问题是:

  1. 全局可访问性:如果我有100个控制器并且都需要访问该服务,我不想明确地注入它100次. 如何使全球服务可用? 我现在唯一能想到的就是将它从根范围内包装起来,这会破坏目的.
  2. 视图中的辅助功能:如何从视图中访问服务? 这篇文章建议从控制器中包装服务.如果我要达到那个长度,似乎我应该只在根范围上实现功能?

Rob*_*ian 39

找到合理的解决方案.将其注入引导方法(运行),并将其添加到根范围.从那里它将可用于所有控制器和视图.

myApp.run(function ($rootScope, $location, $http, $timeout, FooService) {
    $rootScope.foo = FooService;
    ....
Run Code Online (Sandbox Code Playgroud)

重新阅读上面提到的帖子,它没有说"完全包裹"......只是"抽象",所以我认为海报是指同一个解决方案.

为了彻底,(1)的答案是:

myApp.controller('FooCtrl', ['$scope', function ($scope) { 
    // scope inherits from root scope
    $scope.foo.doSomething();
    ...
Run Code Online (Sandbox Code Playgroud)

(2)的答案很简单:

{{doSomething()}}
Run Code Online (Sandbox Code Playgroud)

添加克里斯托弗的评论以确保它被看到:

@rob - 根据最佳实践,工厂应该注入需要使用它的控制器,而不是根据范围.如上所述,第一个问题实际上是反模式.如果您需要工厂100次,则将其注射100次.在缩小时几乎不需要任何额外的代码,并且非常清楚工厂的使用位置,并且通过在函数签名中列出所需的工厂,可以更容易(更明显地)使用模拟测试这些控制器. - Christopher WJ Rueber 2013年11月25日20:06

  • @rob - 根据最佳实践,工厂应该注入需要使用它的控制器,而不是根据范围.如上所述,第一个问题实际上是反模式.如果您需要工厂100次,则将其注射100次.在缩小时几乎不需要任何额外的代码,并且非常清楚工厂的使用位置,并且通过在函数签名中列出所需的工厂,可以更容易(更明显地)使用模拟测试这些控制器. (7认同)
  • @sidonaldson这通常被认为是角度世界中的反模式.因为它使得环境较不可测试(由于假设服务始终在作用域上可用).这就是为什么你没有看到关于这个主题的很多帖子. (5认同)
  • @chris - 我同意并且在使用Angular一段时间后得出了同样的结论.如果你在答案中清楚地表达了这一点,我将在最佳答案中选择它(如果可能的话,之前没有覆盖答案选择).否则我可能只是在找到时间时编辑这篇文章. (5认同)

Jam*_*s M 6

至于直接在视图中访问服务,这看起来非常无棱角.将它绑定到控制器中的范围变量似乎比在UI中直接使用服务来帮助维护职责分离更好.

  • 我不同意这是做"它"的"正确"方式.更多zen-angular方式似乎是基于服务调用在控制器中设置范围变量,然后绑定到范围变量而不是直接绑定到服务调用.职责分离! (6认同)

ton*_*hnb 6

补充问题#1(全局可访问性)我只会添加它以避免在最小化文件时出现问题(如果是这种情况),它应该像这样写:

this.app.run(["$rootScope", "Foo", function($rootScope, FooService) {
    return $rootScope.fooService = FooService;
  }
]);
Run Code Online (Sandbox Code Playgroud)