可以在Angular.js中将哪些“事物”注入到其他事物中?


142

我在理解Angular中的依赖注入方面有些困难。所以我的问题是,任何人都可以解释我们可以注入到其他“类型”(如控制器,工厂,提供者等)中吗,包括相同“类型”的其他实例?

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

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

Answers:


391

而是只在表中填入“是”和“否”而没有任何解释,我将进一步详细介绍。

[注意,在完成后添加:最终结果是……比我预期的要长得多。底部有一个tl; dr,但我希望这能为您提供参考。]

[此答案也已添加到AngularJS Wiki:了解依赖注入 ]


提供者($provide

$provide服务负责告诉Angular如何创建新的可注射事物;这些东西叫做服务。服务由称为提供者的东西定义,这是您在使用时创建的$provide。定义提供程序是通过服务上的provider方法完成的$provide,您可以$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');
  };
});

现在,这就是窍门。 factory,,servicevalue都是定义提供程序各个部分的快捷方式-也就是说,它们提供了一种定义提供程序的方法,而不必键入所有内容。例如,您可以这样编写完全相同的提供程序

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

了解这一点很重要,因此我将重新表述:在幕后,AngularJS正在调用与我们上面我们编写的代码$provide.provider版本)完全相同的代码。从字面上看,两个版本没有100%的差异。以相同的方式工作-如果我们从函数(即函数)返回的内容始终完全相同,则可以使用编写更少的代码。例如,由于我们总是为服务返回相同的函数,因此我们也可以使用它来定义它:value$getfactoryvaluegreetingvalue

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定义函数)

由于constants和values始终返回静态值,因此不会通过注入器调用它们,因此您无法注入任何东西。

配置提供者

你可能会奇怪为什么会有人费心去建立一个全面的供应商与provide方法,如果factoryvalue等都是容易得多。答案是提供程序允许大量配置。我们已经提到过,当您通过提供程序(或Angular提供的任何快捷方式)创建服务时,您将创建一个新的提供程序,用于定义该服务的构造方式。我没有提到的是,可以将这些提供程序注入到config应用程序的各个部分中,以便您与它们进行交互!

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

您可以使用myMod.configmyMod.run函数添加要在这些阶段中运行的其他代码-每个代码都需要一个函数在该特定阶段中运行。正如我们在第一部分中看到的那样,这些函数是可注入的-我们$provide在第一个代码示例中注入了内置服务。然而,值得一提的是,config阶段,只有供应商可以注射(与服务的除外AUTOmodule-- $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由于无法更改s,因此允许将它们注入到config块内部(这与values 有所不同)。仅通过名称即可访问它们(无需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

可以将控制器功能注入,但是不能将控制器本身注入其他东西。这是因为控制器不是通过提供程序创建的。而是有一个内置的Angular服务$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 可以被注入),并且您可以使用这些服务来获取使用这些方法定义的新过滤器和控制器(即使您定义的内容本身不能做到)注入东西)。

除此之外,任何注入程序调用的功能都可以注入任何提供商提供的服务-没有限制(除了此处列出的config和之间的run差异)。


6
哇!感谢您抽出宝贵时间回答如此详细的信息!我读过两次,我想我已经了解了很多。我们将研究它以及您今天稍后详细提供的链接。还有猫的+1。:)
user1527166

18
我遇到的最有用,最详尽的SO解答之一-谢谢!
戈德斯2013年

11
这个答案定义了新的令人敬畏的水平。照明的东西。
Ngure Nyaga 2013年

4
到目前为止,我遇到的关于AngularJS的最佳资源。谢谢。
code90 2013年

5
从字面上看,我见过最好的AngularJS文档。要走的路!
伊恩·邓肯

13

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

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

AngularJS都是提供者
(来源:simplygoodcode.com


7

米歇尔的好答案。我只想指出可以注入指令。如果您有一个名为的指令myThing,则可以将其注入myThingDirective这是一个人为的示例

上面的示例不是很实际,但是当您要装饰该指令时,注入指令的功能很有用。


从Angular 1.4开始,装饰该指令的第二个示例似乎不起作用。(请参见Juan Juan Biscaia的评论)
Vadorequest 2015年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.