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 ]
Il $provide
servizio è 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 provider
metodo sul $provide
servizio e puoi ottenere il $provide
servizio chiedendo che venga iniettato nella config
funzione 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 greeting
in qualsiasi funzione iniettabile (come controller, più avanti) e Angular chiamerà la $get
funzione del provider per restituire una nuova istanza del servizio. In questo caso, la cosa che verrà iniettata è una funzione che accetta un name
parametro e alert
un 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
, service
e value
sono 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.provider
versione) per noi. Non c'è letteralmente il 100% di differenza nelle due versioni. value
funziona allo stesso modo - se qualunque cosa torniamo dalla nostra $get
funzione (ovvero la nostra factory
funzione) è sempre esattamente la stessa, possiamo scrivere ancora meno codice usando value
. Ad esempio, poiché restituiamo sempre la stessa funzione per il nostro greeting
servizio, possiamo anche usarla value
per 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 $provider
metodi 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);
});
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 $injector
che viene creato al primo avvio dell'applicazione; puoi ottenerlo iniettando $injector
qualsiasi funzione iniettabile (sì, $injector
sa come iniettarsi!)
Una volta che hai $injector
, è possibile ottenere un'istanza di un servizio definito chiamando get
su 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 invoke
metodo 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
$get
metodi dei fornitori (ovvero le factory
funzioni di definizione)
Poiché constant
s e value
s 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 provide
metodo 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 config
sezioni della tua applicazione in modo da poter interagire con loro!
In primo luogo, angolare esegue l'applicazione in due fasi - la config
e run
fasi. La config
fase, 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 run
fase, 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.config
e 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 $provide
nel nostro primo esempio di codice. Tuttavia, ciò che vale la pena notare è che durante la config
fase, solo i provider possono essere iniettati (ad eccezione dei servizi nel AUTO
modulo-- $provide
e $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 constant
s, poiché non possono essere modificate, possono essere iniettate all'interno di config
blocchi (è così che differiscono da value
s). Sono accessibili solo con il loro nome (nessun Provider
suffisso necessario).
Ogni volta che si definisce un provider per un servizio, quel provider viene nominato serviceProvider
, dove si service
trova 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 setText
che possiamo usare per personalizzare la nostra alert
; possiamo ottenere l'accesso a questo provider in un config
blocco per chiamare questo metodo e personalizzare il servizio. Quando finalmente eseguiamo la nostra app, possiamo prendere il greeting
servizio 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/
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 $controller
responsabile 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 $controller
servizio (che a sua volta utilizza il $injector
per invocare la funzione del controller in modo che vengano iniettate anche le sue dipendenze).
Filtri e direttive
filter
e directive
funziona esattamente allo stesso modo di controller
; filter
usa un servizio chiamato $filter
e il suo provider $filterProvider
, mentre directive
usa un servizio chiamato $compile
e il suo provider $compileProvider
. Alcuni link:
Come per gli altri esempi, myMod.filter
e myMod.directive
sono scorciatoie per la configurazione di questi servizi.
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 $controller
e $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 config
e run
qui elencate).