angularjs - services - angular.service vs angular.factory
angularjs services w3schools (9)
He visto tanto angular.factory() como angular.service() para declarar servicios; sin embargo, no angular.service() angular.service
en ningún lugar en la documentación oficial.
¿Cuál es la diferencia entre los dos métodos? ¿Cuál debería ser usado para qué (asumiendo que hacen cosas diferentes)?
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 y, por lo tanto, se usan indistintamente.
Están destinados a ser utilizados 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 aquel en el que su aplicación se divide en unidades de funcionalidad lógicamente consistentes. Un ejemplo podría ser un descriptor de acceso a la API o un conjunto de lógica de negocios.
Esto es especialmente importante en Angular porque los modelos Angular generalmente son solo objetos JSON extraídos de un servidor, por lo que necesitamos un lugar donde colocar nuestra lógica empresarial.
Aquí hay un servicio Github, por ejemplo. Sabe hablar con Github. Sabe de 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 uno en el que usamos una función de fábrica para generar un objeto. Típicamente podríamos 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);
})
Haríamos 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.
Debido a que una fábrica simplemente devuelve un objeto, puede devolver cualquier tipo de objeto que desee, incluida una función de constructor, como vemos anteriormente.
Las fábricas devuelven un objeto; los servicios son nuevos
Otra diferencia técnica está en la forma en que se componen los servicios y las fábricas. Se generará una función de servicio para generar el objeto. Se llamará una función de fábrica y devolverá el objeto.
- Los servicios son constructores nuevos.
- Las fábricas son simplemente llamadas y devuelven un objeto.
Esto significa que en un servicio, agregamos "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!";
}
}
});
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 cachea e inyecta.
//factory
var obj = fn();
return obj;
Con los servicios, Angular invocará la función de constructor llamando a new . La función construida es cacheada e inyectada.
//service
var obj = new fn();
return obj;
Implementación
Las fábricas generalmente devuelven un objeto literal porque el valor de retorno es lo que se inyecta en los controladores, bloques de ejecución, 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 por lo general no devuelven nada. En su lugar, realizan la inicialización y exponen funciones. Las funciones también pueden hacer referencia a ''this'', ya que se construyó con ''new''.
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 un controlador, directivas, bloque de ejecución, etc., y se usan en el código del cliente de manera muy parecida. También son ambos 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ía preferir? Cualquiera de los dos, son tan similares que las diferencias son triviales. Si elige uno sobre el otro, tenga en cuenta cómo están construidos, para que pueda implementarlos correctamente.
Aquí están las principales diferencias:
Servicios
Sintaxis: module.service( ''serviceName'', function );
Resultado: Al declarar serviceName como un argumento inyectable, se le proporcionará la instancia de una función pasada a module.service
.
Uso: podría ser útil para compartir funciones de utilidad que son útiles para invocar simplemente añadiendo ( )
a la referencia de función inyectada. También podría ejecutarse 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 pasada a module.factory
.
Uso: podría ser útil para devolver una función de ''clase'' que luego puede ser nueva para crear instancias.
Aquí está el ejemplo que usa los servicios y la fábrica . Lea más acerca de AngularJS Service vs Factory .
También puede consultar la angular.factory() y una pregunta similar sobre confundida sobre service vs factory .
El patrón de fábrica es más flexible, ya que puede devolver funciones y valores, así como objetos.
No hay mucho punto en el patrón de servicio en mi humilde opinión, ya que todo lo que hace lo puede hacer fácilmente 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á el tipo del nuevo servicio.
- Si ya tiene una función de constructor que está utilizando en otro lugar que también quiere usar como servicio (aunque probablemente no sea de mucha utilidad si quiere inyectar algo en ella).
Podría decirse que el patrón de servicio es una forma ligeramente más agradable de crear un nuevo objeto desde un punto de vista de sintaxis, pero también es más costoso de 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 hace angular es usar el patrón de fábrica internamente para envolver su función de constructor. A continuación, hace algunos ingeniosos juegos de simulación para simular el "nuevo" operador de javascript, invocando a su constructor con un número variable de argumentos inyectables, pero puede omitir este paso si solo usa el patrón de fábrica directamente, lo que aumenta ligeramente la eficiencia de su código.
He pasado un tiempo tratando de averiguar la diferencia.
Y creo que la función de fábrica utiliza el patrón de módulo y la función de servicio usa el patrón de constructor de script java estándar.
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''));
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 preguntaba. Pero también es importante tener en cuenta que hay varios otros, incluyendo provider()
, value()
y constant()
.
La clave para recordar es que cada uno es un caso especial del otro. Cada caso especial de la cadena le permite hacer lo mismo con menos código. Cada uno también tiene alguna limitación adicional.
Para decidir cuándo usar cuál simplemente ve cuál le permite hacer lo que quiere en menos código. Aquí hay una imagen que ilustra cuán similares son:
Para obtener un desglose paso a paso completo y una referencia rápida de cuándo usar cada una, puede visitar la publicación del blog de donde obtuve esta imagen:
TL; DR
1) Cuando está utilizando una Fábrica , crea un objeto, le agrega propiedades y luego devuelve 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á utilizando el Servicio , Angular lo crea entre bambalinas con la palabra clave "nuevo". Debido a 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;
}
});
No 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 que lo que dijo el TL; DR. Solo creas un objeto, le agregas propiedades y luego devuelves ese mismo objeto. Luego, cuando pase 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. A continuación se muestra un ejemplo más extenso.
app.factory(''myFactory'', function(){
var service = {};
return service;
});
Ahora, todas las propiedades que adjuntamos al ''servicio'' estarán disponibles cuando pasemos ''myFactory'' a nuestro controlador.
Ahora agreguemos algunas variables ''privadas'' a nuestra función de devolución de llamada. No se podrá acceder directamente a estos desde el controlador, pero eventualmente estableceremos algunos métodos de obtención / configuración 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 adjuntando esas variables / función al ''servicio''. Simplemente los estamos creando para usarlos o modificarlos más tarde.
- baseUrl es la URL base que requiere la API de iTunes
- _artista es el artista que deseamos buscar.
- _finalUrl es la URL final y totalmente integrada a la que haremos la llamada a iTunes. makeUrl es una función que creará y devolverá nuestra URL amigable de iTunes.
Ahora que nuestras funciones y variables de ayuda / privadas están en su lugar, agreguemos algunas propiedades al objeto ''servicio''. Independientemente de lo que pongamos en "servicio", podremos utilizarlo directamente en cualquier controlador al que pasemos "myFactory".
Vamos a crear métodos setArtist y getArtist que simplemente devuelven o configuran el artista. También vamos a crear un método que llamará 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 tienes mucha experiencia en el uso de promesas en Angular, te recomiendo que hagas una inmersión profunda en ellas.
A continuación, setArtist acepta un artista y le permite configurar el artista. getArtist devuelve el artista callItunes first calls makeUrl () para construir la URL que usaremos con nuestra solicitud $ http. Luego configura un objeto de promesa, realiza una solicitud $ http con nuestra url final y, a continuación, debido a que $ 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 la 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 inyectar ''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 de arriba, estamos inyectando en el servicio ''myFactory''. Luego establecemos propiedades en nuestro objeto $ scope que provienen de datos de ''myFactory''. El único código complicado anterior es si nunca antes has cumplido las promesas. Debido a que callItunes está devolviendo una promesa, podemos usar 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''. Todos nuestros datos lógicos y persistentes se encuentran en nuestro servicio, no en nuestro controlador.
2) servicio
Quizás lo más importante que se debe saber cuando se trata de crear un Servicio es que se crea una instancia con la palabra clave "nuevo". Para los gurús de JavaScript, esto debería darle una gran pista sobre la naturaleza del código. Para aquellos de ustedes con antecedentes limitados en JavaScript o para aquellos que no están muy familiarizados con lo que realmente hace la "nueva" palabra clave, revisemos algunos aspectos fundamentales de JavaScript que eventualmente nos ayudarán a entender la naturaleza de un Servicio.
Para ver realmente los cambios que ocurren cuando invoca una función con la palabra clave ''nueva'', creemos una función y la invocamos con la palabra clave ''nueva'', luego mostremos qué hace el intérprete cuando ve la palabra clave ''nueva''. Los resultados finales serán los mismos.
Primero vamos a crear nuestro Constructor.
var Person = function(name, age){
this.name = name;
this.age = age;
}
Esta es una función constructora de JavaScript típica. Ahora, cada vez que invocamos la función Persona utilizando la palabra clave ''nuevo'', ''esto'' estará vinculado 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 Persona.
Person.prototype.sayName = function(){
alert(''My name is '' + this.name);
}
Ahora, debido a que 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 constructora Person y nuestra función sayName en su prototipo, creamos realmente una instancia de Person y luego llamamos a la función sayName.
var tyler = new Person(''Tyler'', 23);
tyler.sayName(); //alerts ''My name is Tyler''
Así que todos juntos el código para crear un constructor de Person, agregar una función a su prototipo, crear una instancia de Person y luego llamar a la función en su prototipo se parece a esto.
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 lo que realmente está sucediendo cuando usas la palabra clave ''nuevo'' en JavaScript. Lo primero que debe notar es que después de usar ''nuevo'' en nuestro ejemplo, podemos llamar a un método (sayName) en ''tyler'' como si fuera un objeto, eso es porque lo es. Entonces, primero, sabemos que nuestro constructor Persona está devolviendo 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 se encuentra en el prototipo y no directamente en la instancia de Persona, el objeto que la función de Persona está devolviendo debe delegar a 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 buscar el objeto ''tyler'' que acabamos de crear, localice la función sayName y luego llámela. Espera un momento, 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 se muestra el código para saber qué es lo que realmente hace la palabra clave ''nueva'' en JavaScript. Es básicamente un ejemplo de código del párrafo anterior. He puesto la ''vista de 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 tenemos este conocimiento de lo que realmente hace la palabra clave ''nueva'' en JavaScript, crear un Servicio en Angular debería ser más fácil de entender.
Lo más importante que se debe entender al crear un Servicio es saber que los Servicios se crean instancias con la palabra clave "nuevo". Combinando ese conocimiento con nuestros ejemplos anteriores, ahora debe reconocer que adjuntará sus propiedades y métodos directamente a ''esto'', que luego será devuelto desde el propio Servicio. Echemos un vistazo a esto en acción.
A diferencia de lo que hicimos originalmente con el ejemplo de Factory, no necesitamos crear un objeto y luego devolver ese objeto porque, como se mencionó muchas veces antes, usamos la palabra clave ''nuevo'' 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 parecer muy familiar ya que hicimos exactamente lo mismo con nuestra fábrica. No explicaré lo que hace cada línea aquí porque lo hice en el ejemplo de fábrica. Si estás confundido, vuelve 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 en el que pasemos 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 entiendes lo que hace ''nuevo'', los Servicios son casi idénticos a las fábricas en Angular.
angular.service(''myService'', myServiceFunction);
angular.factory(''myFactory'', myFactoryFunction);
Tuve problemas para envolver mi cabeza alrededor de este concepto hasta que me lo puse de esta manera:
Servicio : la función que escribas será nueva :
myInjectedService <---- new myServiceFunction()
Fábrica : la función (constructor) que escribas se invocará :
myInjectedFactory <--- myFactoryFunction()
Lo que hagas con eso depende de ti, pero hay algunos patrones útiles ...
Como escribir una función de servicio 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 una función de fábrica 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 una función de fábrica 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, la fábrica le brinda un poco más de flexibilidad para crear un inyectable con una sintaxis más simple. Esto se debe a que, si bien myInjectedService siempre debe ser 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 ser instanciado así:
var myShinyNewObject = new myInjectedService.myFunction()
que es posiblemente menos deseable que esto:
var myShinyNewObject = new myInjectedFactory();
(Pero debe tener cuidado al usar este tipo de patrón en primer lugar porque los nuevos objetos en sus controladores crean dependencias difíciles de rastrear que son difíciles de simular para realizar pruebas. Es mejor tener un servicio que administre una colección de objetos para usted que use new()
wily-nilly.)
Una cosa más, todos son 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 una cadena. En ese caso, siempre obtendrá el mismo valor, pero no una referencia).