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

我很难理解Angular中的dependency injection。 所以我的问题是,任何人都可以解释哪些“types”,如控制器,工厂,提供者等我们可以注入到其他人,包括其他相同的“types”的实例吗?

我真正想要的是这个表格填写y / n。 对于具有相同行/列的单元格,这意味着将一个“types”的值注入另一个具有相同“types”

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

相反,只是在没有任何解释的情况下填写“是”和“否”,我会详细介绍一下。

[注意,在完成后添加:最终结果是…比我预期的要长。 在底部有一个博士,但我希望这certificate信息。]

[这个答案也被添加到AngularJS wiki中: 理解dependency injection ]


提供者( $provide

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

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

这里我们已经定义了一个名为greeting的服务的新提供者; 我们可以将一个名为greeting的variables注入到任何可注入的函数中(比如控制器,稍后会介绍),Angular会调用提供者的$get函数来返回一个新的服务实例。 在这种情况下,将被注入的东西是一个函数,该函数根据name采用name参数并alert sa消息。 我们可以这样使用它:

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

现在这是诀窍。 factoryservicevalue都是定义提供者各个部分的捷径,也就是说,他们提供了一种定义提供者的方法,而不必input所有的东西。 例如,您可以像这样编写完全相同的提供程序

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

理解这一点非常重要,所以我将重新翻译:AngularJS正在调用与我们上面编写的完全相同的代码$provide.provider版本)。 从字面上看,两个版本100%没有区别。 value工作原理是一样的 – 如果我们从$get函数返回的东西(也就是我们的factory函数)总是完全一样的话,我们可以使用value编写更less的代码。 例如,因为我们总是为我们的greeting服务返回相同的函数,所以我们也可以使用value来定义它:

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

再次,这与我们用来定义这个函数的其他两个方法是100%相同的 – 这只是一种保存一些input的方法。

现在你可能已经注意到了这个恼人的app.config(function($provide) { ... }) 。 由于定义了新的提供者(通过上面给出的任何方法)非常常见,AngularJS直接在模块对象上暴露$provider方法,以节省更多的input:

 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 (无双关语) $provide的代码实际创build我们服务的实例。 任何时候你写一个需要注入参数的函数,你都会看到注入器在工作。 每个AngularJS应用程序都有一个$injector ,它在应用程序第一次启动时被创build; 你可以通过注入$injector到任何可注入的函数来获得它(是的, $injector知道如何注入自己!)

一旦你有$injector ,你可以通过调用get服务的名字来获得一个已定义的服务的实例。 例如,

 var greeting = $injector.get('greeting'); greeting('Ford Prefect'); 

注入器也负责将服务注入function; 例如,你可以神奇的注入服务到你使用注入器的invoke方法的任何函数中;

 var myFunction = function(greeting) { greeting('Ford Prefect'); }; $injector.invoke(myFunction); 

值得注意的是注入器只会创build一个服务的实例。 然后它通过服务的名字caching提供者返回的任何东西; 下一次你要求服务,你会得到完全相同的对象。

所以,要回答你的问题,你可以将服务注入到任何使用$injector.invoke调用的函数中 。 这包括

  • 控制器定义function
  • 指令定义function
  • filter定义函数
  • 提供者的$get方法(也就是factory定义函数)

由于constant s和value s总是返回一个静态值,所以它们不会通过注入器被调用,因此你不能注入任何东西。

configuration提供程序

您可能想知道,为什么有人会在factoryvalue等方面比较容易地用provide方法build立一个完整的提供商。 答案是提供者允许很多configuration。 我们已经提到过,当你通过提供者(或Angular提供的任何捷径)创build一个服务时,你创build一个新的提供者来定义这个服务是如何构build的。 我没有提到的是,这些提供程序可以注入到应用程序的config部分,以便您可以与它们交互!

首先,Angular以两个阶段运行你的应用程序 – configrun阶段。 正如我们所看到的, config阶段是您可以根据需要设置任何提供程序的地方。 这也是设置指令,控制器,filter等的地方。 你可能会猜到, 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! }); 

有一个重要的exception: constant s,因为它们不能被改变,允许被注入config块(这是如何不同的value )。 他们只能通过他们的名字访问(不需要Provider后缀)。

无论何时为服务定义提供者,该提供者都将命名为serviceProvider ,其中serviceservice的名称。 现在我们可以利用提供商的力量做一些更复杂的东西!

 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

控制器function可以被注入,但是控制器本身不能注入其他的东西。 这是因为控制器不是通过提供者创build的。 相反,有一个名为$controller的内置Angular服务,负责设置你的控制器。 当你调用myMod.controller(...) ,你实际上是在访问这个服务的提供者 ,就像上一节一样。

例如,当你像这样定义一个控制器时:

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

你实际做的是这样的:

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

后来,当Angular需要创build你的控制器的实例时,它使用$controller服务(它反过来使用$injector来调用你的控制器函数,所以它也得到了它的dependency injection)。

filter和指令

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

  • $ filter: http : //docs.angularjs.org/api/ng.$filter
  • $ filterProvider: http ://docs.angularjs.org/api/ng.$filterProvider
  • $ compile: http : //docs.angularjs.org/api/ng.$compile
  • $ compileProvider: http ://docs.angularjs.org/api/ng.$compileProvider

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


TL;博士

所以,总结一下,使用$injector.invoke调用的任何函数都可以注入 。 这包括,从您的图表(但不限于):

  • 调节器
  • 指示
  • 过滤
  • 提供者$get (将提供者定义为对象时)
  • 提供者函数(将提供者定义为构造函数时)
  • 服务

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

  • 不变
  • 提供商
  • 服务

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

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

BinaryMuse在关于供应商,工厂和服务的惊人回答中所expression的观点都是非常重要的。

下面是我认为可以直观地说明她的观点的图像:

AngularJS他们都只是供应商wp-content/uploads/2015/11/angularjs-provider-service-factory-highlight.png

Michelle很好的回答。 我只想指出, 指令可以注入。 如果你有一个名为myThing的指令,你可以用myThingDirective注入它: 这是一个人为的例子 。

上面的例子不是很实用,但是当你想要装饰这个指令的时候,注入一个指令的能力是很有用的。