在应用程序配置中使用自定义提供者内的 $http,angular.js

91

主要问题是:是否可能?我尝试过,但没有成功。

主要的app.js文件

...
var app = angular.module('myApp', ['services']);
app.config(['customProvider', function (customProvider) {

}]);
...

服务提供商本身

var services = angular.module('services', []);
services.provider('custom', function ($http) {
});

我遇到了这样的错误:

Uncaught Error: Unknown provider: $http from services 

有什么想法吗?

谢谢!


可能的 - http://plnkr.co/edit/Sk9NmIIwO3B0Bg1bNP3S?p=preview - SET001
人,是的,没错,但我说的是“app.config”部分。 - Kosmetika
https://dev59.com/FGUp5IYBdhLWcg3wbXJf - SET001
我也知道这个限制,但是我认为在提供程序内部有可能解决。 - Kosmetika
4个回答

160

底线是:

  • 你不可以将服务注入到提供者配置部分
  • 你可以将服务注入到初始化提供者服务的部分

详情:

Angular框架有一个2阶段初始化过程:

阶段1:Config

config阶段中,所有提供者都被初始化并执行了所有的config部分。由于config部分可能包含用于配置提供者对象的代码,因此它们可以被注入Provider对象。 然而,由于提供者是服务对象的工厂,在这个阶段提供者还没有完全初始化/配置,所以你不能要求提供者为你创建服务->在配置阶段你不能使用/注入服务。当这个阶段完成时,所有的提供者都准备好了(在配置阶段完成后不再进行提供者配置)。

阶段2:Run

run阶段中,所有run部分都被执行。在这个阶段,提供者已经准备好并且可以创建服务->在run阶段你可以使用/注入服务

例子:

1. 向提供者初始化函数注入$http服务不会起作用

//ERRONEOUS
angular.module('myModule').provider('myProvider', function($http) {
    // SECTION 1: code to initialize/configure the PROVIDER goes here (executed during `config` phase)
    ...

    this.$get = function() {
        // code to initialize/configure the SERVICE goes here (executed during `run` stage)

        return myService;
    };
});
由于我们正试图将$http服务注入到在config阶段执行的函数中,因此会出现错误:
Uncaught Error: Unknown provider: $http from services 
实际上,这个错误是在说用于创建$http服务的$httpProvider还没有准备好(因为我们仍然处于config阶段)。将$http服务注入服务初始化函数将会起作用。
//OK
angular.module('myModule').provider('myProvider', function() {
    // SECTION 1: code to initialize/configure the PROVIDER goes here (executed during `config` phase)
    ...

    this.$get = function($http) {
        // code to initialize/configure the SERVICE goes here (executed during `run` stage)

        return myService;
    };
});

由于我们现在将服务注入到服务初始化函数中,在执行run阶段,因此这段代码将起作用。


64
回答不错,但是它只解释了在配置期间无法注入服务的原因,没有解释如何在配置期间进行HTTP POST/GET。这对于使用API提供的值进行配置的应用程序非常重要。 - Sean O'Dell
3
@bebraw 和 Kosmetika - 在配置阶段,我认为您需要请求的唯一事项是某种设置对象。也许它包含 API 端点、用户信息、用户的语言环境和语言设置等。如果是这种情况,我建议在 JavaScript 源代码中以某种方式包含这些信息。您可以在 index.html 上使用服务器端渲染来放置一些设置,以便在应用程序初始化之前就可以使用。其他所有内容,我会尝试在初始化后再进行处理。 - Sean Clark Hess
2
@Sean:如何进行HTTP POST/GET请求是一个不同的问题,与OP(是否可以在配置阶段内使用$http)无关,可能需要单独发布;由于Angular的配置阶段是同步的,提供服务器端数据给您的配置代码的好方法是在服务器端渲染期间将其呈现为JavaScript对象,例如<script>var config = <% = mySettings.toJson() %>;</script>。这可以使用模板引擎,如PHP的Smarty,Python的Jinja2,NodeJS的Nunchucks等来完成。 - Trevor
4
将配置数据直接插入到服务器上的 HTML 或 js 中,仅在客户端代码来自同一服务器时才有效。使用 CORS,现在可以(并且非常希望)从不同的服务器提供客户端代码,并从单独的服务器提供数据。在这些情况下,我们确实需要使用 HTTP 获取配置数据。 - Bernard
4
虽然这是一个回答,但不是被问到的问题的答案。 - Eric Rini
显示剩余6条评论

65

这可能会给你一些杠杆力:

var initInjector = angular.injector(['ng']);
var $http = initInjector.get('$http');

不过要小心,成功或错误的回调可能会让你在应用程序启动和服务器响应之间陷入竞争条件。


6
“被采纳的答案”对于我的供应商没有用……我花了两天时间尝试让它工作,但是没有希望。而你的方法立刻就有效了。 - Dave Alperovich
你能否澄清这里创建的实例是“真正”的服务单例还是仅仅是服务的一个实例,在 Angular 执行其真正的注入器魔法时被丢弃。 - Eric Rini
Eric,目前我无法确认。但是,如果适用的话,我通常会使用 angular.injector(['mymodule']) -- 但我不确定你是否可以使用这种方法来调用 $http 服务。我想说我以前用过。不确定这是否有帮助 :-/ - Cody
2
这应该是被接受的答案。我曾经花了很长时间努力让它工作,但这种方法立即解决了我的问题。我认为这可能是一个非常普遍的问题。谢谢@Cody。 - iamdash
5
我确认已接受的解决方案在提供程序中使用 $http 不起作用。但是 @Cody 的答案确实解决了这个问题。 - Dino

1
这是一个老问题,如果我们想依赖库的核心能力,似乎存在一些鸡生蛋蛋生鸡的问题。
我所做的不是从根本上解决问题,而是绕过它。创建一个指令来包装整个主体。例如:
<body ng-app="app">
  <div mc-body>
    Hello World
  </div>
</body>

现在需要在渲染之前初始化mc-body(仅一次),例如:
link: function(scope, element, attrs) {
  Auth.login().then() ...
}

"Auth"是一个服务或提供者,例如。
.provider('Auth', function() {
  ... keep your auth configurations
  return {
    $get: function($http) {
      return {
        login: function() {
          ... do something about the http
        }
      }
    }
  }
})

似乎我对引导程序的顺序有控制权,它在常规引导程序解决所有提供程序配置后尝试初始化mc-body指令。
对我来说,这个指令似乎可以在路由之前,因为路由也是通过指令注入的,例如<ui-route />。但我可能是错误的。需要进行更多调查。

你能详细阐述一下你的解决方案吗? - Mark

-3
作为对你的问题“有什么想法?”的回应,我本来会回答“有”。但是等等,还有更多!
我建议在配置中只使用JQuery。例如:
var app = angular.module('myApp', ['services']);
app.config(['$anyProvider', function ($anyProvider) {
    $.ajax({
        url: 'www.something.com/api/lolol',
        success: function (result) {
            $anyProvider.doSomething(result);
        }
    });
}]);

成功回调中的$customProvider包含了$符号,就好像它是一个内部提供者一样。 - Jeff Fischer
1
你说得对,我混用了美元符号和非美元符号。我已经把它们都更新为美元符号了。 - Suamere

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接