在Angular.js中可以向其他人注入什么“东西”?

时间:2013-05-30 05:02:52

标签: angularjs dependency-injection

我很难理解Angular中的依赖注入。所以我的问题是,任何人都可以解释哪些“类型”,如Controller,Factory,Provider等可以注入其他类型,包括其他相同“类型”的实例?

我实际上正在寻找的是这个表填充了y / n。对于具有相同行/列的单元格,这意味着将一个“类型”的值注入另一个具有相同“类型”的类型

+----------------+----------+------------+-----------+---------+--------+----------+---------+-------+
| Can we inject? | Constant | Controller | Directive | Factory | Filter | Provider | Service | Value |
+----------------+----------+------------+-----------+---------+--------+----------+---------+-------+
| Constant       |          |            |           |         |        |          |         |       |
| Controller     |          |            |           |         |        |          |         |       |
| Directive      |          |            |           |         |        |          |         |       |
| Factory        |          |            |           |         |        |          |         |       |
| Filter         |          |            |           |         |        |          |         |       |
| Provider       |          |            |           |         |        |          |         |       |
| Service        |          |            |           |         |        |          |         |       |
| Value          |          |            |           |         |        |          |         |       |
+----------------+----------+------------+-----------+---------+--------+----------+---------+-------+

3 个答案:

答案 0 :(得分:386)

而只是填写表格" yes"和"不"没有任何解释,我会详细介绍一下。

[注意,完成后添加:这最终......比我预期的要长得多。在底部有一个博士,但我希望这证明是有用的。]

[这个答案也被添加到AngularJS wiki:Understanding Dependency Injection]


提供者($provide

$provide服务负责告诉Angular如何创建新的可注射物品;这些东西叫做服务。服务由名为提供商的内容定义,这是您在使用$provide时创建的内容。定义提供程序是通过provider服务上的$provide方法完成的,您可以通过请求将其注入应用程序来获取$provide服务config { {1}}功能。一个例子可能是这样的:

app.config(function($provide) {
  $provide.provider('greeting', function() {
    this.$get = function() {
      return function(name) {
        alert("Hello, " + name);
      };
    };
  });
});

在这里,我们为名为greeting的服务定义了一个新的提供商;我们可以将一个名为greeting的变量注入任何可注入函数(如控制器,稍后会详细介绍),Angular将调用提供者的$get函数以返回服务的新实例。在这种情况下,将要注入的东西是一个函数,它根据名称获取name参数和alert消息。我们可能会这样使用它:

app.controller('MainController', function($scope, greeting) {
  $scope.onClick = function() {
    greeting('Ford Prefect');
  };
});

现在就是诀窍。 factoryservicevalue都只是定义提供商各个部分的捷径 - 也就是说,它们提供了一种定义提供者的方法,而无需输入所有内容。例如,您可以像这样编写完全相同的提供商

app.config(function($provide) {
  $provide.factory('greeting', function() {
    return function(name) {
      alert("Hello, " + name);
    };
  });
});
理解这一点非常重要,所以我要说一下:AngularJS正在调用我们上面写的完全相同的代码$provide.provider版本) for 我们。从字面上看,这两个版本没有差别。 value的工作方式相同 - 如果我们从$get函数(也就是我们的factory函数)返回的内容总是完全相同,我们可以使用{{ 1}}。例如,由于我们始终为value服务返回相同的功能,因此我们也可以使用greeting来定义它:

value

同样,这与我们用于定义此功能的其他两种方法完全相同 - 它只是一种节省输入的方法。

现在你可能已经注意到了我一直在使用的令人讨厌的app.config(function($provide) { $provide.value('greeting', function(name) { alert("Hello, " + name); }); }); 事情。由于定义新的提供程序(通过上面给定方法的任何)是如此常见,AngularJS直接在模块对象上公开app.config(function($provide) { ... })方法,以节省更多的输入:

$provider

这些都与我们之前使用的更详细的var myMod = angular.module('myModule', []); myMod.provider("greeting", ...); myMod.factory("greeting", ...); myMod.value("greeting", ...); 版本完全相同。

到目前为止我跳过的注射剂是app.config(...)。就目前而言,它很容易说它就像constant一样。我们稍后会看到其中的一个区别。

要审核所有这些代码正在执行完全相同的事情:

value

注射器($injector

注入器负责使用我们通过myMod.provider('greeting', function() { this.$get = function() { return function(name) { alert("Hello, " + name); }; }; }); myMod.factory('greeting', function() { return function(name) { alert("Hello, " + name); }; }); myMod.value('greeting', function(name) { alert("Hello, " + name); }); 提供的代码实际创建我们服务的实例(没有双关语意)。每当你编写一个带有注入参数的函数时,你就会看到喷射器在工作。每个AngularJS应用程序都有一个$provide,它在应用程序首次启动时创建;您可以通过向任何可注射函数注入$injector来获取它(是的,$injector知道如何注入自己!)

获得$injector后,您可以通过使用服务名称调用$injector来获取已定义服务的实例。例如,

get

注入器还负责将服务注入功能;例如,您可以使用注入器var greeting = $injector.get('greeting'); greeting('Ford Prefect'); 方法将服务神奇地注入到您拥有的任何函数中;

invoke

值得注意的是,注入器只会创建一个服务实例一次。然后它通过服务的名称缓存提供者返回的任何内容;下次您要求提供服务时,您实际上会得到完全相同的对象。

因此,要回答您的问题,您可以将服务注入使用var myFunction = function(greeting) { greeting('Ford Prefect'); }; $injector.invoke(myFunction); 调用的任何函数。这包括

  • 控制器定义功能
  • 指令定义函数
  • 过滤器定义功能
  • 提供者的$injector.invoke方法(又名$get定义函数)

由于factoryconstant总是返回静态值,因此不会通过注入器调用它们,因此您无法注入任何内容。

配置提供商

如果valueprovide等更容易,您可能想知道为什么有人愿意使用factory方法设置一个成熟的提供商。答案是提供商允许大量配置。我们已经提到过,当您通过提供程序(或Angular为您提供的任何快捷方式)创建服务时,您将创建一个新的提供程序来定义该服务的构造方式。我没有提及的是,这些提供商可以注入您应用的value部分,以便您与他们互动!

首先,Angular分两个阶段运行您的应用程序 - configconfig阶段。正如我们所见,run阶段是您可以根据需要设置任何提供商的地方。这也是设置指令,控制器,过滤器等的地方。正如您可能猜到的那样,config阶段是Angular实际编译DOM并启动应用程序的地方。

您可以使用runmyMod.config函数添加要在这些阶段中运行的其他代码 - 每个函数都会在该特定阶段运行一个函数。正如我们在第一部分中看到的,这些函数是可注入的 - 我们在第一个代码示例中注入了内置的myMod.run服务。但是,值得注意的是,$provide阶段的只能注入提供商config模块中的服务除外 - { {1}}和AUTO)。

例如,以下是不允许

$provide

$injector

有一个重要的例外:myMod.config(function(greeting) { // WON'T WORK -- greeting is an *instance* of a service. // Only providers for services can be injected in config blocks. }); s,因为它们无法更改,允许在myMod.config(function(greetingProvider) { // a-ok! }); 块内注入(这是它们与constant s的区别)。它们只能通过名称访问(不需要config后缀)。

每当您为服务定义提供程序时,该提供程序都会被命名为value,其中Provider是服务的名称。现在我们可以利用提供商的力量来做一些更复杂的事情!

serviceProvider

现在我们在名为service的提供商上有一个功能,我们可以用它来自定义myMod.provider('greeting', function() { var text = 'Hello, '; this.setText = function(value) { text = value; }; this.$get = function() { return function(name) { alert(text + name); }; }; }); myMod.config(function(greetingProvider) { greetingProvider.setText("Howdy there, "); }); myMod.run(function(greeting) { greeting('Ford Prefect'); }); ;我们可以在setText块中访问此提供程序以调用此方法并自定义服务。当我们最终运行我们的应用程序时,我们可以获取alert服务,并尝试查看我们的自定义生效。

由于这是一个更复杂的例子,这是一个工作示范:http://jsfiddle.net/BinaryMuse/9GjYg/

控制器($controller

可以注入控制器功能,但控制器本身不能注入其他东西。这是因为控制器不是通过提供商创建的。相反,有一个名为config的内置Angular服务负责设置您的控制器。当您致电greeting时,您实际上正在访问this service's provider,就像上一节一样。

例如,当您定义这样的控制器时:

$controller

你实际做的是:

myMod.controller(...)

稍后,当Angular需要创建控制器的实例时,它使用myMod.controller('MainController', function($scope) { // ... }); 服务(后者又使用myMod.config(function($controllerProvider) { $controllerProvider.register('MainController', function($scope) { // ... }); }); 来调用您的控制器函数,因此它也会注入其依赖项)。

过滤器和指令

$controller$injector的工作方式与filter完全相同; directive使用名为controller的服务及其提供商filter,而$filter使用名为$filterProvider的服务及其提供商directive。一些链接:

根据其他示例,$compile$compileProvider是配置这些服务的快捷方式。


tl;dr

因此,总而言之,任何使用myMod.filter 调用的函数都可以注入。这包括来自您的图表(但不限于):

  • 控制器
  • 指令
  • 工厂
  • 过滤
  • provider myMod.directive(将提供程序定义为对象时)
  • 提供者函数(将提供者定义为构造函数时)
  • 服务

提供商创建可以注入事物的新服务 。这包括:

  • 常数
  • 工厂
  • 提供商
  • 服务

也就是说,$injector.invoke$get 等内置服务可以注入,您可以使用这些服务来获取您使用这些方法定义的新过滤器和控制器(即使您定义的内容本身也不能注入事物中)。

除此之外,任何注入器调用的函数都可以注入任何提供者提供的服务 - 没有限制(除了此处列出的$controller$filter差异)。

答案 1 :(得分:13)

BinaryMuse在她关于提供商,工厂和服务的惊人答案中所做的一切都非常重要。

下面是一张我认为可以在视觉上说明她观点的图片:

AngularJS they are all just providers http://www.simplygoodcode.com/wp-content/uploads/2015/11/angularjs-provider-service-factory-highlight.png

答案 2 :(得分:7)

米歇尔的精彩回答。我只想指出可以注入指令。如果您有一个名为myThing的指令,则可以使用myThingDirective注入Here is a contrived example

以上示例不太实用,但是当您需要to decorate that directive时,注入指令的功能非常有用。