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

use*_*166 140 dependency-injection angularjs

我很难理解Angular中的依赖注入.所以我的问题是,任何人都可以解释哪些"类型",如控制器,工厂,提供商等,我们可以注入其他类型,包括相同"类型"的其他实例?

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

+----------------+----------+------------+-----------+---------+--------+----------+---------+-------+
| Can we inject? | Constant | Controller | Directive | Factory | Filter | Provider | Service | Value |
+----------------+----------+------------+-----------+---------+--------+----------+---------+-------+
| Constant       |          |            |           |         |        |          |         |       |
| Controller     |          |            |           |         |        |          |         |       |
| Directive      |          |            |           |         |        |          |         |       |
| Factory        |          |            |           |         |        |          |         |       |
| Filter         |          |            |           |         |        |          |         |       |
| Provider       |          |            |           |         |        |          |         |       |
| Service        |          |            |           |         |        |          |         |       |
| Value          |          |            |           |         |        |          |         |       |
+----------------+----------+------------+-----------+---------+--------+----------+---------+-------+
Run Code Online (Sandbox Code Playgroud)

Mic*_*ley 389

而只是填写表中的"是"和"否"没有解释,我会详细介绍一下.

[注意,在完成后添加:这最终......比我预期的要长得多.底部有一个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);
      };
    };
  });
});
Run Code Online (Sandbox Code Playgroud)

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

app.controller('MainController', function($scope, greeting) {
  $scope.onClick = function() {
    greeting('Ford Prefect');
  };
});
Run Code Online (Sandbox Code Playgroud)

现在这就是诀窍. factory,servicevalue,都只是定义提供程序的各个部分的快捷方式 - 也就是说,它们提供了一种定义提供程序的方法,而无需键入所有这些东西.例如,您可以像这样编写完全相同的提供程序:

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

了解这一点很重要,所以我会改写:引擎盖下,AngularJS呼唤着完全相同的代码,我们在上面(在写$provide.provider版)我们.从字面上看,这两个版本没有差别.value以同样的方式工作 - 如果我们从$get函数(也就是我们的factory函数)返回的内容总是完全相同,我们可以使用更少的代码编写value.例如,因为我们总是为我们的greeting服务返回相同的函数,所以我们也可以使用value它来定义它:

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

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

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

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

myMod.provider("greeting", ...);
myMod.factory("greeting", ...);
myMod.value("greeting", ...);
Run Code Online (Sandbox Code Playgroud)

这些都与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);
});
Run Code Online (Sandbox Code Playgroud)

注射器($injector)

注入器负责使用我们提供的代码实际创建我们服务的实例$provide(没有双关语意).每当你编写一个带注入参数的函数时,你就会看到注入器工作正常.每个AngularJS应用程序都有一个$injector在应用程序首次启动时创建的应用程序; 你可以通过注入$injector任何注射功能来获得它(是的,$injector知道如何自我注射!)

完成后$injector,您可以通过使用服务get名称调用已获定义服务的实例.例如,

var greeting = $injector.get('greeting');
greeting('Ford Prefect');
Run Code Online (Sandbox Code Playgroud)

注入器还负责将服务注入功能; 例如,您可以使用注入器的invoke方法将服务神奇地注入到您拥有的任何函数中;

var myFunction = function(greeting) {
  greeting('Ford Prefect');
};
$injector.invoke(myFunction);
Run Code Online (Sandbox Code Playgroud)

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

因此,要回答您的问题,您可以将服务注入任何调用的函数$injector.invoke.这包括

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

由于constants和values总是返回一个静态值,因此它们不会通过注入器调用,因此您无法注入任何东西.

配置提供商

你可能想知道为什么有人会费心去设置一个成熟的提供者,provide如果factory,value等等更容易.答案是提供商允许大量配置.我们已经提到过,当您通过提供程序(或Angular为您提供的任何快捷方式)创建服务时,您将创建一个新的提供程序,用于定义该服务的构造方式.我没有提到的是,这些提供程序可以注入config应用程序的各个部分,以便您可以与它们进行交互!

首先,Angular分两个阶段运行您的应用程序 - 阶段configrun阶段.config正如我们所见,这个阶段是您可以根据需要设置任何提供商的地方.这也是设置指令,控制器,过滤器等的地方.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.
});
Run Code Online (Sandbox Code Playgroud)

什么,你可以访问任何供应商为您所做的服务:

myMod.config(function(greetingProvider) {
  // a-ok!
});
Run Code Online (Sandbox Code Playgroud)

有一个重要的例外:constants,因为它们无法更改,所以允许在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');
});
Run Code Online (Sandbox Code Playgroud)

现在我们在我们的提供商上有一个函数setText,我们可以使用它来定制我们的alert; 我们可以在config块中访问此提供程序以调用此方法并自定义服务.当我们最终运行我们的应用程序时,我们可以获取greeting服务,并尝试查看我们的自定义生效.

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

控制器($controller)

控制器功能可以注入,但控制器本身不能注入其他东西.那是因为控制器不是通过提供商创建的.相反,有一个内置的Angular服务$controller,负责设置您的控制器.当你打电话时myMod.controller(...),你实际上正在访问这个服务的提供者,就像在上一节中一样.

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

myMod.controller('MainController', function($scope) {
  // ...
});
Run Code Online (Sandbox Code Playgroud)

你实际在做的是:

myMod.config(function($controllerProvider) {
  $controllerProvider.register('MainController', function($scope) {
    // ...
  });
});
Run Code Online (Sandbox Code Playgroud)

之后,当Angular需要创建控制器的实例时,它会使用该$controller服务(该服务又使用它$injector来调用您的控制器函数,因此它也会注入其依赖项).

过滤器和指令

filter并且directive工作方式完全相同controller; filter使用被调用的服务$filter及其提供者$filterProvider,同时directive使用被调用的服务$compile及其提供者$compileProvider.一些链接:

按照其他的例子,myMod.filtermyMod.directive有快捷键配置这些服务.


TL;博士

因此,总而言之,$injector.invoke 可以注入任何被调用的函数.这包括,从您的图表(但不限于):

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

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

  • 不变
  • 提供商
  • 服务

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

除此之外,任何喷射器调用的功能可以用任何提供者提供的服务被注入-没有限制(除了其他configrun本文所列出的差异).

  • 我遇到的最有用和最详细的答案之一 - 谢谢! (18认同)
  • 这个答案定义了一个令人敬畏的新水平.照亮的东西. (11认同)
  • 哇!感谢您抽出时间详细解答!我已经读了两遍,我想我已经理解了很多.今天晚些时候要研究它和你提供的链接.另外还有+1的猫.:) (6认同)
  • 字面上我见过的最好的AngularJS文档.路要走! (5认同)
  • 到目前为止,我为AngularJS找到了最好的资源.谢谢. (4认同)

Lui*_*rez 13

BinaryMuse在她关于提供商,工厂和服务的惊人答案中提出的观点都非常重要.

以下是我认为可以在视觉上说明她的观点的图像:

AngularJS他们都只是提供者http://www.simplygoodcode.com/wp-content/uploads/2015/11/angularjs-provider-service-factory-highlight.png


Gil*_*man 7

米歇尔给出了很好的答案.我只想指出可以注入指令.如果你有一个命名的指令myThing,你可以用它注入myThingDirective:这是一个人为的例子.

上面的示例不太实用,但是当您想要修饰该指令时,注入指令的功能非常有用.