angularjs inject - angular.service vs angular.factory




module dependencies (9)

app.factory ('fn', fn) vs. app.service ('fn', fn)

Costruzione

Con le fabbriche, Angular invocherà la funzione per ottenere il risultato. È il risultato che viene memorizzato nella cache e iniettato.

 //factory
 var obj = fn();
 return obj;

Con i servizi, Angular invocherà la funzione di costruzione chiamando new . La funzione costruita viene memorizzata nella cache e iniettata.

  //service
  var obj = new fn();
  return obj;

Implementazione

Le fabbriche in genere restituiscono un oggetto letterale perché il valore restituito è ciò che viene iniettato in controller, blocchi di esecuzione, direttive, ecc.

  app.factory('fn', function(){
         var foo = 0;
         var bar = 0;
         function setFoo(val) {
               foo = val;
         }
         function setBar (val){
               bar = val;
         }
         return {
                setFoo: setFoo,
                serBar: setBar
         }
  });

Le funzioni di servizio in genere non restituiscono nulla. Invece, eseguono le funzioni di inizializzazione ed esposizione. Le funzioni possono anche fare riferimento a "questo" poiché è stato costruito utilizzando "nuovo".

app.service('fn', function () {
         var foo = 0;
         var bar = 0;
         this.setFoo = function (val) {
               foo = val;
         }
         this.setBar = function (val){
               bar = val;
         }
});

Conclusione

Quando si tratta di utilizzare fabbriche o servizi, sono entrambi molto simili. Vengono iniettati in controller, direttive, blocchi di esecuzione, ecc. E utilizzati nel codice client praticamente nello stesso modo. Sono anche entrambi singleton - il che significa che la stessa istanza è condivisa tra tutti i luoghi in cui viene iniettato il servizio / fabbrica.

Quindi quale preferisci? O uno - sono così simili che le differenze sono banali. Se scegli l'una rispetto all'altra, tieni presente come sono costruite, in modo da poterle implementare correttamente.

Ho visto sia angular.factory() che angular.service() utilizzati per dichiarare servizi; tuttavia, non angular.service() angular.service ovunque nella documentazione ufficiale.

Qual è la differenza tra i due metodi? Quale dovrebbe essere usato per cosa (supponendo che facciano cose diverse)?


Ho passato un po 'di tempo a cercare di capire la differenza.

E penso che la funzione factory usi il modello del modulo e la funzione di servizio usi il modello standard del costruttore di script java.


  angular.service('myService', myServiceFunction);
  angular.factory('myFactory', myFactoryFunction);

Ho avuto difficoltà a sistemare la mia mente su questo concetto fino a quando non l'ho messo da solo in questo modo:

Servizio : la funzione che scrivi sarà nuova :

  myInjectedService  <----  new myServiceFunction()

Factory : la funzione (costruttore) che scrivi sarà invocata :

  myInjectedFactory  <---  myFactoryFunction()

Quello che fai con quello dipende da te, ma ci sono alcuni schemi utili ...

Come scrivere una funzione di servizio per esporre un'API pubblica:

function myServiceFunction() {
  this.awesomeApi = function(optional) {
    // calculate some stuff
    return awesomeListOfValues;
  }
}
---------------------------------------------------------------------------------
// Injected in your controller
$scope.awesome = myInjectedService.awesomeApi();

O utilizzando una funzione di fabbrica per esporre un'API pubblica:

function myFactoryFunction() {
  var aPrivateVariable = "yay";

  function hello() {
    return "hello mars " + aPrivateVariable;
  }

  // expose a public API
  return {
    hello: hello
  };
}
---------------------------------------------------------------------------------
// Injected in your controller
$scope.hello = myInjectedFactory.hello();

O utilizzando una funzione di fabbrica per restituire un costruttore:

function myFactoryFunction() {
    return function() {
        var a = 2;
        this.a2 = function() {
            return a*2;
        };
    };
}
---------------------------------------------------------------------------------
// Injected in your controller
var myShinyNewObject = new myInjectedFactory();
$scope.four = myShinyNewObject.a2();

Quale usare? ...

Puoi realizzare la stessa cosa con entrambi. Tuttavia, in alcuni casi la fabbrica offre un po 'più di flessibilità per creare un iniettabile con una sintassi più semplice. Questo perché myInjectedService deve sempre essere un oggetto, myInjectedFactory può essere un oggetto, un riferimento di funzione o qualsiasi valore. Ad esempio, se hai scritto un servizio per creare un costruttore (come nell'ultimo esempio sopra), dovrebbe essere istanziato in questo modo:

var myShinyNewObject = new myInjectedService.myFunction()

che è probabilmente meno desiderabile di questo:

var myShinyNewObject = new myInjectedFactory();

(Ma dovresti essere cauto nell'usare questo tipo di pattern, in primo luogo perché gli oggetti new- in dei controller creano dipendenze difficili da tracciare difficili da simulare per i test. È meglio avere un servizio che gestisca una collezione di oggetti per tu che usi new() wily-nilly.)

Un'altra cosa, sono tutti singletons ...

Inoltre, tieni presente che in entrambi i casi, l'angolare ti aiuta a gestire un singleton. Indipendentemente da dove o quante volte iniettate il vostro servizio o funzione, otterrete lo stesso riferimento allo stesso oggetto o funzione. (Ad eccezione di quando una factory restituisce semplicemente un valore come un numero o una stringa. In tal caso, si otterrà sempre lo stesso valore, ma non un riferimento.)


Lo schema di fabbrica è più flessibile in quanto può restituire funzioni e valori nonché oggetti.

Non c'è molto punto nel modello di servizio IMHO, dato che tutto ciò che si fa è altrettanto facile con una fabbrica. Le eccezioni potrebbero essere:

  • Se ti interessa il tipo dichiarato del servizio istanziato per qualche motivo, se utilizzi il modello di servizio, il tuo costruttore sarà il tipo del nuovo servizio.
  • Se hai già una funzione di costruzione che stai utilizzando altrove che vuoi anche usare come servizio (anche se probabilmente non serve molto se vuoi iniettarvi qualcosa!).

Probabilmente, il modello di servizio è un modo leggermente migliore per creare un nuovo oggetto da un punto di vista della sintassi, ma è anche più costoso creare un'istanza. Altri hanno indicato che l'angolare utilizza "nuovo" per creare il servizio, ma questo non è del tutto vero - non è in grado di farlo perché ogni costruttore di servizi ha un numero diverso di parametri. Ciò che in realtà è angolare è usare internamente il modello di fabbrica per avvolgere la funzione del costruttore. Quindi fa un pò di jiggery pokery per simulare l'operatore "nuovo" di javascript, invocando il tuo costruttore con un numero variabile di argomenti iniettabili - ma puoi lasciare questo passo se usi semplicemente il modello factory direttamente, aumentando così leggermente l'efficienza del tuo codice.


In poche parole ..

// Service
service = (a, b) => {
  a.lastName = b;
  return a;
};

// Factory
factory = (a, b) => Object.assign({}, a, { lastName: b });

const fullName = { firstName: 'john' };

// Service
const lastNameService = (a, b) => {
  a.lastName = b;
  return a;
};
console.log(lastNameService(fullName, 'doe'));

// Factory
const lastNameFactory = (a, b) => 
  Object.assign({}, a, { lastName: b })
console.log(lastNameFactory(fullName, 'doe'));


Ecco le principali differenze:

Servizi

Sintassi: module.service( 'serviceName', function );

Risultato: quando si dichiara serviceName come argomento iniettabile, verrà fornita l' istanza di una funzione passata a module.service .

Utilizzo: Potrebbe essere utile per condividere le funzioni di utilità che sono utili da richiamare semplicemente aggiungendo ( ) al riferimento della funzione iniettata. Potrebbe anche essere eseguito con injectedArg.call( this ) o simile.

fabbriche

Sintassi: module.factory( 'factoryName', function );

Risultato: quando si dichiara factoryName come argomento iniettabile, verrà fornito il valore restituito richiamando il riferimento alla funzione passato a module.factory .

Utilizzo: potrebbe essere utile per restituire una funzione di 'classe' che può quindi essere nuova per creare istanze.

Ecco un esempio usando i servizi e la fabbrica . Maggiori informazioni su AngularJS Service vs Factory .

È anche possibile controllare la angular.factory() e una domanda simile su confusa su service vs factory .


Tutte le risposte qui sembrano essere intorno al servizio e alla fabbrica, ed è valido poiché era quello che veniva chiesto. Ma è anche importante ricordare che ce ne sono molti altri tra cui provider() , value() e constant() .

La chiave per ricordare è che ognuno è un caso speciale dell'altro. Ogni caso speciale lungo la catena consente di fare la stessa cosa con meno codice. Ognuno ha anche qualche limite aggiuntivo.

Per decidere quando utilizzare il quale si vede solo quello che consente di fare ciò che si vuole in meno codice. Ecco un'immagine che illustra quanto siano simili:

Per una completa spiegazione passo passo e una rapida consultazione su quando utilizzare ognuno di essi è possibile visitare il post del blog in cui ho ottenuto questa immagine da:


TL; DR

1) Quando usi una Fabbrica , crei un oggetto, aggiungi delle proprietà, quindi restituisci lo stesso oggetto. Quando si passa questo stabilimento al controller, tali proprietà sull'oggetto saranno ora disponibili in quel controller attraverso la fabbrica.

app.controller('myFactoryCtrl', function($scope, myFactory){
  $scope.artist = myFactory.getArtist();
});

app.factory('myFactory', function(){
  var _artist = 'Shakira';
  var service = {};

  service.getArtist = function(){
    return _artist;
  }

  return service;
});


2) Quando utilizzi il servizio , Angular lo istanzia dietro le quinte con la parola chiave 'nuova'. Per questo motivo, aggiungi proprietà a "this" e il servizio restituirà "this". Quando si passa il servizio nel controller, tali proprietà su "this" saranno ora disponibili su quel controller tramite il servizio.

app.controller('myServiceCtrl', function($scope, myService){
  $scope.artist = myService.getArtist();
});

app.service('myService', function(){
  var _artist = 'Nelly';
  this.getArtist = function(){
    return _artist;
  }
});



Non TL; DR

1) fabbrica
Le fabbriche sono il modo più popolare per creare e configurare un servizio. Non c'è davvero molto di più di quello che TL, DR ha detto. Devi solo creare un oggetto, aggiungere proprietà ad esso, quindi restituire lo stesso oggetto. Quindi, quando si passa la fabbrica nel controller, tali proprietà sull'oggetto saranno ora disponibili in quel controller attraverso la fabbrica. Un esempio più ampio è qui sotto.

app.factory('myFactory', function(){
  var service = {};
  return service;
});

Ora qualsiasi proprietà che attribuiamo al "servizio" sarà a nostra disposizione quando passeremo "myFactory" nel nostro controller.

Ora aggiungiamo alcune variabili 'private' alla nostra funzione di callback. Questi non saranno direttamente accessibili dal controller, ma alla fine creeremo alcuni metodi getter / setter su 'service' per poter modificare queste variabili 'private' quando necessario.

app.factory('myFactory', function($http, $q){
  var service = {};
  var baseUrl = 'https://itunes.apple.com/search?term=';
  var _artist = '';
  var _finalUrl = '';

  var makeUrl = function(){
   _artist = _artist.split(' ').join('+');
    _finalUrl = baseUrl + _artist + '&callback=JSON_CALLBACK';
    return _finalUrl
  }

  return service;
});

Qui noterai che non stiamo associando quelle variabili / funzioni a 'servizio'. Li stiamo semplicemente creando per utilizzarli o modificarli in seguito.

  • baseUrl è l'URL di base richiesto dall'API di iTunes
  • _artist è l'artista che desideriamo cercare
  • _finalUrl è l'URL finale e completo a cui faremo la chiamata a iTunes makeUrl è una funzione che creerà e restituirà il nostro URL di iTunes amichevole.

Ora che sono disponibili le nostre variabili helper / private e la funzione, aggiungiamo alcune proprietà all'oggetto 'service'. Qualsiasi cosa mettiamo sul "servizio" saremo in grado di utilizzare direttamente in qualsiasi controller in cui passiamo "myFactory".

Creeremo metodi setArtist e getArtist che semplicemente restituiscono o impostano l'artista. Stiamo anche creando un metodo che chiamerà l'API di iTunes con il nostro URL creato. Questo metodo restituirà una promessa che si realizzerà non appena i dati torneranno dall'API di iTunes. Se non hai avuto molta esperienza nell'usare promesse in Angular, ti consiglio caldamente di fare un tuffo profondo su di loro.

Sotto setArtist accetta un artista e consente di impostare l'artista. getArtist restituisce l'artista callItunes prima chiama makeUrl () per creare l'URL che useremo con la nostra richiesta $ http. Quindi imposta un oggetto promessa, effettua una richiesta $ http con il nostro URL finale, quindi poiché $ http restituisce una promessa, siamo in grado di chiamare .success o .error dopo la nostra richiesta. Quindi risolviamo la nostra promessa con i dati di iTunes, oppure la rifiutiamo con un messaggio che dice "C'era un errore".

app.factory('myFactory', function($http, $q){
  var service = {};
  var baseUrl = 'https://itunes.apple.com/search?term=';
  var _artist = '';
  var _finalUrl = '';

  var makeUrl = function(){
    _artist = _artist.split(' ').join('+');
    _finalUrl = baseUrl + _artist + '&callback=JSON_CALLBACK'
    return _finalUrl;
  }

  service.setArtist = function(artist){
    _artist = artist;
  }

  service.getArtist = function(){
    return _artist;
  }

  service.callItunes = function(){
    makeUrl();
    var deferred = $q.defer();
    $http({
      method: 'JSONP',
      url: _finalUrl
    }).success(function(data){
      deferred.resolve(data);
    }).error(function(){
      deferred.reject('There was an error')
    })
    return deferred.promise;
  }

  return service;
});

Ora la nostra fabbrica è completa. Ora siamo in grado di iniettare "myFactory" in qualsiasi controller e potremo quindi chiamare i nostri metodi che abbiamo allegato al nostro oggetto di servizio (setArtist, getArtist e callItunes).

app.controller('myFactoryCtrl', function($scope, myFactory){
  $scope.data = {};
  $scope.updateArtist = function(){
    myFactory.setArtist($scope.data.artist);
  };

  $scope.submitArtist = function(){
    myFactory.callItunes()
      .then(function(data){
        $scope.data.artistData = data;
      }, function(data){
        alert(data);
      })
  }
});

Nel controller sopra ci stiamo iniettando nel servizio 'myFactory'. Quindi impostiamo le proprietà sul nostro oggetto $ scope che provengono dai dati di "myFactory". L'unico codice difficile sopra è se non hai mai affrontato le promesse prima. Poiché callItunes sta restituendo una promessa, siamo in grado di utilizzare il metodo .then () e di impostare $ scope.data.artistData una volta che la nostra promessa è stata soddisfatta con i dati di iTunes. Noterai che il nostro controller è molto 'sottile'. Tutta la nostra logica e i dati persistenti si trovano nel nostro servizio, non nel nostro controller.

2) Servizio
Forse la cosa più importante da sapere quando si ha a che fare con la creazione di un servizio è quella di creare un'istanza con la parola chiave "nuova". Per voi guru JavaScript questo dovrebbe darvi un grande suggerimento sulla natura del codice. Per quelli di voi con uno sfondo limitato in JavaScript o per coloro che non conoscono troppo bene la parola chiave "nuova", esaminiamo alcuni concetti fondamentali di JavaScript che alla fine ci aiuteranno a capire la natura di un servizio.

Per vedere veramente i cambiamenti che si verificano quando invochi una funzione con la parola chiave 'nuova', creiamo una funzione e la invochiamo con la 'nuova' parola chiave, quindi mostriamo cosa fa l'interprete quando vede la parola chiave 'nuova'. I risultati finali saranno entrambi uguali.

Per prima cosa creiamo il nostro Costruttore.

var Person = function(name, age){
  this.name = name;
  this.age = age;
}

Questa è una tipica funzione di costruzione JavaScript. Ora ogni volta che invochiamo la funzione Persona usando la "nuova" parola chiave, "questo" sarà associato all'oggetto appena creato.

Ora aggiungiamo un metodo al prototipo della nostra persona in modo che sia disponibile su ogni istanza della "classe" della nostra persona.

Person.prototype.sayName = function(){
  alert('My name is ' + this.name);
}

Ora, poiché inseriamo la funzione sayName sul prototipo, ogni istanza di Person sarà in grado di chiamare la funzione sayName per avvisare il nome di tale istanza.

Ora che abbiamo la nostra funzione di costruzione Person e la nostra funzione sayName sul suo prototipo, creiamo effettivamente un'istanza di Persona, quindi chiamiamo la funzione sayName.

var tyler = new Person('Tyler', 23);
tyler.sayName(); //alerts 'My name is Tyler'

Quindi, tutto il codice per creare un costruttore Person, aggiungere una funzione al suo prototipo, creare un'istanza Persona e quindi chiamare la funzione sul suo prototipo appare così.

var Person = function(name, age){
  this.name = name;
  this.age = age;
}
Person.prototype.sayName = function(){
  alert('My name is ' + this.name);
}
var tyler = new Person('Tyler', 23);
tyler.sayName(); //alerts 'My name is Tyler'

Ora vediamo cosa succede realmente quando usi la "nuova" parola chiave in JavaScript. La prima cosa che dovresti notare è che dopo aver usato 'new' nel nostro esempio, siamo in grado di chiamare un metodo (sayName) su 'tyler' proprio come se fosse un oggetto - è perché lo è. Quindi, per prima cosa, sappiamo che il nostro costruttore di Person sta restituendo un oggetto, se possiamo vederlo nel codice oppure no. In secondo luogo, sappiamo che poiché la nostra funzione sayName si trova sul prototipo e non direttamente sull'istanza Person, l'oggetto che la funzione Persona sta restituendo deve delegare al suo prototipo su ricerche fallite. In termini più semplici, quando chiamiamo tyler.sayName () l'interprete dice "OK, vado a cercare sull'oggetto 'tyler' che abbiamo appena creato, individuare la funzione sayName, quindi chiamarla. Aspetta un attimo, non lo vedo qui - tutto quello che vedo è il nome e l'età, fammi controllare il prototipo. Sì, sembra che sia sul prototipo, lascia che lo chiami ".

Di seguito è riportato il codice per come puoi pensare a ciò che la parola chiave "nuova" sta effettivamente facendo in JavaScript. È fondamentalmente un esempio di codice del paragrafo precedente. Ho inserito la "vista dell'interprete" o il modo in cui l'interprete vede il codice all'interno delle note.

var Person = function(name, age){
  //The line below this creates an obj object that will delegate to the person's prototype on failed lookups.
  //var obj = Object.create(Person.prototype);

  //The line directly below this sets 'this' to the newly created object
  //this = obj;

  this.name = name;
  this.age = age;

  //return this;
}

Ora avendo questa conoscenza di ciò che la "nuova" parola chiave fa davvero in JavaScript, la creazione di un servizio in Angular dovrebbe essere più facile da capire.

La cosa più importante da capire quando si crea un servizio è sapere che i servizi vengono istanziati con la parola chiave 'nuova'. Combinando questa conoscenza con i nostri esempi sopra, ora dovresti riconoscere che allegerai le tue proprietà e i tuoi metodi direttamente a "questo" che verrà poi restituito dal Servizio stesso. Diamo un'occhiata a questo in azione.

A differenza di ciò che originariamente avevamo fatto con l'esempio Factory, non abbiamo bisogno di creare un oggetto, quindi restituire quell'oggetto perché, come accennato molte volte prima, abbiamo usato la parola chiave 'new' in modo che l'interprete crei quell'oggetto, lo faccia delegare a è un prototipo, quindi restituiscilo per noi senza che noi dobbiamo fare il lavoro.

Per prima cosa, creiamo la nostra funzione "privata" e di aiuto. Questo dovrebbe sembrare molto familiare dato che abbiamo fatto esattamente la stessa cosa con la nostra fabbrica. Non spiegherò cosa fa ogni riga qui perché l'ho fatto nell'esempio di fabbrica, se sei confuso, rileggi l'esempio di fabbrica.

app.service('myService', function($http, $q){
  var baseUrl = 'https://itunes.apple.com/search?term=';
  var _artist = '';
  var _finalUrl = '';

  var makeUrl = function(){
    _artist = _artist.split(' ').join('+');
    _finalUrl = baseUrl + _artist + '&callback=JSON_CALLBACK'
    return _finalUrl;
  }
});

Ora, allegheremo tutti i nostri metodi che saranno disponibili nel nostro controller per "questo".

app.service('myService', function($http, $q){
  var baseUrl = 'https://itunes.apple.com/search?term=';
  var _artist = '';
  var _finalUrl = '';

  var makeUrl = function(){
    _artist = _artist.split(' ').join('+');
    _finalUrl = baseUrl + _artist + '&callback=JSON_CALLBACK'
    return _finalUrl;
  }

  this.setArtist = function(artist){
    _artist = artist;
  }

  this.getArtist = function(){
    return _artist;
  }

  this.callItunes = function(){
    makeUrl();
    var deferred = $q.defer();
    $http({
      method: 'JSONP',
      url: _finalUrl
    }).success(function(data){
      deferred.resolve(data);
    }).error(function(){
      deferred.reject('There was an error')
    })
    return deferred.promise;
  }

});

Ora, proprio come nella nostra fabbrica, setArtist, getArtist e callItunes saranno disponibili in qualsiasi controller in cui passiamo myService. Ecco il controller myService (che è quasi esattamente lo stesso del nostro controller di fabbrica).

app.controller('myServiceCtrl', function($scope, myService){
  $scope.data = {};
  $scope.updateArtist = function(){
    myService.setArtist($scope.data.artist);
  };

  $scope.submitArtist = function(){
    myService.callItunes()
      .then(function(data){
        $scope.data.artistData = data;
      }, function(data){
        alert(data);
      })
  }
});

Come ho detto prima, una volta capito veramente cosa fa 'new', i servizi sono quasi identici alle fabbriche in Angular.


Sebbene entrambe le risposte siano tecnicamente corrette, voglio introdurre una diversa scelta di sintassi per questa risposta. Questo imho rende più facile leggere cosa sta succedendo con l'iniezione, distinguere tra ecc.

File uno

// Create the module that deals with controllers
angular.module('myApp.controllers', []);

File due

// Here we get the module we created in file one
angular.module('myApp.controllers')

// We are adding a function called Ctrl1
// to the module we got in the line above
.controller('Ctrl1', Ctrl1);

// Inject my dependencies
Ctrl1.$inject = ['$scope', '$http'];

// Now create our controller function with all necessary logic
function Ctrl1($scope, $http) {
  // Logic here
}

File tre

// Here we get the module we created in file one
angular.module('myApp.controllers')

// We are adding a function called Ctrl2
// to the module we got in the line above
.controller('Ctrl2', Ctrl2);

// Inject my dependencies
Ctrl2.$inject = ['$scope', '$http'];

// Now create our controller function with all necessary logic
function Ctrl2($scope, $http) {
  // Logic here
}




angularjs angular-services