在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'); }; });
现在这是诀窍。 factory
, service
和value
都是定义提供者各个部分的捷径,也就是说,他们提供了一种定义提供者的方法,而不必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提供程序
您可能想知道,为什么有人会在factory
, value
等方面比较容易地用provide
方法build立一个完整的提供商。 答案是提供者允许很多configuration。 我们已经提到过,当你通过提供者(或Angular提供的任何捷径)创build一个服务时,你创build一个新的提供者来定义这个服务是如何构build的。 我没有提到的是,这些提供程序可以注入到应用程序的config
部分,以便您可以与它们交互!
首先,Angular以两个阶段运行你的应用程序 – config
和run
阶段。 正如我们所看到的, config
阶段是您可以根据需要设置任何提供程序的地方。 这也是设置指令,控制器,filter等的地方。 你可能会猜到, run
阶段是Angular实际编译DOM并启动你的应用程序的地方。
您可以使用myMod.config
和myMod.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
,其中service
是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
)
控制器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和指令
filter
和directive
工作方式与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.filter
和myMod.directive
是configuration这些服务的快捷方式。
所以,总结一下,使用$injector.invoke
调用的任何函数都可以注入 。 这包括,从您的图表(但不限于):
- 调节器
- 指示
- 厂
- 过滤
- 提供者
$get
(将提供者定义为对象时) - 提供者函数(将提供者定义为构造函数时)
- 服务
提供者创build可以注入事物的新服务。 这包括:
- 不变
- 厂
- 提供商
- 服务
- 值
也就是说,像$controller
和$filter
这样的内置服务可以被注入,你可以使用这些服务来获得你用这些方法定义的新的filter和控制器(即使你定义的东西本身不是,能被注入的东西)。
除此之外,任何注入器调用的函数都可以使用任何提供者提供的服务注入 – 没有限制(除了此处列出的config
和run
差异)。
BinaryMuse在关于供应商,工厂和服务的惊人回答中所expression的观点都是非常重要的。
下面是我认为可以直观地说明她的观点的图像:
AngularJS他们都只是供应商wp-content/uploads/2015/11/angularjs-provider-service-factory-highlight.png
Michelle很好的回答。 我只想指出, 指令可以注入。 如果你有一个名为myThing
的指令,你可以用myThingDirective
注入它: 这是一个人为的例子 。
上面的例子不是很实用,但是当你想要装饰这个指令的时候,注入一个指令的能力是很有用的。