Quais "coisas" podem ser injetadas em outras pessoas no Angular.js?

142

Estou tendo um pouco de dificuldade para entender a injeção de dependência em angular. Então, minha pergunta é: alguém pode explicar quais dos "tipos", como Controller, Factory, Provider, etc, podemos injetar em outros, incluindo outras instâncias do mesmo "tipo"?

Na verdade, o que estou procurando é esta tabela preenchida com y / n. Para células com a mesma linha / coluna, isso significa injetar o valor de um "tipo" em outro com o mesmo "tipo"

+----------------+----------+------------+-----------+---------+--------+----------+---------+-------+
| Can we inject? | Constant | Controller | Directive | Factory | Filter | Provider | Service | Value |
+----------------+----------+------------+-----------+---------+--------+----------+---------+-------+
| Constant       |          |            |           |         |        |          |         |       |
| Controller     |          |            |           |         |        |          |         |       |
| Directive      |          |            |           |         |        |          |         |       |
| Factory        |          |            |           |         |        |          |         |       |
| Filter         |          |            |           |         |        |          |         |       |
| Provider       |          |            |           |         |        |          |         |       |
| Service        |          |            |           |         |        |          |         |       |
| Value          |          |            |           |         |        |          |         |       |
+----------------+----------+------------+-----------+---------+--------+----------+---------+-------+
user1527166
fonte
aqui está a resposta docs.angularjs.org/guide/providers#conclusion
JFouad

Respostas:

391

Em vez disso, basta preencher a tabela com "sim" e "não" sem explicação, irei detalhar um pouco mais.

[Nota, adicionada após o término: isso acabou sendo ... um pouco mais longo do que eu esperava. Há um tl; dr na parte inferior, mas espero que isso seja informativo.]

[Esta resposta também foi adicionada ao wiki do AngularJS: Entendendo a injeção de dependência ]


O Fornecedor ( $provide)

O $provideserviço é responsável por informar à Angular como criar novas coisas injetáveis; essas coisas são chamadas de serviços . Os serviços são definidos por coisas chamadas provedores , que é o que você cria quando usa $provide. A definição de um provedor é feita pelo providermétodo no $provideserviço, e você pode se apossar do $provideserviço solicitando que ele seja injetado na configfunção de um aplicativo . Um exemplo pode ser algo como isto:

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

Aqui nós definimos um novo provedor para um serviço chamado greeting; podemos injetar uma variável nomeada greetingem qualquer função injetável (como controladores, mais sobre isso posteriormente) e o Angular chamará a $getfunção do provedor para retornar uma nova instância do serviço. Nesse caso, o que será injetado é uma função que recebe um nameparâmetro e uma alertmensagem com base no nome. Podemos usá-lo assim:

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

Agora, aqui está o truque. factory,, servicee valuetodos são apenas atalhos para definir várias partes de um provedor - ou seja, eles fornecem um meio de definir um provedor sem precisar digitar tudo isso. Por exemplo, você pode escrever exatamente o mesmo provedor da seguinte maneira:

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

É importante entender, então vou reformular: sob o capô, o AngularJS está chamando exatamente o mesmo código que escrevemos acima (a $provide.providerversão) para nós. Não há, literalmente, 100% de diferença nas duas versões. valuefunciona da mesma maneira - se o que quer que retornássemos da nossa $getfunção (também conhecida como nossa factoryfunção) é sempre exatamente o mesmo, podemos escrever ainda menos código usando value. Por exemplo, como sempre retornamos a mesma função para nosso greetingserviço, também podemos valuedefini-la:

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

Novamente, isso é 100% idêntico aos outros dois métodos que usamos para definir essa função - é apenas uma maneira de salvar algumas digitações.

Agora você provavelmente notou essa app.config(function($provide) { ... })coisa chata que tenho usado. Como definir novos provedores (através de qualquer um dos métodos acima) é tão comum, o AngularJS expõe os $providermétodos diretamente no objeto do módulo, para economizar ainda mais digitação:

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

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

Todos eles fazem o mesmo que as app.config(...)versões mais detalhadas que usamos anteriormente.

O injetável que pulei até agora é constant. Por enquanto, é fácil dizer que funciona da mesma maneira value. Veremos que há uma diferença mais tarde.

Para revisar , todos esses trechos de código estão fazendo exatamente a mesma coisa:

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

O Injetor ( $injector)

O injetor é responsável por realmente criar instâncias de nossos serviços usando o código que fornecemos via $provide(sem trocadilhos). Sempre que você escreve uma função que recebe argumentos injetados, vê o injetor funcionando. Cada aplicativo AngularJS possui um $injectorque é criado quando o aplicativo é iniciado pela primeira vez; você pode controlá-lo injetando $injectorqualquer função injetável (sim, $injectorsabe como se injetar!)

Depois de ter $injector, você pode obter uma instância de um serviço definido chamando get-o com o nome do serviço. Por exemplo,

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

O injetor também é responsável por injetar serviços nas funções; por exemplo, você pode injetar serviços magicamente em qualquer função que tenha usando o invokemétodo do injetor ;

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

Vale a pena notar que o injetor criará apenas uma instância de um serviço uma vez . Em seguida, ele armazena em cache o que o provedor retornar pelo nome do serviço; Na próxima vez em que você solicitar o serviço, você obterá exatamente o mesmo objeto.

Portanto, para responder sua pergunta, você pode injetar serviços em qualquer função chamada$injector.invoke . Isso inclui

  • funções de definição do controlador
  • funções de definição de diretiva
  • funções de definição de filtro
  • os $getmétodos dos provedores (também conhecidos como factoryfunções de definição)

Como constants e values sempre retornam um valor estático, eles não são invocados pelo injetor e, portanto, você não pode injetá-los com nada.

Configurando Provedores

Você pode estar se perguntando por que alguém iria incomodar a criação de um provedor de pleno direito com o providemétodo se factory, value, etc, são muito mais fácil. A resposta é que os provedores permitem muita configuração. Já mencionamos que, ao criar um serviço por meio do provedor (ou qualquer um dos atalhos fornecidos pelo Angular), você cria um novo provedor que define como esse serviço é construído. O que eu não mencionei é que esses provedores podem ser injetados emconfig seções do seu aplicativo para que você possa interagir com eles!

Primeiro, o Angular executa seu aplicativo em duas fases - as fases confige run. A configfase, como vimos, é onde você pode configurar qualquer provedor, conforme necessário. É também aqui que diretivas, controladores, filtros e similares são configurados. A runfase, como você pode imaginar, é onde o Angular realmente compila seu DOM e inicia seu aplicativo.

Você pode adicionar código adicional a ser executado nessas fases com as funções myMod.confige myMod.run- cada uma delas executa uma função para executar durante essa fase específica. Como vimos na primeira seção, essas funções são injetáveis ​​- injetamos o $provideserviço interno em nosso primeiro exemplo de código. No entanto, o que vale a pena notar é que, durante a configfase, apenas os provedores podem ser injetados (com exceção dos serviços no AUTOmódulo-- $providee $injector).

Por exemplo, o seguinte não é permitido :

myMod.config(function(greeting) {
  // WON'T WORK -- greeting is an *instance* of a service.
  // Only providers for services can be injected in config blocks.
});

O que você faz tem acesso a quaisquer provedores de serviços que você fez:

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

Há uma exceção importante: constants, uma vez que não podem ser alterados, podem ser injetados dentro de configblocos (é assim que diferem de values). Eles são acessados ​​apenas pelo nome (não é Providernecessário sufixo).

Sempre que você define um provedor para um serviço, esse provedor é nomeado serviceProvider, onde serviceé o nome do serviço. Agora podemos usar o poder dos provedores para fazer coisas mais complicadas!

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

Agora, temos uma função em nosso provedor chamada setTextque podemos usar para personalizar nossa alert; podemos obter acesso a esse provedor em um configbloco para chamar esse método e personalizar o serviço. Quando finalmente executamos nosso aplicativo, podemos pegar o greetingserviço e testá-lo para ver se nossa personalização entrou em vigor.

Como este é um exemplo mais complexo, aqui está uma demonstração de trabalho: http://jsfiddle.net/BinaryMuse/9GjYg/

Controladores ( $controller)

As funções do controlador podem ser injetadas, mas os próprios controladores não podem ser injetados em outras coisas. Isso ocorre porque os controladores não são criados por meio do provedor. Em vez disso, existe um serviço Angular interno chamado $controllerresponsável pela configuração de seus controladores. Quando você liga myMod.controller(...), está realmente acessando o provedor deste serviço , assim como na última seção.

Por exemplo, quando você define um controlador como este:

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

O que você está realmente fazendo é o seguinte:

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

Posteriormente, quando o Angular precisar criar uma instância do seu controlador, ele usará o $controllerserviço (que por sua vez usa o $injectorpara chamar a função do controlador para que também seja injetada suas dependências).

Filtros e Diretivas

filtere directivefunciona exatamente da mesma maneira que controller; filterusa um serviço chamado $filtere seu provedor $filterProvider, enquanto directiveusa um serviço chamado $compilee seu provedor $compileProvider. Alguns links:

Conforme os outros exemplos, myMod.filtere myMod.directivesão atalhos para configurar esses serviços.


tl; dr

Portanto, para resumir, qualquer função que seja chamada $injector.invoke pode ser injetada . Isso inclui, no seu gráfico (mas não se limita a):

  • controlador
  • diretiva
  • fábrica
  • filtro
  • provedor $get(ao definir provedor como um objeto)
  • função de provedor (ao definir provedor como uma função de construtor)
  • serviço

O provedor cria novos serviços que podem ser injetados nas coisas . Isso inclui:

  • constante
  • fábrica
  • fornecedor
  • serviço
  • valor

Dito isto, serviços internos gostam $controllere $filter podem ser injetados, e você pode usá- los para se apossar dos novos filtros e controladores que você definiu com esses métodos (mesmo que as coisas que você definiu não possam, por si só, ser injetado nas coisas).

Para além de que, qualquer função invocada-injector pode ser injectado com qualquer serviço fornecido pelo provedor - não há nenhuma restrição (excepto o confige rundiferenças aqui listados).

Michelle Tilley
fonte
6
Uau! obrigado por responder com tanto detalhe! Eu li isso duas vezes e acho que entendi um pouco. Vou estudá-lo e os links que você deu em detalhes ainda hoje. E mais um +1 para o gato. :)
user1527166
18
Uma das respostas SO mais úteis e detalhadas que já encontrei - obrigado!
Godders 6/06/2013
11
Esta resposta define um novo nível de impressionante. Material iluminador.
Ngure Nyaga
4
De longe, o melhor recurso que encontrei para o AngularJS. Obrigado.
code90
5
Literalmente, a melhor parte da documentação do AngularJS que eu já vi. Caminho a percorrer!
Iain Duncan #
13

O ponto que a BinaryMuse faz em sua resposta incrível sobre fornecedores, fábricas e serviços é a mesma coisa extremamente importante.

Abaixo está uma imagem que acho que pode ilustrar visualmente o argumento dela:

AngularJS são todos apenas provedores
(fonte: simplygoodcode.com )

Luis Perez
fonte
7

Ótima resposta de Michelle. Eu só quero ressaltar que diretivas podem ser injetadas. Se você tiver uma diretiva denominada myThing, poderá injetá-la com myThingDirective: Aqui está um exemplo artificial .

O exemplo acima não é muito prático, no entanto, a capacidade de injetar uma diretiva é útil quando você deseja decorá-la .

Gil Birman
fonte
Parece que o segundo exemplo para decorar essa diretiva não funciona desde o Angular 1.4. (veja o comentário de Juan Biscaia lá)
Vadorequest 25/11