在 Angular.js 中有什么“东西”可以注入到其他人体内?

我很难理解角度依赖注入的意思。所以我的问题是,有人能解释哪些“类型”,如控制器,工厂,提供者等,我们可以注入到其他人,包括其他相同的“类型”的实例?

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

+----------------+----------+------------+-----------+---------+--------+----------+---------+-------+
| Can we inject? | Constant | Controller | Directive | Factory | Filter | Provider | Service | Value |
+----------------+----------+------------+-----------+---------+--------+----------+---------+-------+
| Constant       |          |            |           |         |        |          |         |       |
| Controller     |          |            |           |         |        |          |         |       |
| Directive      |          |            |           |         |        |          |         |       |
| Factory        |          |            |           |         |        |          |         |       |
| Filter         |          |            |           |         |        |          |         |       |
| Provider       |          |            |           |         |        |          |         |       |
| Service        |          |            |           |         |        |          |         |       |
| Value          |          |            |           |         |        |          |         |       |
+----------------+----------+------------+-----------+---------+--------+----------+---------+-------+
37473 次浏览

相反,只是在表格中填上“是”和“不是”,没有任何解释,我将进入更多的细节。

[注意,完成后添加: 这结束了... ... 比我预期的要长得多。底部有一个 tl; dr,但我希望这能提供信息。]

[这个答案也被添加到 AngularJS wiki: 了解依赖注入]


提供者($provide)

$provide服务负责告诉 Angular 如何创建新的可注射的东西; 这些东西被称为 服务。服务由称为 供应商的东西定义,这就是您在使用 $provide时创建的东西。定义提供程序是通过 $provide服务上的 provider方法完成的,您可以通过请求将 $provide服务注入到应用程序的 config函数中来获取它。一个例子可能是这样的:

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版本) 为了 us。毫不夸张地说,这两个版本之间没有100% 的区别。value的工作原理也是一样的——如果我们从 $get函数(也就是我们的 factory函数)返回的代码总是完全相同的,那么我们可以使用 value编写更少的代码。例如,因为我们总是为 greeting服务返回相同的函数,所以我们也可以使用 value来定义它:

app.config(function($provide) {
$provide.value('greeting', function(name) {
alert("Hello, " + name);
});
});

同样,这与我们用来定义这个函数的其他两个方法100% 相同——这只是一种节省一些输入的方法。

现在你可能注意到了我一直在用的这个烦人的 app.config(function($provide) { ... })。由于定义新的提供程序(通过上面给定方法的 任何)是非常常见的,AngularJS 将 $provider方法直接暴露在模块对象上,以节省更多的输入:

var myMod = angular.module('myModule', []);


myMod.provider("greeting", ...);
myMod.factory("greeting", ...);
myMod.value("greeting", ...);

它们都与我们以前使用的更详细的 app.config(...)版本做同样的事情。

到目前为止,我跳过的一种注射剂是 constant。现在,很容易说它的工作原理就像 value一样。稍后我们将看到一个不同之处。

为了回顾 所有这些代码正在做 一模一样相同的事情:

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);
});

注射器($injector)

注入器负责使用我们通过 $provide提供的代码实际创建我们的服务实例(无双关意思)。任何时候你写一个接受注入参数的函数,你都会看到注入器在工作。每个 AngularJS 应用程序都有一个在应用程序首次启动时创建的 $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方法(即 factory定义函数)

由于 constantvalue总是返回一个静态值,因此不能通过注入器调用它们,因此不能向它们注入任何内容。

配置提供程序

您可能想知道,如果 factoryvalue等更简单的话,为什么还有人愿意使用 provide方法设置一个成熟的提供程序。答案是提供者允许大量配置。我们已经提到过,当您通过提供者创建服务(或者 Angular 提供的任何快捷方式)时,您将创建一个新的提供者,该提供者定义如何构建该服务。我提到的是,这些提供程序可以被注入到应用程序的 config部分,因此您可以与它们交互!

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

您可以添加额外的代码,使用 myMod.configmyMod.run函数在这些阶段中运行——每个函数在特定阶段中运行一个函数。正如我们在第一部分中看到的,这些函数是可注入的——我们在第一个代码示例中注入了内置的 $provide服务。然而,值得注意的是 config阶段,只能注入提供者(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.
});

你的 可以访问的是你所提供服务的任何 供应商:

myMod.config(function(greetingProvider) {
// a-ok!
});

有一个重要的例外: 由于 constant不能更改,因此允许在 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; 我们可以在 config块中访问这个提供程序来调用这个方法并定制服务。当我们最终运行我们的应用程序时,我们可以抓取 greeting服务,并尝试它来看看我们的定制生效了。

因为这是一个更复杂的示例,所以下面是一个可行的演示: http://jsfiddle.net/BinaryMuse/9GjYg/

控制器($controller)

控制器函数可以被注入,但控制器本身不能被注入到其他事物中。这是因为控制器不是通过提供程序创建的。相反,有一个内置的角度服务称为 $controller,是负责设置您的控制器。当您调用 myMod.controller(...)时,实际上是在访问 这项服务的提供者,就像在上一节中一样。

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

myMod.controller('MainController', function($scope) {
// ...
});

你实际上是这样做的:

myMod.config(function($controllerProvider) {
$controllerProvider.register('MainController', function($scope) {
// ...
});
});

稍后,当 Angular 需要创建控制器的一个实例时,它使用 $controller服务(反过来使用 $injector来调用控制器函数,这样它也可以注入它的依赖项)。

过滤器和指令

filterdirective的工作方式与 controller完全相同; filter使用称为 $filter的服务及其提供商 $filterProvider,而 directive使用称为 $compile的服务及其提供商 $compileProvider。有关连结:

根据其他示例,myMod.filtermyMod.directive是配置这些服务的快捷方式。


tl;dr

因此,总结一下,任何用 $injector.invoke 可以注射到调用的函数。这包括,从你的图表(但不限于) :

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

提供商创建新的服务 可以注射到东西里,其中包括:

  • 不变
  • 工厂
  • 提供者
  • 服务
  • 价值

也就是说,像 $controller$filter 可以这样的内置服务被注入,你可以通过 使用这些服务来获取你用这些方法定义的新的过滤器和控制器(即使你定义的东西本身不能被注入)。

除此之外,任何注入器调用的函数都可以注入任何提供者提供的服务——没有任何限制(除了本文列出的 configrun之外)。

米歇尔的回答很棒。我只是想指出,指令可以注射。如果您有一个名为 myThing的指令,那么可以用 myThingDirective: 这里有一个人为的例子注入它。

上面的示例不是很实用,但是当您需要 来装饰这个指令时,注入指令的能力是非常有用的。

BinaryMuse 在她关于提供商、工厂和服务都是同一件事情的惊人回答中提出的观点是非常重要的。

下面的图片可以直观地说明她的观点:

AngularJS they are all just providers
(来源: Simplygoodcode.com)