Pregunta angular.service versus angular.factory


He visto ambos angular.factory () y angular.service () utilizado para declarar servicios; Sin embargo, yo no puede encontrar  angular.service en cualquier lugar de la documentación oficial.

¿Cuál es la diferencia entre los dos métodos? ¿Cuál debería usarse para qué (suponiendo que hagan cosas diferentes)?


1036
2018-01-14 18:36


origen


Respuestas:


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

Tuve problemas para entender este concepto hasta que me lo planteé de esta manera:

Servicio: el función que escribas será nuevo-ed:

  myInjectedService  <----  new myServiceFunction()

Fábrica: el función (constructor) que escribes será invocado:

  myInjectedFactory  <---  myFactoryFunction()

Lo que hagas con eso depende de ti, pero hay algunos patrones útiles ...

Como escribir un Servicio función para exponer una API pública:

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

O usando un fábrica función para exponer una API pública:

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 usando un fábrica función para devolver un constructor:

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

¿Cuál usar? ...

Puedes lograr lo mismo con ambos. Sin embargo, en algunos casos el fábrica le da un poco más de flexibilidad para crear un inyectable con una sintaxis más simple. Esto se debe a que myInjectedService debe ser siempre un objeto, myInjectedFactory puede ser un objeto, una referencia de función o cualquier valor. Por ejemplo, si escribiste un servicio para crear un constructor (como en el último ejemplo anterior), tendría que crear una instancia de la siguiente manera:

var myShinyNewObject = new myInjectedService.myFunction()

que es posiblemente menos deseable que esto:

var myShinyNewObject = new myInjectedFactory();

(Pero debe tener cuidado con el uso de este tipo de patrón, en primer lugar, porque nuevoel uso de objetos en sus controladores crea dependencias difíciles de rastrear que son difíciles de simular para la prueba. Es mejor tener un servicio para administrar una colección de objetos para usted que usar new() wily-nilly.)


Una cosa más, son todos Singletons ...

También tenga en cuenta que en ambos casos, angular le ayuda a administrar un singleton. Independientemente de dónde o cuántas veces inyecte su servicio o función, obtendrá la misma referencia al mismo objeto o función. (Con la excepción de cuando una fábrica simplemente devuelve un valor como un número o cadena. En ese caso, siempre obtendrá el mismo valor, pero no una referencia).


1248
2018-02-20 06:46



Simplemente pon ..

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


316
2018-01-08 02:05



Estas son las principales diferencias:

Servicios

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

Resultado: al declarar ServiceName como un argumento inyectable, se le proporcionará el instancia de una función pasó a module.service.

Uso: podría ser útil para compartiendo funciones de utilidad que son útiles para invocar simplemente añadiendo ( ) a la referencia de función inyectada. También se podría ejecutar con injectedArg.call( this ) o similar.

Suerte

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

Resultado: al declarar FactoryName como un argumento inyectable, se le proporcionará el valor que se devuelve al invocar la referencia de función pasó a module.factory.

Uso: podría ser útil para devolver un 'clase' función que luego puede ser renovada para crear instancias.

Aquí está ejemplo usando servicios y fábrica. Leer más sobre AngularJS Service vs Factory.

También puedes verificar Documentación AngularJS y una pregunta similar en stackoverflow confundido acerca de servicio vs fábrica.


243
2018-04-09 23:18



TL; DR 

1) Cuando estás usando un Fábrica creas un objeto, le agregas propiedades y luego regresas ese mismo objeto. Cuando pase esta fábrica a su controlador, esas propiedades en el objeto ahora estarán disponibles en ese controlador a través de su fábrica.

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) Cuando estás usando Servicio, Angular crea una instancia detrás de escena con la palabra clave 'nueva'. Por eso, agregará propiedades a 'esto' y el servicio devolverá 'esto'. Cuando pase el servicio a su controlador, esas propiedades en 'esto' ahora estarán disponibles en ese controlador a través de su servicio.

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) Fábrica 
Las fábricas son la forma más popular de crear y configurar un servicio. Realmente no hay mucho más de lo que dijo el TL; DR. Usted acaba de crear un objeto, agregarle propiedades y luego devolver ese mismo objeto. Luego, cuando pasa la fábrica a su controlador, esas propiedades en el objeto ahora estarán disponibles en ese controlador a través de su fábrica. Un ejemplo más extenso está debajo.

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

Ahora cualquier propiedad que adjuntemos a 'servicio' estará disponible para nosotros cuando pasemos 'myFactory' a nuestro controlador.

Ahora agreguemos algunas variables 'privadas' a nuestra función de devolución de llamada. Estos no serán accesibles directamente desde el controlador, pero eventualmente configuraremos algunos métodos getter / setter en 'servicio' para poder alterar estas variables 'privadas' cuando sea necesario.

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

Aquí notará que no estamos asociando esas variables / función a 'servicio'. Simplemente los creamos para usarlos o modificarlos más tarde.

  • baseUrl es la URL base que requiere la API de iTunes
  • _artist es el artista que queremos buscar
  • _finalUrl es la URL final y totalmente construida a la que haremos la llamada a iTunes makeUrl es una función que creará y devolverá nuestro URL amigable con iTunes.

Ahora que nuestras variables y funciones auxiliares / privadas están en su lugar, agreguemos algunas propiedades al objeto 'servicio'. Lo que sea que pongamos en 'servicio' lo podremos usar directamente en cualquier controlador al que le pasemos 'myFactory'.

Vamos a crear los métodos setArtist y getArtist que simplemente devuelven o configuran el artista. También vamos a crear un método que llame a la API de iTunes con nuestra URL creada. Este método devolverá una promesa que se cumplirá una vez que los datos hayan regresado de la API de iTunes. Si no has tenido mucha experiencia en el uso de promesas en Angular, te recomiendo encarecidamente profundizar en ellas.

Abajo setArtist acepta un artista y le permite configurar el artista. getArtist devuelve el artista callItunes primeras llamadas makeUrl () para construir la URL que usaremos con nuestra solicitud de $ http. Luego configura un objeto de promesa, realiza una solicitud de $ http con nuestra url final, y luego porque $ http devuelve una promesa, podemos llamar a .success o .error después de nuestra solicitud. Luego resolvemos nuestra promesa con los datos de iTunes, o lo rechazamos con un mensaje que dice 'Hubo un error'.

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

Ahora nuestra fábrica está completa. Ahora podemos insertar 'myFactory' en cualquier controlador y luego podremos llamar a nuestros métodos que adjuntamos a nuestro objeto de servicio (setArtist, getArtist y 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);
      })
  }
});

En el controlador anterior, estamos inyectando en el servicio 'myFactory'. Luego establecemos propiedades en nuestro objeto $ scope que provienen de datos de 'myFactory'. El único código engañoso de arriba es si nunca antes has tratado con las promesas. Debido a que callItunes está devolviendo una promesa, podemos utilizar el método .then () y solo establecer $ scope.data.artistData una vez que nuestra promesa se cumpla con los datos de iTunes. Notarás que nuestro controlador es muy "delgado". Toda nuestra lógica y datos persistentes se encuentran en nuestro servicio, no en nuestro controlador.

2) Servicio 
Quizás lo más importante que debe saberse cuando se trata de crear un Servicio es que se crea una instancia con la palabra clave 'nueva'. Para tus gurús de JavaScript esto debería darte una gran pista sobre la naturaleza del código. Para aquellos de ustedes con un conocimiento limitado en JavaScript o para aquellos que no están muy familiarizados con lo que realmente hace la palabra clave 'nueva', repasemos algunos fundamentos de JavaScript que eventualmente nos ayudarán a comprender la naturaleza de un Servicio.

Para ver realmente los cambios que ocurren cuando se invoca una función con la palabra clave 'nueva', vamos a crear una función e invocarla con la palabra clave 'nueva', luego mostraremos qué hace el intérprete cuando ve la palabra clave 'nueva'. Los resultados finales serán los mismos.

Primero, creemos nuestro Constructor.

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

Esta es una función típica de constructor de JavaScript. Ahora cada vez que invocamos la función Person usando la palabra clave 'new', 'this' se vinculará al objeto recién creado.

Ahora agreguemos un método al prototipo de nuestra Persona para que esté disponible en cada instancia de nuestra "clase" de personas.

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

Ahora, porque ponemos la función sayName en el prototipo, cada instancia de Person podrá llamar a la función sayName para alertar el nombre de esa instancia.

Ahora que tenemos nuestra función constructor de persona y nuestra función sayName en su prototipo, creemos realmente una instancia de Person y luego llamemos a la función sayName.

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

De modo que todos juntos el código para crear un constructor de persona, agregar una función a su prototipo, crear una instancia de persona y llamar a la función en su prototipo se ve así.

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'

Ahora veamos qué está sucediendo realmente cuando usas la palabra clave 'nueva' en JavaScript. Lo primero que debe observar es que después de usar 'nuevo' en nuestro ejemplo, podemos llamar a un método (sayName) en 'tyler' como si fuera un objeto, porque es así. Entonces, primero, sabemos que nuestro constructor de persona devuelve un objeto, ya sea que podamos verlo en el código o no. En segundo lugar, sabemos que debido a que nuestra función sayName está ubicada en el prototipo y no directamente en la instancia de la persona, el objeto que la función Persona está devolviendo debe delegar en su prototipo en las búsquedas fallidas. En términos más simples, cuando llamamos a tyler.sayName () el intérprete dice "OK, voy a ver el objeto 'tyler' que acabo de crear, ubico la función sayName, luego la llamo. Espera un minuto, no lo veo aquí, todo lo que veo es nombre y edad, déjame ver el prototipo. Sí, parece que está en el prototipo, déjame llamarlo ".

A continuación, encontrará un código sobre cómo puede pensar acerca de qué está haciendo realmente la palabra clave "nueva" en JavaScript. Básicamente es un ejemplo de código del párrafo anterior. He puesto la "vista del intérprete" o la forma en que el intérprete ve el código dentro de las notas.

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

Ahora que tiene este conocimiento de lo que realmente hace la palabra clave "nueva" en JavaScript, la creación de un servicio en Angular debería ser más fácil de entender.

Lo más importante para comprender al crear un servicio es saber que los servicios se instancian con la palabra clave 'nueva'. Combinando ese conocimiento con nuestros ejemplos anteriores, ahora debe reconocer que va a adjuntar sus propiedades y métodos directamente a 'esto', que luego será devuelto por el Servicio mismo. Echemos un vistazo a esto en acción.

A diferencia de lo que hicimos originalmente con el ejemplo de fábrica, no necesitamos crear un objeto y luego devolver ese objeto porque, como se mencionó muchas veces antes, usamos la palabra clave 'nueva' para que el intérprete cree ese objeto, haga que delegue en es un prototipo, luego devuélvanoslo sin que tengamos que hacer el trabajo.

Lo primero es lo primero, creemos nuestra función 'privada' y de ayuda. Esto debería ser muy familiar ya que hicimos exactamente lo mismo con nuestra fábrica. No voy a explicar lo que hace cada línea aquí porque lo hice en el ejemplo de fábrica, si está confundido, vuelva a leer el ejemplo de fábrica.

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

Ahora, adjuntaremos todos nuestros métodos que estarán disponibles en nuestro controlador a 'esto'.

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

});

Ahora, al igual que en nuestra fábrica, setArtist, getArtist y callItunes estarán disponibles en cualquier controlador que pase myService. Aquí está el controlador myService (que es casi exactamente el mismo que nuestro controlador de fábrica).

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

Como mencioné antes, una vez que realmente entiende lo que 'nuevo' hace, los Servicios son casi idénticos a las fábricas en Angular.


134
2018-05-14 16:14



La pista está en el nombre

Los servicios y las fábricas son similares entre sí. Ambos producirán un objeto singleton que se puede inyectar en otros objetos, por lo que a menudo se usan indistintamente.

Están destinados a ser usados ​​semánticamente para implementar diferentes patrones de diseño.

Los servicios son para implementar un patrón de servicio

Un patrón de servicio es uno en el que su aplicación se divide en unidades de funcionalidad lógicamente consistentes. Un ejemplo podría ser un acceso de API o un conjunto de lógica de negocios.

Esto es especialmente importante en Angular porque los modelos angulares suelen ser solo objetos JSON extraídos de un servidor, por lo que necesitamos un lugar donde poner nuestra lógica comercial.

Aquí hay un servicio de Github, por ejemplo. Sabe cómo hablar con Github. Sabe sobre urls y métodos. Podemos inyectarlo en un controlador, y generará y devolverá una promesa.

(function() {
  var base = "https://api.github.com";

  angular.module('github', [])
    .service('githubService', function( $http ) {
      this.getEvents: function() {
        var url = [
          base,
          '/events',
          '?callback=JSON_CALLBACK'
        ].join('');
        return $http.jsonp(url);
      }
    });
  )();

Las fábricas implementan un patrón de fábrica

Las fábricas, por otro lado están destinadas a implementar un patrón de fábrica. Un patrón de fábrica en el que usamos una función de fábrica para generar un objeto. Por lo general, podemos usar esto para construir modelos. Aquí hay una fábrica que devuelve un constructor de autor:

angular.module('user', [])
  .factory('User', function($resource) {
    var url = 'http://simple-api.herokuapp.com/api/v1/authors/:id'
    return $resource(url);
  })

Haremos uso de esto así:

angular.module('app', ['user'])
  .controller('authorController', function($scope, User) {
    $scope.user = new User();
  })

Tenga en cuenta que las fábricas también devuelven singletons.

Las fábricas pueden devolver un constructor

Como una fábrica simplemente devuelve un objeto, puede devolver cualquier tipo de objeto que desee, incluida una función de constructor, como se ve más arriba.

Las fábricas devuelven un objeto; los servicios son nuevos

Otra diferencia técnica está en la manera en que se componen los servicios y las fábricas. Se actualizará una función de servicio para generar el objeto. Se llamará a una función de fábrica y devolverá el objeto.

  • Los servicios son constructores nuevos.
  • Las fábricas simplemente se llaman y devuelven un objeto.

Esto significa que en un servicio, agregamos a "esto" que, en el contexto de un constructor, apuntará al objeto en construcción.

Para ilustrar esto, aquí está el mismo objeto simple creado usando un servicio y una fábrica:

angular.module('app', [])
  .service('helloService', function() {
    this.sayHello = function() {
      return "Hello!";
    }
  })
  .factory('helloFactory', function() {
    return {
      sayHello: function() {
        return "Hello!";
      }
    }
  });

33
2017-12-18 11:48



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

Construcción

Con las fábricas, Angular invocará la función para obtener el resultado. Es el resultado que se almacena en caché y se inyecta.

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

Con servicios, Angular invocará la función de constructor llamando nuevo. La función construida se almacena en caché y se inyecta.

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

Implementación

Las fábricas suelen devolver un objeto literal porque el valor de retorno es lo que se inyecta en los controladores, ejecuta bloques, directivas, etc.

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

Las funciones de servicio generalmente no devuelven nada. En cambio, realizan funciones de inicialización y exposición. Las funciones también pueden hacer referencia a 'esto' ya que se construyó usando 'nuevo'.

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

Conclusión

Cuando se trata de usar fábricas o servicios, ambos son muy similares. Se inyectan en controladores, directivas, bloques de ejecución, etc. y se usan en el código del cliente de la misma manera. También son singletons, lo que significa que la misma instancia se comparte entre todos los lugares donde se inyecta el servicio / fábrica.

Entonces, ¿cuál deberías preferir? Cualquiera de los dos: son tan similares que las diferencias son triviales. Si elige uno sobre el otro, solo tenga en cuenta cómo están construidos, para que pueda implementarlos correctamente.


23
2018-06-27 00:17



Todas las respuestas aquí parecen estar relacionadas con el servicio y la fábrica, y eso es válido ya que eso era lo que se estaba preguntando. Pero también es importante tener en cuenta que hay muchos otros que incluyen provider(), value()y constant().

La clave para recordar es que cada uno es un caso especial del otro. Cada caso especial en la cadena que le permite hacer lo mismo con menos código. Cada uno también tiene alguna limitación adicional.

Decidir cuándo usar cuál acaba de ver cuál le permite hacer lo que desea con menos código. Aquí hay una imagen que ilustra qué tan similares son:

enter image description here

Para un desglose completo paso a paso y una referencia rápida de cuándo usar cada uno, puede visitar la publicación del blog donde obtuve esta imagen:

http://www.simplygoodcode.com/2015/11/the-difference-between-service-provider-and-factory-in-angularjs/


23
2017-12-10 12:55



He pasado un tiempo tratando de descubrir la diferencia.

Y creo que la función de fábrica utiliza el patrón de módulo y la función de servicio utiliza el patrón de constructor de script java estándar.


5
2018-03-10 15:04



El patrón de fábrica es más flexible, ya que puede devolver funciones, valores y objetos.

No hay mucho sentido en el modelo de servicio en mi humilde opinión, ya que todo lo que hace es igual de fácil que hacerlo con una fábrica. Las excepciones pueden ser:

  • Si le importa el tipo declarado de su servicio instanciado por alguna razón, si usa el patrón de servicio, su constructor será del tipo del nuevo servicio.
  • Si ya tiene una función de constructor que está utilizando en otro lugar, también quiere usarla como servicio (¡aunque probablemente no le sirva de mucho si quiere inyectar algo en ella!).

Podría decirse que el patrón de servicio es una ligeramente Una manera más agradable de crear un nuevo objeto desde el punto de vista de la sintaxis, pero también es más costoso crear instancias. Otros han indicado que angular utiliza "nuevo" para crear el servicio, pero esto no es del todo cierto; no puede hacerlo porque cada constructor de servicio tiene un número diferente de parámetros. Lo que realmente angular es usar el patrón de fábrica internamente para envolver su función constructora. Luego hace algunos ejercicios de manipulación inteligentes para simular el operador "nuevo" de javascript, invocando a su constructor con una cantidad variable de argumentos inyectables, pero puede dejar este paso si solo usa el patrón de fábrica directamente, aumentando muy levemente la eficiencia de su código.


2
2017-08-06 14:54