Quali "cose" possono essere iniettate negli altri in Angular.js?


142

Sto facendo fatica a capire l'iniezione di dipendenza in angolare. Quindi la mia domanda è: qualcuno può spiegare quale dei "tipi", come Controller, Factory, Provider, ecc., Possiamo iniettare in altri, inclusi altri casi dello stesso "tipo"?

Quello che sto effettivamente cercando è questa tabella piena di y / n. Per le celle con la stessa riga / colonna, ciò significa iniettare il valore di un "tipo" in un altro con lo stesso "tipo"

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

Risposte:


391

Piuttosto che riempi semplicemente la tabella con "sì" e "no" senza spiegazioni, entrerò in qualche dettaglio in più.

[Nota, aggiunto dopo aver terminato: questo ha finito per essere ... un po 'più lungo di quanto mi aspettassi. C'è un tl; dr in fondo, ma spero che questo si dimostri informativo.]

[Questa risposta è stata aggiunta anche al wiki di AngularJS: comprensione dell'iniezione delle dipendenze ]


The Provider ( $provide)

Il $provideservizio è responsabile di dire ad Angular come creare nuove cose iniettabili; queste cose sono chiamate servizi . I servizi sono definiti da cose chiamate provider , che è ciò che stai creando quando lo usi $provide. La definizione di un provider viene effettuata tramite il providermetodo sul $provideservizio e puoi ottenere il $provideservizio chiedendo che venga iniettato nella configfunzione di un'applicazione . Un esempio potrebbe essere qualcosa del genere:

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

Qui abbiamo definito un nuovo provider per un servizio chiamato greeting; possiamo iniettare una variabile nominata greetingin qualsiasi funzione iniettabile (come controller, più avanti) e Angular chiamerà la $getfunzione del provider per restituire una nuova istanza del servizio. In questo caso, la cosa che verrà iniettata è una funzione che accetta un nameparametro e alertun messaggio basato sul nome. Potremmo usarlo in questo modo:

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

Ora ecco il trucco. factory, servicee valuesono solo delle scorciatoie per definire le varie parti di un provider, ovvero forniscono un mezzo per definire un provider senza dover digitare tutte quelle cose. Ad esempio, potresti scrivere esattamente lo stesso provider in questo modo:

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

È importante capire, quindi riformulerò: sotto il cofano, AngularJS sta chiamando lo stesso identico codice che abbiamo scritto sopra (la $provide.providerversione) per noi. Non c'è letteralmente il 100% di differenza nelle due versioni. valuefunziona allo stesso modo - se qualunque cosa torniamo dalla nostra $getfunzione (ovvero la nostra factoryfunzione) è sempre esattamente la stessa, possiamo scrivere ancora meno codice usando value. Ad esempio, poiché restituiamo sempre la stessa funzione per il nostro greetingservizio, possiamo anche usarla valueper definirla:

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

Ancora una volta, questo è identico al 100% agli altri due metodi che abbiamo usato per definire questa funzione: è solo un modo per salvare un po 'di battitura.

Ora probabilmente hai notato questa app.config(function($provide) { ... })cosa fastidiosa che ho usato. Poiché la definizione di nuovi provider (tramite uno dei metodi indicati sopra) è così comune, AngularJS espone i $providermetodi direttamente sull'oggetto modulo, per risparmiare ancora di più digitando:

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

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

Tutti fanno la stessa cosa delle app.config(...)versioni più dettagliate che abbiamo usato in precedenza.

Quello iniettabile che ho saltato finora è constant. Per ora, è abbastanza facile dire che funziona proprio come value. Vedremo che c'è una differenza più tardi.

Per rivedere , tutti questi pezzi di codice stanno facendo esattamente la stessa cosa:

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

The Injector ( $injector)

L'iniettore è responsabile della creazione effettiva delle istanze dei nostri servizi utilizzando il codice fornito tramite $provide(nessun gioco di parole previsto). Ogni volta che scrivi una funzione che accetta argomenti iniettati, vedi l'iniettore al lavoro. Ogni applicazione AngularJS ha un singolo $injectorche viene creato al primo avvio dell'applicazione; puoi ottenerlo iniettando $injectorqualsiasi funzione iniettabile (sì, $injectorsa come iniettarsi!)

Una volta che hai $injector, è possibile ottenere un'istanza di un servizio definito chiamando getsu di esso con il nome del servizio. Per esempio,

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

L'iniettore è anche responsabile dell'iniezione di servizi nelle funzioni; per esempio, puoi iniettare magicamente servizi in qualsiasi funzione tu abbia usando il invokemetodo dell'iniettore ;

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

Vale la pena notare che l'iniettore creerà un'istanza di un servizio una sola volta . Quindi memorizza nella cache qualunque cosa il provider restituisca con il nome del servizio; la prossima volta che chiedi il servizio, otterrai effettivamente lo stesso oggetto.

Quindi, per rispondere alla tua domanda, puoi inserire servizi in qualsiasi funzione venga chiamata$injector.invoke . Ciò comprende

  • funzioni di definizione del controller
  • funzioni di definizione della direttiva
  • funzioni di definizione del filtro
  • i $getmetodi dei fornitori (ovvero le factoryfunzioni di definizione)

Poiché constants e values restituiscono sempre un valore statico, non vengono invocati tramite l'iniettore, quindi non è possibile iniettarli con nulla.

Configurare i provider

Ci si potrebbe chiedere perché qualcuno dovrebbe preoccuparsi di creare un provider di vera e propria con il providemetodo se factory, value, ecc, sono molto più facile. La risposta è che i provider consentono molta configurazione. Abbiamo già detto che quando si crea un servizio tramite il provider (o una delle scorciatoie fornite da Angular), si crea un nuovo provider che definisce come viene costruito quel servizio. Quello che non ho menzionato è che questi provider possono essere iniettati in configsezioni della tua applicazione in modo da poter interagire con loro!

In primo luogo, angolare esegue l'applicazione in due fasi - la confige runfasi. La configfase, come abbiamo visto, è quella in cui è possibile configurare qualsiasi provider, se necessario. Questo è anche il punto in cui vengono impostate direttive, controller, filtri e simili. La runfase, come puoi immaginare, è dove Angular compila effettivamente il tuo DOM e avvia la tua app.

È possibile aggiungere codice aggiuntivo da eseguire in queste fasi con le funzioni myMod.confige myMod.run- ognuna accetta una funzione da eseguire durante quella fase specifica. Come abbiamo visto nella prima sezione, queste funzioni sono iniettabili: abbiamo iniettato il servizio integrato $providenel nostro primo esempio di codice. Tuttavia, ciò che vale la pena notare è che durante la configfase, solo i provider possono essere iniettati (ad eccezione dei servizi nel AUTOmodulo-- $providee $injector).

Ad esempio, è la seguente non è consentito :

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

Quello che fare avere accesso a sono dei fornitori per i servizi che hai fatto:

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

C'è un'eccezione importante: le constants, poiché non possono essere modificate, possono essere iniettate all'interno di configblocchi (è così che differiscono da values). Sono accessibili solo con il loro nome (nessun Providersuffisso necessario).

Ogni volta che si definisce un provider per un servizio, quel provider viene nominato serviceProvider, dove si servicetrova il nome del servizio. Ora possiamo usare il potere dei provider per fare alcune cose più complicate!

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

Ora abbiamo una funzione sul nostro provider chiamata setTextche possiamo usare per personalizzare la nostra alert; possiamo ottenere l'accesso a questo provider in un configblocco per chiamare questo metodo e personalizzare il servizio. Quando finalmente eseguiamo la nostra app, possiamo prendere il greetingservizio e provarlo per vedere che la nostra personalizzazione ha avuto effetto.

Poiché questo è un esempio più complesso, ecco una dimostrazione funzionante: http://jsfiddle.net/BinaryMuse/9GjYg/

Controller ( $controller)

Le funzioni del controller possono essere iniettate, ma i controller stessi non possono essere iniettati in altre cose. Questo perché i controller non vengono creati tramite il provider. Al contrario, esiste un servizio angolare integrato chiamato $controllerresponsabile della configurazione dei controller. Quando chiami myMod.controller(...), stai effettivamente accedendo al provider di questo servizio , proprio come nell'ultima sezione.

Ad esempio, quando si definisce un controller in questo modo:

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

Quello che stai effettivamente facendo è questo:

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

Successivamente, quando Angular deve creare un'istanza del controller, utilizza il $controllerservizio (che a sua volta utilizza il $injectorper invocare la funzione del controller in modo che vengano iniettate anche le sue dipendenze).

Filtri e direttive

filtere directivefunziona esattamente allo stesso modo di controller; filterusa un servizio chiamato $filtere il suo provider $filterProvider, mentre directiveusa un servizio chiamato $compilee il suo provider $compileProvider. Alcuni link:

Come per gli altri esempi, myMod.filtere myMod.directivesono scorciatoie per la configurazione di questi servizi.


tl; dr

Quindi, per riassumere, qualsiasi funzione che viene chiamata con $injector.invoke può essere iniettata in . Ciò include, dal tuo grafico (ma non è limitato a):

  • controllore
  • direttiva
  • fabbrica
  • filtro
  • provider $get(quando si definisce provider come oggetto)
  • funzione provider (quando si definisce provider come funzione di costruzione)
  • servizio

Il provider crea nuovi servizi che possono essere iniettati in cose . Ciò comprende:

  • costante
  • fabbrica
  • fornitore
  • servizio
  • valore

Detto questo, i servizi integrati come $controllere $filter possono essere iniettati, e puoi usare questi servizi per ottenere i nuovi filtri e controller che hai definito con quei metodi (anche se le cose che hai definito non sono, da sole, in grado di essere iniettato nelle cose).

Oltre a ciò, qualsiasi funzione invocata dall'iniettore può essere iniettata con qualsiasi servizio fornito dal provider - non vi sono restrizioni (a parte le differenze confige runqui elencate).


6
Wow! grazie per aver dedicato del tempo a rispondere in modo così dettagliato! L'ho letto due volte e penso di aver capito abbastanza. Lo studierò e i collegamenti che hai dato in dettaglio più tardi oggi. E un altro +1 per il gatto. :)
user1527166

18
Una delle risposte SO più utili e dettagliate che ho incontrato - grazie!
Godders,

11
Questa risposta definisce un nuovo livello di fantastico. Roba illuminante.
Ngure Nyaga,

4
Di gran lunga la migliore risorsa con cui mi sono imbattuto per AngularJS. Grazie.
codice90

5
Letteralmente il miglior pezzo di documentazione di AngularJS che abbia mai visto. Ben fatto!
Iain Duncan,

13

Il punto che BinaryMuse fa nella sua straordinaria risposta su fornitori, fabbriche e servizi è la stessa cosa estremamente importante.

Di seguito un'immagine che penso possa illustrare visivamente il suo punto:

AngularJS sono solo fornitori
(fonte: simplygoodcode.com )


7

Ottima risposta di Michelle. Voglio solo sottolineare che le direttive possono essere iniettate. Se hai una direttiva chiamata myThing, puoi iniettarla con myThingDirective: Ecco un esempio inventato .

L'esempio sopra non è molto pratico, tuttavia la possibilità di iniettare una direttiva è utile quando si desidera decorare quella direttiva .


Sembra che il secondo esempio per decorare quella direttiva non funzioni da Angular 1.4. (vedi il commento di Juan Biscaia lì)
Vadorequest,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.