¿Cuáles son las diferencias entre una Service
, Provider
y Factory
en AngularJS?
service.factory
. No quería complicar más este tema.
¿Cuáles son las diferencias entre una Service
, Provider
y Factory
en AngularJS?
service.factory
. No quería complicar más este tema.
Respuestas:
De la lista de correo de AngularJS obtuve un hilo increíble que explica el servicio vs fábrica vs proveedor y su uso de inyección. Compilando las respuestas:
Sintaxis: module.service( 'serviceName', function );
Resultado: al declarar serviceName como argumento inyectable, se le proporcionará una instancia de la función. En otras palabras new FunctionYouPassedToService()
.
Sintaxis: module.factory( 'factoryName', function );
Resultado: al declarar factoryName como argumento inyectable, se le proporcionará el valor que se devuelve invocando la referencia de función pasada a module.factory .
Sintaxis: module.provider( 'providerName', function );
Resultado: al declarar providerName como argumento inyectable, se le proporcionará (new ProviderFunction()).$get()
. La función de constructor se instancia antes de que se llame al método $ get: ProviderFunction
es la referencia de función que se pasa a module.provider.
Los proveedores tienen la ventaja de que pueden configurarse durante la fase de configuración del módulo.
Vea aquí el código provisto.
Aquí hay una gran explicación adicional de Misko:
provide.value('a', 123);
function Controller(a) {
expect(a).toEqual(123);
}
En este caso, el inyector simplemente devuelve el valor tal como está. Pero, ¿qué pasa si quieres calcular el valor? Luego usa una fábrica
provide.factory('b', function(a) {
return a*2;
});
function Controller(b) {
expect(b).toEqual(246);
}
Entonces, factory
es una función responsable de crear el valor. Tenga en cuenta que la función de fábrica puede solicitar otras dependencias.
Pero, ¿qué pasa si quieres ser más OO y tener una clase llamada Greeter?
function Greeter(a) {
this.greet = function() {
return 'Hello ' + a;
}
}
Entonces para instanciar tendrías que escribir
provide.factory('greeter', function(a) {
return new Greeter(a);
});
Entonces podríamos pedir 'greeter' en un controlador como este
function Controller(greeter) {
expect(greeter instanceof Greeter).toBe(true);
expect(greeter.greet()).toEqual('Hello 123');
}
Pero eso es demasiado prolijo. Una forma más corta de escribir esto seríaprovider.service('greeter', Greeter);
Pero, ¿y si quisiéramos configurar la Greeter
clase antes de la inyección? Entonces podríamos escribir
provide.provider('greeter2', function() {
var salutation = 'Hello';
this.setSalutation = function(s) {
salutation = s;
}
function Greeter(a) {
this.greet = function() {
return salutation + ' ' + a;
}
}
this.$get = function(a) {
return new Greeter(a);
};
});
Entonces podemos hacer esto:
angular.module('abc', []).config(function(greeter2Provider) {
greeter2Provider.setSalutation('Halo');
});
function Controller(greeter2) {
expect(greeter2.greet()).toEqual('Halo 123');
}
Como nota al margen, service
, factory
, y value
son todos derivados de proveedor.
provider.service = function(name, Class) {
provider.provide(name, function() {
this.$get = function($injector) {
return $injector.instantiate(Class);
};
});
}
provider.factory = function(name, factory) {
provider.provide(name, function() {
this.$get = function($injector) {
return $injector.invoke(factory);
};
});
}
provider.value = function(name, value) {
provider.factory(name, function() {
return value;
});
};
toEqual
y cómo greeter.Greet
es. ¿Por qué no usar algo un poco más real y relatable?
factory
/ service
/ provider
:var myApp = angular.module('myApp', []);
//service style, probably the simplest one
myApp.service('helloWorldFromService', function() {
this.sayHello = function() {
return "Hello, World!";
};
});
//factory style, more involved but more sophisticated
myApp.factory('helloWorldFromFactory', function() {
return {
sayHello: function() {
return "Hello, World!";
}
};
});
//provider style, full blown, configurable version
myApp.provider('helloWorld', function() {
this.name = 'Default';
this.$get = function() {
var name = this.name;
return {
sayHello: function() {
return "Hello, " + name + "!";
}
}
};
this.setName = function(name) {
this.name = name;
};
});
//hey, we can configure a provider!
myApp.config(function(helloWorldProvider){
helloWorldProvider.setName('World');
});
function MyCtrl($scope, helloWorld, helloWorldFromFactory, helloWorldFromService) {
$scope.hellos = [
helloWorld.sayHello(),
helloWorldFromFactory.sayHello(),
helloWorldFromService.sayHello()];
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<body ng-app="myApp">
<div ng-controller="MyCtrl">
{{hellos}}
</div>
</body>
this
cambia el contexto en la $get
función? - ya no se refiere al proveedor instanciado en esa función.
this
no cambia el contexto, porque lo que se llama es new Provider()
. $ Get (), a dónde se Provider
pasa la función app.provider
. Es decir que $get()
se está llamando como un método en el construido Provider
, por lo que this
se referirá Provider
como sugiere el ejemplo.
Unknown provider: helloWorldProvider <- helloWorld
cuando ejecuto esto localmente? Al comentarlo, el mismo error para los otros 2 ejemplos. ¿Hay alguna configuración de proveedor oculta? (Angular 1.0.8) - Encontrado: stackoverflow.com/questions/12339272/…
TL; DR
1) Cuando usa una Fábrica , crea un objeto, le agrega propiedades y luego devuelve el mismo objeto. Cuando pasa 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 usa el Servicio , AngularJS lo instancia detrás de escena con la palabra clave 'nuevo'. Debido a eso, agregará propiedades a 'this' y el servicio devolverá 'this'. Cuando pasa 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;
}
});
3) Los proveedores son el único servicio que puede pasar a su función .config (). Utilice un proveedor cuando desee proporcionar una configuración de todo el módulo para su objeto de servicio antes de ponerlo a disposición.
app.controller(‘myProvider’, function($scope, myProvider){
$scope.artist = myProvider.getArtist();
$scope.data.thingFromConfig = myProvider.thingOnConfig;
});
app.provider(‘myProvider’, function(){
//Only the next two lines are available in the app.config()
this._artist = ‘’;
this.thingFromConfig = ‘’;
this.$get = function(){
var that = this;
return {
getArtist: function(){
return that._artist;
},
thingOnConfig: that.thingFromConfig
}
}
});
app.config(function(myProviderProvider){
myProviderProvider.thingFromConfig = ‘This was set in config’;
});
No TL; DR
1)
Fábricas Las fábricas son la forma más popular de crear y configurar un servicio. Realmente no hay mucho más de lo que dice el TL; DR. Simplemente crea un objeto, le agrega propiedades y luego devuelve el 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. Un ejemplo más extenso está abajo.
app.factory(‘myFactory’, function(){
var service = {};
return service;
});
Ahora, cualquier propiedad que adjuntemos al 'servicio' estará disponible 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 / funciones a 'servicio'. Simplemente los estamos creando para usarlos o modificarlos más tarde.
Ahora que nuestras funciones y variables auxiliares / privadas están en su lugar, agreguemos algunas propiedades al objeto 'servicio'. Todo lo que ponemos en 'servicio' se puede usar directamente en cualquier controlador al que le pasemos 'myFactory'.
Vamos a crear métodos setArtist y getArtist que simplemente devuelven o configuran al 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 ha tenido mucha experiencia usando promesas en AngularJS, le recomiendo que profundice en ellas.
A continuación, setArtist acepta un artista y le permite configurarlo. getArtist devuelve el artista. callItunes primero llama a makeUrl () para construir la URL que usaremos con nuestra solicitud $ http. Luego configura un objeto de promesa, realiza una solicitud de $ http con nuestra url final, luego, 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 anterior, estamos inyectando en el servicio 'myFactory'. Luego establecemos propiedades en nuestro objeto $ scope con datos de 'myFactory'. El único código complicado anterior es si nunca antes ha tratado con 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á que nuestro controlador es muy 'delgado' (esta es una buena práctica de codificación). 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 instancia con la palabra clave 'nueva'. Para sus 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 demasiado familiarizados con lo que realmente hace la 'nueva' palabra clave, revisemos algunos fundamentos de JavaScript que eventualmente nos ayudarán a comprender 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 e invoquémosla con la palabra clave 'nueva', luego muestremos 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 invoquemos la función Persona usando 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 de constructor Person y nuestra función sayName en su prototipo, creemos 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’
Entonces, todo el código para crear un constructor de Persona, agregar una función a su prototipo, crear una instancia de Persona y luego 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 echemos un vistazo a lo que realmente está sucediendo cuando usa 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. Primero, sabemos que nuestro constructor de Persona está devolviendo un objeto, ya sea que podamos ver eso 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 Person, el objeto que devuelve la función Person debe delegarse en su prototipo en 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, ubicar la función sayName y luego llamarla. Espera un momento, no lo veo aquí, todo lo que veo es nombre y edad, Déjame comprobar el prototipo. Sí, parece que está en el prototipo, déjenme llamarlo ".
A continuación se muestra el código de cómo puede pensar qué está haciendo la palabra clave 'nueva' en JavaScript. Básicamente es un ejemplo de código del párrafo anterior. Puse 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 below line creates an object(obj) 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 conocemos lo que realmente hace la 'nueva' palabra clave en JavaScript, crear un Servicio en AngularJS debería ser más fácil de entender.
Lo más importante para entender al crear un Servicio es saber que los Servicios se instancian con la palabra clave "nueva". Combinando ese conocimiento con nuestros ejemplos anteriores, ahora debería reconocer que 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 Factory, no necesitamos crear un objeto y luego devolverlo porque, como se mencionó muchas veces antes, usamos la palabra clave 'new' para que el intérprete cree ese objeto, haga que delegue en es prototipo, luego devuélvanoslo sin que tengamos que hacer el trabajo.
Primero lo primero, creemos nuestra función 'privada' y auxiliar. Esto debería parecer muy familiar ya que hicimos exactamente lo mismo con nuestra fábrica. No explicaré qué 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 al que pasamos 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 comprende lo que hace "nuevo", los Servicios son casi idénticos a las fábricas en AngularJS.
3) Proveedor
Lo más importante que debe recordar acerca de los proveedores es que son el único servicio que puede pasar a la parte app.config de su aplicación. Esto es de gran importancia si necesita modificar alguna parte de su objeto de servicio antes de que esté disponible en cualquier otro lugar de su aplicación. Aunque es muy similar a los Servicios / Fábricas, hay algunas diferencias que discutiremos.
Primero configuramos nuestro Proveedor de una manera similar a como lo hicimos con nuestro Servicio y Fábrica. Las siguientes variables son nuestra función 'privada' y auxiliar.
app.provider('myProvider', function(){
var baseUrl = 'https://itunes.apple.com/search?term=';
var _artist = '';
var _finalUrl = '';
//Going to set this property on the config function below.
this.thingFromConfig = ‘’;
var makeUrl = function(){
_artist = _artist.split(' ').join('+');
_finalUrl = baseUrl + _artist + '&callback=JSON_CALLBACK'
return _finalUrl;
}
}
* Una vez más, si alguna parte del código anterior es confusa, consulte la sección Fábrica donde explico lo que hace, más detalles.
Puede pensar que los Proveedores tienen tres secciones. La primera sección son las variables / funciones 'privadas' que se modificarán / establecerán más tarde (como se muestra arriba). La segunda sección son las variables / funciones que estarán disponibles en su función app.config y, por lo tanto, están disponibles para modificar antes de que estén disponibles en cualquier otro lugar (también se muestra arriba). Es importante tener en cuenta que esas variables deben adjuntarse a la palabra clave 'this'. En nuestro ejemplo, solo 'thingFromConfig' estará disponible para modificar en la app.config. La tercera sección (que se muestra a continuación) es todas las variables / funciones que estarán disponibles en su controlador cuando pase el servicio 'myProvider' a ese controlador específico.
Al crear un servicio con el Proveedor, las únicas propiedades / métodos que estarán disponibles en su controlador son aquellas propiedades / métodos que se devuelven de la función $ get (). El siguiente código pone $ get en 'this' (que sabemos que eventualmente será devuelto por esa función). Ahora, esa función $ get devuelve todos los métodos / propiedades que queremos que estén disponibles en el controlador. Aquí hay un ejemplo de código.
this.$get = function($http, $q){
return {
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;
},
setArtist: function(artist){
_artist = artist;
},
getArtist: function(){
return _artist;
},
thingOnConfig: this.thingFromConfig
}
}
Ahora el código de proveedor completo se ve así
app.provider('myProvider', function(){
var baseUrl = 'https://itunes.apple.com/search?term=';
var _artist = '';
var _finalUrl = '';
//Going to set this property on the config function below
this.thingFromConfig = '';
var makeUrl = function(){
_artist = _artist.split(' ').join('+');
_finalUrl = baseUrl + _artist + '&callback=JSON_CALLBACK'
return _finalUrl;
}
this.$get = function($http, $q){
return {
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;
},
setArtist: function(artist){
_artist = artist;
},
getArtist: function(){
return _artist;
},
thingOnConfig: this.thingFromConfig
}
}
});
Ahora, al igual que en nuestra fábrica y servicio, setArtist, getArtist y callItunes estarán disponibles en cualquier controlador al que le pasemos myProvider. Aquí está el controlador myProvider (que es casi exactamente el mismo que nuestro controlador de fábrica / servicio).
app.controller('myProviderCtrl', function($scope, myProvider){
$scope.data = {};
$scope.updateArtist = function(){
myProvider.setArtist($scope.data.artist);
};
$scope.submitArtist = function(){
myProvider.callItunes()
.then(function(data){
$scope.data.artistData = data;
}, function(data){
alert(data);
})
}
$scope.data.thingFromConfig = myProvider.thingOnConfig;
});
Como se mencionó anteriormente, el objetivo de crear un servicio con el Proveedor es poder alterar algunas variables a través de la función app.config antes de que el objeto final se pase al resto de la aplicación. Veamos un ejemplo de eso.
app.config(function(myProviderProvider){
//Providers are the only service you can pass into app.config
myProviderProvider.thingFromConfig = 'This sentence was set in app.config. Providers are the only service that can be passed into config. Check out the code to see how it works';
});
Ahora puede ver cómo 'thingFromConfig' es una cadena vacía en nuestro proveedor, pero cuando eso aparezca en el DOM, será 'Esta oración se estableció ...'.
Todos los servicios son singletons ; se instancian una vez por aplicación. Pueden ser de cualquier tipo , ya sea primitivo, literal de objeto, función o incluso una instancia de un tipo personalizado.
El value
, factory
, service
, constant
, y provider
métodos son todos los proveedores. Enseñan al Inyector a crear instancias de los Servicios.
La más detallada, pero también la más completa, es una receta de Proveedor. Los cuatro tipos de recetas restantes : Valor, Fábrica, Servicio y Constante, son solo azúcar sintáctico además de una receta de proveedor .
Debe usar la receta del proveedor solo cuando desee exponer una API para la configuración de toda la aplicación que debe realizarse antes de que la aplicación se inicie. Esto suele ser interesante solo para servicios reutilizables cuyo comportamiento puede necesitar variar ligeramente entre las aplicaciones.
decorator
.Comprender la fábrica, el servicio y el proveedor de AngularJS
Todos estos se utilizan para compartir objetos singleton reutilizables. Ayuda a compartir código reutilizable a través de su aplicación / varios componentes / módulos.
Del Servicio de Documentos / Fábrica :
- Instancia perezosa : Angular solo crea una instancia de un servicio / fábrica cuando un componente de la aplicación depende de él.
- Singletons : cada componente que depende de un servicio obtiene una referencia a la instancia única generada por la fábrica de servicios.
Una fábrica es una función en la que puede manipular / agregar lógica antes de crear un objeto, luego se devuelve el objeto recién creado.
app.factory('MyFactory', function() {
var serviceObj = {};
//creating an object with methods/functions or variables
serviceObj.myFunction = function() {
//TO DO:
};
//return that object
return serviceObj;
});
Uso
Puede ser solo una colección de funciones como una clase. Por lo tanto, se puede instanciar en diferentes controladores cuando lo está inyectando dentro de las funciones de su controlador / fábrica / directiva. Se instancia solo una vez por aplicación.
Simplemente mientras mira los servicios piense en el prototipo de matriz. Un servicio es una función que instancia un nuevo objeto usando la palabra clave 'nuevo'. Puede agregar propiedades y funciones a un objeto de servicio utilizando la this
palabra clave. A diferencia de una fábrica, no devuelve nada (devuelve un objeto que contiene métodos / propiedades).
app.service('MyService', function() {
//directly binding events to this context
this.myServiceFunction = function() {
//TO DO:
};
});
Uso
Úselo cuando necesite compartir un solo objeto en toda la aplicación. Por ejemplo, detalles de usuarios autenticados, métodos / datos compartibles, funciones de utilidad, etc.
Un proveedor se utiliza para crear un objeto de servicio configurable. Puede configurar el servicio desde la función config. Devuelve un valor utilizando la $get()
función La $get
función se ejecuta en la fase de ejecución en angular.
app.provider('configurableService', function() {
var name = '';
//this method can be be available at configuration time inside app.config.
this.setName = function(newName) {
name = newName;
};
this.$get = function() {
var getName = function() {
return name;
};
return {
getName: getName //exposed object to where it gets injected.
};
};
});
Uso
Cuando necesite proporcionar una configuración de módulo inteligente para su objeto de servicio antes de ponerlo a disposición, por ejemplo. supongamos que desea establecer su URL de API en función de su entorno como dev
, stage
oprod
NOTA
Solo el proveedor estará disponible en la fase de configuración de angular, mientras que el servicio y la fábrica no.
Espero que esto haya aclarado su comprensión sobre Fábrica, Servicio y Proveedor .
only when you want to expose an API for application-wide configuration that must be made before the application starts. This is usually interesting only for reusable services whose behavior might need to vary slightly between applications
, así que no parece posible, ¿verdad?
Para mí, la revelación llegó cuando me di cuenta de que todos funcionan de la misma manera: ejecutando algo una vez , almacenando el valor que obtienen, y luego tose ese mismo valor almacenado cuando se hace referencia a través de la inyección de dependencia .
Digamos que tenemos:
app.factory('a', fn);
app.service('b', fn);
app.provider('c', fn);
La diferencia entre los tres es que:
a
El valor almacenado proviene de la ejecución fn
.b
El valor almacenado proviene de new
ing fn
.c
El valor almacenado proviene de obtener primero una instancia new
ing fn
y luego ejecutar un $get
método de la instancia.Lo que significa que hay algo así como un objeto de caché dentro de AngularJS, cuyo valor de cada inyección solo se asigna una vez, cuando se inyectaron la primera vez y dónde:
cache.a = fn()
cache.b = new fn()
cache.c = (new fn()).$get()
Es por eso que usamos this
en servicios y definimos a this.$get
en proveedores.
factory
s. La única razón por service
la que existen son lenguajes como CoffeeScript, TypeScript, ES6, etc., para que pueda usar su sintaxis de clase. Solo necesita provider
s si su módulo se usa en varias aplicaciones con diferentes configuraciones mediante el uso app.config()
. Si su servicio es un singleton puro o es capaz de crear instancias de algo, solo depende de su implementación.
Servicio vs proveedor vs fábrica:
Estoy tratando de mantenerlo simple. Se trata del concepto básico de JavaScript.
En primer lugar, ¡hablemos de los servicios en AngularJS!
Qué es el servicio: en AngularJS, serviciono es más que un objeto JavaScript singleton que puede almacenar algunos métodos o propiedades útiles. Este objeto singleton se crea por ngApp (aplicación angular) y se comparte entre todos los controladores dentro de la aplicación actual. Cuando Angularjs crea una instancia de un objeto de servicio, registra este objeto de servicio con un nombre de servicio único. Entonces, cada vez que necesitamos una instancia de servicio, Angular busca en el registro este nombre de servicio y devuelve la referencia al objeto de servicio. De modo que podamos invocar el método, acceder a propiedades, etc. en el objeto de servicio. ¡Puede tener dudas sobre si también puede poner propiedades, métodos en el objeto de alcance de los controladores! Entonces, ¿por qué necesita un objeto de servicio? Las respuestas son: los servicios se comparten entre el alcance del controlador múltiple. Si coloca algunas propiedades / métodos en el objeto de alcance de un controlador, estará disponible solo para el alcance actual.
Entonces, si hay tres alcances de controlador, que sea controladorA, controladorB y controladorC, todos compartirán la misma instancia de servicio.
<div ng-controller='controllerA'>
<!-- controllerA scope -->
</div>
<div ng-controller='controllerB'>
<!-- controllerB scope -->
</div>
<div ng-controller='controllerC'>
<!-- controllerC scope -->
</div>
¿Cómo crear un servicio?
AngularJS proporciona diferentes métodos para registrar un servicio. Aquí nos concentraremos en tres métodos de fábrica (..), servicio (..), proveedor (..);
Use este enlace para referencia de código
Podemos definir una función de fábrica de la siguiente manera.
factory('serviceName',function fnFactory(){ return serviceInstance;})
AngularJS proporciona el método 'factory (' serviceName ', fnFactory)' que toma dos parámetros, serviceName y una función de JavaScript. Angular crea una instancia de servicio invocando la función fnFactory () como se muestra a continuación.
var serviceInstace = fnFactory();
La función pasada puede definir un objeto y devolver ese objeto. AngularJS simplemente almacena esta referencia de objeto a una variable que se pasa como primer argumento. Cualquier cosa que se devuelva desde fnFactory estará vinculada a serviceInstance. En lugar de devolver el objeto, también podemos devolver funciones, valores, etc. Lo que devolvamos estará disponible para la instancia de servicio.
Ejemplo:
var app= angular.module('myApp', []);
//creating service using factory method
app.factory('factoryPattern',function(){
var data={
'firstName':'Tom',
'lastName':' Cruise',
greet: function(){
console.log('hello!' + this.firstName + this.lastName);
}
};
//Now all the properties and methods of data object will be available in our service object
return data;
});
service('serviceName',function fnServiceConstructor(){})
Es la otra forma, podemos registrar un servicio. La única diferencia es la forma en que AngularJS intenta crear una instancia del objeto de servicio. Esta vez angular usa la palabra clave 'nuevo' y llama a la función constructora algo como a continuación.
var serviceInstance = new fnServiceConstructor();
En la función constructora podemos usar la palabra clave 'this' para agregar propiedades / métodos al objeto de servicio. ejemplo:
//Creating a service using the service method
var app= angular.module('myApp', []);
app.service('servicePattern',function(){
this.firstName ='James';
this.lastName =' Bond';
this.greet = function(){
console.log('My Name is '+ this.firstName + this.lastName);
};
});
La función Provider () es la otra forma de crear servicios. Deje que nos interese crear un servicio que solo muestre algún mensaje de saludo para el usuario. Pero también queremos proporcionar una funcionalidad tal que el usuario pueda configurar su propio mensaje de saludo. En términos técnicos, queremos crear servicios configurables. Cómo podemos hacer esto ? Debe haber una manera, para que la aplicación pueda pasar sus mensajes de saludo personalizados y Angularjs la ponga a disposición de la función de fábrica / constructor que crea nuestra instancia de servicios. En tal caso, la función de proveedor () hace el trabajo. usando la función de proveedor () podemos crear servicios configurables.
Podemos crear servicios configurables utilizando la sintaxis del proveedor como se indica a continuación.
/*step1:define a service */
app.provider('service',function serviceProviderConstructor(){});
/*step2:configure the service */
app.config(function configureService(serviceProvider){});
1.El objeto proveedor se crea utilizando la función de constructor que definimos en nuestra función de proveedor.
var serviceProvider = new serviceProviderConstructor();
2. La función que pasamos en app.config (), se ejecuta. Esto se llama fase de configuración, y aquí tenemos la oportunidad de personalizar nuestro servicio.
configureService(serviceProvider);
3. Finalmente, la instancia de servicio se crea llamando al método $ get de serviceProvider.
serviceInstance = serviceProvider.$get()
var app= angular.module('myApp', []);
app.provider('providerPattern',function providerConstructor(){
//this function works as constructor function for provider
this.firstName = 'Arnold ';
this.lastName = ' Schwarzenegger' ;
this.greetMessage = ' Welcome, This is default Greeting Message' ;
//adding some method which we can call in app.config() function
this.setGreetMsg = function(msg){
if(msg){
this.greetMessage = msg ;
}
};
//We can also add a method which can change firstName and lastName
this.$get = function(){
var firstName = this.firstName;
var lastName = this.lastName ;
var greetMessage = this.greetMessage;
var data={
greet: function(){
console.log('hello, ' + firstName + lastName+'! '+ greetMessage);
}
};
return data ;
};
});
app.config(
function(providerPatternProvider){
providerPatternProvider.setGreetMsg(' How do you do ?');
}
);
Resumen:
Factory usa una función de fábrica que devuelve una instancia de servicio. serviceInstance = fnFactory ();
El servicio usa una función de constructor e Angular invoca esta función de constructor usando la palabra clave 'new' para crear la instancia de servicio. serviceInstance = new fnServiceConstructor ();
El proveedor define una función providerConstructor, esta función providerConstructor define una función de fábrica $ get . Llamadas angulares $ get () para crear el objeto de servicio. La sintaxis del proveedor tiene una ventaja adicional de configurar el objeto de servicio antes de que se instancia. serviceInstance = $ get ();
Como señalan varias personas aquí correctamente, una fábrica, un proveedor, un servicio e incluso un valor constante son versiones de lo mismo. Puedes diseccionar el más general provider
en todos ellos. Al igual que:
Aquí está el artículo del que proviene esta imagen:
Le das a AngularJS una función, AngularJS almacenará en caché e inyectará el valor de retorno cuando se solicite la fábrica.
Ejemplo:
app.factory('factory', function() {
var name = '';
// Return value **is** the object that will be injected
return {
name: name;
}
})
Uso:
app.controller('ctrl', function($scope, factory) {
$scope.name = factory.name;
});
Le das a AngularJS una función, AngularJS llamará a new para instanciarla. Es la instancia que crea AngularJS la que se almacenará en caché e inyectará cuando se solicite el servicio. Como se usó new para instanciar el servicio, la palabra clave this es válida y se refiere a la instancia.
Ejemplo:
app.service('service', function() {
var name = '';
this.setName = function(newName) {
name = newName;
}
this.getName = function() {
return name;
}
});
Uso:
app.controller('ctrl', function($scope, service) {
$scope.name = service.getName();
});
Le das a AngularJS una función, y AngularJS llamará a su $get
función. Es el valor de retorno de la $get
función que se almacenará en caché e inyectará cuando se solicite el servicio.
Los proveedores le permiten configurar el proveedor antes de que AngularJS llame al $get
método para obtener el inyectable.
Ejemplo:
app.provider('provider', function() {
var name = '';
this.setName = function(newName) {
name = newName;
}
this.$get = function() {
return {
name: name
}
}
})
Uso (como inyectable en un controlador)
app.controller('ctrl', function($scope, provider) {
$scope.name = provider.name;
});
Uso (la configuración del proveedor antes $get
se llama para crear el inyectable)
app.config(function(providerProvider) {
providerProvider.setName('John');
});
Noté algo interesante al jugar con los proveedores.
La visibilidad de los inyectables es diferente para los proveedores que para los servicios y las fábricas. Si declara una "constante" de AngularJS (por ejemplo myApp.constant('a', 'Robert');
), puede inyectarla en servicios, fábricas y proveedores.
Pero si declara un "valor" de AngularJS (por ejemplo, myApp.value('b', {name: 'Jones'});
), puede inyectarlo en servicios y fábricas, pero NO en la función de creación de proveedores. Sin embargo, puede inyectarlo en la $get
función que defina para su proveedor. Esto se menciona en la documentación de AngularJS, pero es fácil pasarlo por alto. Puede encontrarlo en la página% provide en las secciones sobre el valor y los métodos constantes.
<div ng-app="MyAppName">
<div ng-controller="MyCtrl">
<p>from Service: {{servGreet}}</p>
<p>from Provider: {{provGreet}}</p>
</div>
</div>
<script>
var myApp = angular.module('MyAppName', []);
myApp.constant('a', 'Robert');
myApp.value('b', {name: 'Jones'});
myApp.service('greetService', function(a,b) {
this.greeter = 'Hi there, ' + a + ' ' + b.name;
});
myApp.provider('greetProvider', function(a) {
this.firstName = a;
this.$get = function(b) {
this.lastName = b.name;
this.fullName = this.firstName + ' ' + this.lastName;
return this;
};
});
function MyCtrl($scope, greetService, greetProvider) {
$scope.servGreet = greetService.greeter;
$scope.provGreet = greetProvider.fullName;
}
</script>
Esta es una parte muy confusa para los novatos y he tratado de aclararlo con palabras fáciles.
Servicio AngularJS: se utiliza para compartir funciones de utilidad con la referencia de servicio en el controlador. El servicio es de naturaleza única, por lo que para un servicio solo se crea una instancia en el navegador y se usa la misma referencia en toda la página.
En el servicio, creamos nombres de funciones como propiedad con este objeto.
AngularJS Factory: el propósito de Factory también es el mismo que el Servicio, sin embargo, en este caso creamos un nuevo objeto y agregamos funciones como propiedades de este objeto y al final devolvemos este objeto.
Proveedor de AngularJS: el propósito de esto es nuevamente el mismo, sin embargo, el proveedor proporciona la salida de su función $ get.
La definición y el uso del Servicio, Fábrica y Proveedor se explican en http://www.dotnetfunda.com/articles/show/3156/difference-between-angularjs-service-factory-and-provider
Para mí, la mejor y más simple forma de entender la diferencia es:
var service, factory;
service = factory = function(injection) {}
Cómo AngularJS instancia componentes particulares (simplificado):
// service
var angularService = new service(injection);
// factory
var angularFactory = factory(injection);
Entonces, para el servicio, lo que se convierte en el componente AngularJS es la instancia de objeto de la clase que está representada por la función de declaración de servicio. Para la fábrica, es el resultado devuelto por la función de declaración de fábrica. La fábrica puede comportarse igual que el servicio:
var factoryAsService = function(injection) {
return new function(injection) {
// Service content
}
}
La forma más simple de pensar es la siguiente:
El ejemplo de 'clase' de fábrica se proporciona en los comentarios, así como la diferencia del proveedor.
new MyService()
o algo :)
Mi aclaración sobre este asunto:
Básicamente, todos los tipos mencionados (servicio, fábrica, proveedor, etc.) simplemente crean y configuran variables globales (que por supuesto son globales para toda la aplicación), tal como lo fueron las variables globales anticuadas.
Si bien no se recomiendan las variables globales, el uso real de estas variables globales es proporcionar inyección de dependencia , pasando la variable al controlador relevante.
Existen muchos niveles de complicaciones al crear los valores para las "variables globales":
app.config
.
app.config
archivo, y esta función $ .get se comporta igual que la fábrica arriba, en que su valor de retorno se utiliza para inicializar las variables "globales". Mi comprensión es muy simple a continuación.
Fábrica: Simplemente crea un objeto dentro de la fábrica y lo devuelve.
Servicio:
Solo tiene una función estándar que utiliza esta palabra clave para definir una función.
Proveedor:
Hay un $get
objeto que usted define y puede usarse para obtener el objeto que devuelve datos.
Resumen de documentos angulares :
Las mejores respuestas de SO:
https://stackoverflow.com/a/26924234/165673 (<- BUENO)
https://stackoverflow.com/a/27263882/165673
https://stackoverflow.com/a/16566144/165673
Todas las buenas respuestas ya. Me gustaría agregar algunos puntos más en Servicio y Fábrica . Junto con la diferencia entre servicio / fábrica. Y uno también puede tener preguntas como:
Comencemos con la diferencia entre Servicio y fábrica:
Ambos son Singletons : cada vez que Angular los encuentra como una dependencia por primera vez, crea una única instancia de servicio / fábrica. Una vez que se crea la instancia, la misma instancia se usa para siempre.
Se puede usar para modelar un objeto con comportamiento : ambos pueden tener métodos, variables de estado interno, etc. Aunque la forma en que escribes ese código será diferente.
Servicios:
Un servicio es una función constructora, y Angular lo instanciará llamando a new yourServiceName()
. Esto significa un par de cosas.
this
.new yourServiceName(
), recibirá el this
objeto con todas las propiedades que le pones.Ejemplo de muestra:
angular.service('MyService', function() {
this.aServiceVariable = "Ved Prakash"
this.aServiceMethod = function() {
return //code
};
});
Cuando Angular inyecta este
MyService
servicio en un controlador que depende de él, ese controlador obtendrá un servicio alMyService
que puede llamar funciones, por ejemplo, MyService.aServiceMethod ().
Ten cuidado conthis
:
Como el servicio construido es un objeto, los métodos que contiene pueden referirse a esto cuando se los llama:
angular.service('ScoreKeeper', function($http) {
this.score = 0;
this.getScore = function() {
return this.score;
};
this.setScore = function(newScore) {
this.score = newScore;
};
this.addOne = function() {
this.score++;
};
});
Es posible que sienta la tentación de llamar ScoreKeeper.setScore
a una cadena de promesa, por ejemplo, si inicializó el puntaje cogiéndolo del servidor: $http.get('/score').then(ScoreKeeper.setScore).
el problema con esto es que ScoreKeeper.setScore
se llamará con un this
enlace null
y obtendrá errores. La mejor manera sería $http.get('/score').then(ScoreKeeper.setScore.bind(ScoreKeeper))
. Ya sea que elija usar esto en sus métodos de servicio o no, tenga cuidado de cómo los llama.
Devolver un valor de aService
:
Debido a cómo funcionan los constructores de JavaScript, si devuelve un valor complejo (i.e., an Object)
de una constructor
función, la persona que llama obtendrá ese Objeto en lugar de esta instancia.
Esto significa que básicamente se puede copiar y pegar el ejemplo de la fábrica desde abajo, reemplazar factory
con service
, y que va a trabajar:
angular.service('MyService', function($http) {
var api = {};
api.aServiceMethod= function() {
return $http.get('/users');
};
return api;
});
Entonces, cuando Angular construya su servicio con el nuevo MyService (), obtendrá ese objeto api en lugar de la instancia de MyService.
Este es el comportamiento para cualquier valor complejo (objetos, funciones) pero no para los tipos primitivos.
Suerte:
Una fábrica es una función antigua que devuelve un valor. El valor de retorno es lo que se inyecta en cosas que dependen de la fábrica. Un patrón de fábrica típico en Angular es devolver un objeto con funciones como propiedades, como este:
angular.factory('MyFactory', function($http) {
var api = {};
api.aFactoryMethod= function() {
return $http.get('/users');
};
return api;
});
El valor inyectado para una dependencia de fábrica es el valor de retorno de la fábrica, y no tiene que ser un objeto. Podría ser una función
Respuestas a las preguntas anteriores 1 y 2:
En su mayor parte, simplemente use fábricas para todo. Su comportamiento es más fácil de entender. No hay que elegir si devolver un valor o no, y además, no se introducirán errores si haces lo incorrecto.
Sin embargo, todavía me refiero a ellos como "servicios" cuando estoy hablando de inyectarlos como dependencias.
El comportamiento del Servicio / Fábrica es muy similar, y algunas personas dirán que cualquiera de los dos está bien. Eso es algo cierto, pero me resulta más fácil seguir los consejos de la guía de estilo de John Papa y seguir con las fábricas. **
Una aclaración adicional es que las fábricas pueden crear funciones / primitivas, mientras que los servicios no. Echa un vistazo a este jsFiddle basado en Epokk's: http://jsfiddle.net/skeller88/PxdSP/1351/ .
La fábrica devuelve una función que se puede invocar:
myApp.factory('helloWorldFromFactory', function() {
return function() {
return "Hello, World!";
};
});
La fábrica también puede devolver un objeto con un método que se puede invocar:
myApp.factory('helloWorldFromFactory', function() {
return {
sayHello: function() {
return "Hello, World!";
}
};
});
El servicio devuelve un objeto con un método que se puede invocar:
myApp.service('helloWorldFromService', function() {
this.sayHello = function() {
return "Hello, World!";
};
});
Para más detalles, vea una publicación que escribí sobre la diferencia: http://www.shanemkeller.com/tldr-services-vs-factories-in-angular/
Ya hay buenas respuestas, pero solo quiero compartir esta.
En primer lugar: el proveedor es la forma / receta para crear un service
(objeto singleton) que se supone que es inyectado por $ injector (cómo AngulaJS realiza el patrón IoC).
Y Value, Factory, Service y Constant (4 maneras): el azúcar sintáctico sobre la forma / recepción del proveedor .
Hay una Service vs Factory
parte que ha sido cubierta:
https://www.youtube.com/watch?v=BLzNCkPn3ao
El servicio tiene que ver con la new
palabra clave que, como sabemos, hace 4 cosas:
prototype
objetocontext
athis
this
Y Factory se trata de Factory Pattern: contiene funciones que devuelven objetos como ese Servicio.
Y este video simple / corto: cubre también Proveedor : https://www.youtube.com/watch?v=HvTZbQ_hUZY (allí puede ver cómo van de fábrica a proveedor)
La receta del proveedor se usa principalmente en la configuración de la aplicación, antes de que la aplicación se haya iniciado / inicializado por completo.
Después de leer todas estas publicaciones, creó más confusión para mí ... Pero aún así toda la información es valiosa ... finalmente encontré la siguiente tabla que brindará información con una simple comparación
Y para principiantes, comprenda: - Esto puede no ser un caso de uso correcto, pero en un nivel alto esto es lo que es el caso de uso para estos tres.
angular.module('myApp').config(function($testProvider){
$testProvider.someFunction();
})
Para escenarios básicos, la fábrica y el servicio se comportan igual.
Aquí hay un código de placa de identificación que he creado como plantilla de código para la fábrica de objetos en AngularjS. He usado un Car / CarFactory como ejemplo para ilustrar. Crea un código de implementación simple en el controlador.
<script>
angular.module('app', [])
.factory('CarFactory', function() {
/**
* BroilerPlate Object Instance Factory Definition / Example
*/
this.Car = function() {
// initialize instance properties
angular.extend(this, {
color : null,
numberOfDoors : null,
hasFancyRadio : null,
hasLeatherSeats : null
});
// generic setter (with optional default value)
this.set = function(key, value, defaultValue, allowUndefined) {
// by default,
if (typeof allowUndefined === 'undefined') {
// we don't allow setter to accept "undefined" as a value
allowUndefined = false;
}
// if we do not allow undefined values, and..
if (!allowUndefined) {
// if an undefined value was passed in
if (value === undefined) {
// and a default value was specified
if (defaultValue !== undefined) {
// use the specified default value
value = defaultValue;
} else {
// otherwise use the class.prototype.defaults value
value = this.defaults[key];
} // end if/else
} // end if
} // end if
// update
this[key] = value;
// return reference to this object (fluent)
return this;
}; // end this.set()
}; // end this.Car class definition
// instance properties default values
this.Car.prototype.defaults = {
color: 'yellow',
numberOfDoors: 2,
hasLeatherSeats: null,
hasFancyRadio: false
};
// instance factory method / constructor
this.Car.prototype.instance = function(params) {
return new
this.constructor()
.set('color', params.color)
.set('numberOfDoors', params.numberOfDoors)
.set('hasFancyRadio', params.hasFancyRadio)
.set('hasLeatherSeats', params.hasLeatherSeats)
;
};
return new this.Car();
}) // end Factory Definition
.controller('testCtrl', function($scope, CarFactory) {
window.testCtrl = $scope;
// first car, is red, uses class default for:
// numberOfDoors, and hasLeatherSeats
$scope.car1 = CarFactory
.instance({
color: 'red'
})
;
// second car, is blue, has 3 doors,
// uses class default for hasLeatherSeats
$scope.car2 = CarFactory
.instance({
color: 'blue',
numberOfDoors: 3
})
;
// third car, has 4 doors, uses class default for
// color and hasLeatherSeats
$scope.car3 = CarFactory
.instance({
numberOfDoors: 4
})
;
// sets an undefined variable for 'hasFancyRadio',
// explicitly defines "true" as default when value is undefined
$scope.hasFancyRadio = undefined;
$scope.car3.set('hasFancyRadio', $scope.hasFancyRadio, true);
// fourth car, purple, 4 doors,
// uses class default for hasLeatherSeats
$scope.car4 = CarFactory
.instance({
color: 'purple',
numberOfDoors: 4
});
// and then explicitly sets hasLeatherSeats to undefined
$scope.hasLeatherSeats = undefined;
$scope.car4.set('hasLeatherSeats', $scope.hasLeatherSeats, undefined, true);
// in console, type window.testCtrl to see the resulting objects
});
</script>
Aquí hay un ejemplo más simple. Estoy usando algunas bibliotecas de terceros que esperan un objeto "Posición" que exponga la latitud y la longitud, pero a través de diferentes propiedades del objeto. No quería piratear el código del proveedor, así que ajusté los objetos de "Posición" que estaba pasando.
angular.module('app')
.factory('PositionFactory', function() {
/**
* BroilerPlate Object Instance Factory Definition / Example
*/
this.Position = function() {
// initialize instance properties
// (multiple properties to satisfy multiple external interface contracts)
angular.extend(this, {
lat : null,
lon : null,
latitude : null,
longitude : null,
coords: {
latitude: null,
longitude: null
}
});
this.setLatitude = function(latitude) {
this.latitude = latitude;
this.lat = latitude;
this.coords.latitude = latitude;
return this;
};
this.setLongitude = function(longitude) {
this.longitude = longitude;
this.lon = longitude;
this.coords.longitude = longitude;
return this;
};
}; // end class definition
// instance factory method / constructor
this.Position.prototype.instance = function(params) {
return new
this.constructor()
.setLatitude(params.latitude)
.setLongitude(params.longitude)
;
};
return new this.Position();
}) // end Factory Definition
.controller('testCtrl', function($scope, PositionFactory) {
$scope.position1 = PositionFactory.instance({latitude: 39, longitude: 42.3123});
$scope.position2 = PositionFactory.instance({latitude: 39, longitude: 42.3333});
}) // end controller
;
Utilizando como referencia esta página y la documentación (que parece haber mejorado mucho desde la última vez que busqué), armé la siguiente demostración mundial real (-ish) que utiliza 4 de los 5 sabores de proveedor; Valor, constante, fábrica y proveedor completo.
HTML:
<div ng-controller="mainCtrl as main">
<h1>{{main.title}}*</h1>
<h2>{{main.strapline}}</h2>
<p>Earn {{main.earn}} per click</p>
<p>You've earned {{main.earned}} by clicking!</p>
<button ng-click="main.handleClick()">Click me to earn</button>
<small>* Not actual money</small>
</div>
aplicación
var app = angular.module('angularProviders', []);
// A CONSTANT is not going to change
app.constant('range', 100);
// A VALUE could change, but probably / typically doesn't
app.value('title', 'Earn money by clicking');
app.value('strapline', 'Adventures in ng Providers');
// A simple FACTORY allows us to compute a value @ runtime.
// Furthermore, it can have other dependencies injected into it such
// as our range constant.
app.factory('random', function randomFactory(range) {
// Get a random number within the range defined in our CONSTANT
return Math.random() * range;
});
// A PROVIDER, must return a custom type which implements the functionality
// provided by our service (see what I did there?).
// Here we define the constructor for the custom type the PROVIDER below will
// instantiate and return.
var Money = function(locale) {
// Depending on locale string set during config phase, we'll
// use different symbols and positioning for any values we
// need to display as currency
this.settings = {
uk: {
front: true,
currency: '£',
thousand: ',',
decimal: '.'
},
eu: {
front: false,
currency: '€',
thousand: '.',
decimal: ','
}
};
this.locale = locale;
};
// Return a monetary value with currency symbol and placement, and decimal
// and thousand delimiters according to the locale set in the config phase.
Money.prototype.convertValue = function(value) {
var settings = this.settings[this.locale],
decimalIndex, converted;
converted = this.addThousandSeparator(value.toFixed(2), settings.thousand);
decimalIndex = converted.length - 3;
converted = converted.substr(0, decimalIndex) +
settings.decimal +
converted.substr(decimalIndex + 1);
converted = settings.front ?
settings.currency + converted :
converted + settings.currency;
return converted;
};
// Add supplied thousand separator to supplied value
Money.prototype.addThousandSeparator = function(value, symbol) {
return value.toString().replace(/\B(?=(\d{3})+(?!\d))/g, symbol);
};
// PROVIDER is the core recipe type - VALUE, CONSTANT, SERVICE & FACTORY
// are all effectively syntactic sugar built on top of the PROVIDER construct
// One of the advantages of the PROVIDER is that we can configure it before the
// application starts (see config below).
app.provider('money', function MoneyProvider() {
var locale;
// Function called by the config to set up the provider
this.setLocale = function(value) {
locale = value;
};
// All providers need to implement a $get method which returns
// an instance of the custom class which constitutes the service
this.$get = function moneyFactory() {
return new Money(locale);
};
});
// We can configure a PROVIDER on application initialisation.
app.config(['moneyProvider', function(moneyProvider) {
moneyProvider.setLocale('uk');
//moneyProvider.setLocale('eu');
}]);
// The ubiquitous controller
app.controller('mainCtrl', function($scope, title, strapline, random, money) {
// Plain old VALUE(s)
this.title = title;
this.strapline = strapline;
this.count = 0;
// Compute values using our money provider
this.earn = money.convertValue(random); // random is computed @ runtime
this.earned = money.convertValue(0);
this.handleClick = function() {
this.count ++;
this.earned = money.convertValue(random * this.count);
};
});
Demo de trabajo .
Esta respuesta aborda el tema / pregunta
O
básicamente lo que pasa es
Cuando realiza un factory()
conjunto que function
proporcionó en el segundo argumento para el proveedor $get
y lo devuelve ( provider(name, {$get:factoryFn })
), todo lo que obtiene es, provider
pero no hay otra propiedad / método que no sea$get
eso provider
(significa que no puede configurar esto)
Código fuente de fábrica
function factory(name, factoryFn, enforce) {
return provider(name, {
$get: enforce !== false ? enforceReturnValue(name, factoryFn) : factoryFn
});
};
Al hacer un service()
retorno, proporciona una fábrica () con un function
que inyecta el constructor
(devuelve la instancia del constructor que proporcionó en su servicio) y lo devuelve
Código fuente de servicio
function service(name, constructor) {
return factory(name, ['$injector', function($injector) {
return $injector.instantiate(constructor);
}]);
};
Básicamente, en ambos casos, eventualmente obtienes un proveedor $ se establece en la función que proporcionaste, pero puedes dar cualquier cosa adicional que $ get como originalmente puedes proporcionar en el proveedor () para el bloque de configuración
Conozco muchas respuestas excelentes, pero tengo que compartir mi experiencia de usar
1. service
para la mayoría de los casos de incumplimiento
2. factory
utilizado para crear el servicio en esa instancia específica
// factory.js ////////////////////////////
(function() {
'use strict';
angular
.module('myApp.services')
.factory('xFactory', xFactoryImp);
xFactoryImp.$inject = ['$http'];
function xFactoryImp($http) {
var fac = function (params) {
this._params = params; // used for query params
};
fac.prototype.nextPage = function () {
var url = "/_prc";
$http.get(url, {params: this._params}).success(function(data){ ...
}
return fac;
}
})();
// service.js //////////////////////////
(function() {
'use strict';
angular
.module('myApp.services')
.service('xService', xServiceImp);
xServiceImp.$inject = ['$http'];
function xServiceImp($http) {
this._params = {'model': 'account','mode': 'list'};
this.nextPage = function () {
var url = "/_prc";
$http.get(url, {params: this._params}).success(function(data){ ...
}
}
})();
y usando:
controller: ['xFactory', 'xService', function(xFactory, xService){
// books = new instance of xFactory for query 'book' model
var books = new xFactory({'model': 'book', 'mode': 'list'});
// accounts = new instance of xFactory for query 'accounts' model
var accounts = new xFactory({'model': 'account', 'mode': 'list'});
// accounts2 = accounts variable
var accounts2 = xService;
...
Poco tarde para la fiesta. Pero pensé que esto es más útil para quienes deseen aprender (o tener claridad) sobre el desarrollo de los Servicios personalizados de Angular JS utilizando metodologías de fábrica, servicio y proveedor.
Encontré este video que explica claramente sobre las metodologías de fábrica, servicio y proveedor para desarrollar AngularJS Custom Services:
https://www.youtube.com/watch?v=oUXku28ex-M
Código fuente: http://www.techcbt.com/Post/353/Angular-JS-basics/how-to-develop-angularjs-custom-service
El código publicado aquí se copia directamente de la fuente anterior, para beneficio de los lectores.
El código para el servicio personalizado basado en "fábrica" es el siguiente (que va con las versiones de sincronización y asíncrona junto con la llamada al servicio http):
var app = angular.module("app", []);
app.controller('emp', ['$scope', 'calcFactory',
function($scope, calcFactory) {
$scope.a = 10;
$scope.b = 20;
$scope.doSum = function() {
//$scope.sum = calcFactory.getSum($scope.a, $scope.b); //synchronous
calcFactory.getSum($scope.a, $scope.b, function(r) { //aynchronous
$scope.sum = r;
});
};
}
]);
app.factory('calcFactory', ['$http', '$log',
function($http, $log) {
$log.log("instantiating calcFactory..");
var oCalcService = {};
//oCalcService.getSum = function(a,b){
// return parseInt(a) + parseInt(b);
//};
//oCalcService.getSum = function(a, b, cb){
// var s = parseInt(a) + parseInt(b);
// cb(s);
//};
oCalcService.getSum = function(a, b, cb) { //using http service
$http({
url: 'http://localhost:4467/Sum?a=' + a + '&b=' + b,
method: 'GET'
}).then(function(resp) {
$log.log(resp.data);
cb(resp.data);
}, function(resp) {
$log.error("ERROR occurred");
});
};
return oCalcService;
}
]);
El código para la metodología de "servicio" para Servicios personalizados (esto es bastante similar a 'fábrica', pero diferente desde el punto de vista de la sintaxis):
var app = angular.module("app", []);
app.controller('emp', ['$scope', 'calcService', function($scope, calcService){
$scope.a = 10;
$scope.b = 20;
$scope.doSum = function(){
//$scope.sum = calcService.getSum($scope.a, $scope.b);
calcService.getSum($scope.a, $scope.b, function(r){
$scope.sum = r;
});
};
}]);
app.service('calcService', ['$http', '$log', function($http, $log){
$log.log("instantiating calcService..");
//this.getSum = function(a,b){
// return parseInt(a) + parseInt(b);
//};
//this.getSum = function(a, b, cb){
// var s = parseInt(a) + parseInt(b);
// cb(s);
//};
this.getSum = function(a, b, cb){
$http({
url: 'http://localhost:4467/Sum?a=' + a + '&b=' + b,
method: 'GET'
}).then(function(resp){
$log.log(resp.data);
cb(resp.data);
},function(resp){
$log.error("ERROR occurred");
});
};
}]);
El código para la metodología de "proveedor" para los Servicios personalizados (esto es necesario, si desea desarrollar un servicio que pueda configurarse):
var app = angular.module("app", []);
app.controller('emp', ['$scope', 'calcService', function($scope, calcService){
$scope.a = 10;
$scope.b = 20;
$scope.doSum = function(){
//$scope.sum = calcService.getSum($scope.a, $scope.b);
calcService.getSum($scope.a, $scope.b, function(r){
$scope.sum = r;
});
};
}]);
app.provider('calcService', function(){
var baseUrl = '';
this.config = function(url){
baseUrl = url;
};
this.$get = ['$log', '$http', function($log, $http){
$log.log("instantiating calcService...")
var oCalcService = {};
//oCalcService.getSum = function(a,b){
// return parseInt(a) + parseInt(b);
//};
//oCalcService.getSum = function(a, b, cb){
// var s = parseInt(a) + parseInt(b);
// cb(s);
//};
oCalcService.getSum = function(a, b, cb){
$http({
url: baseUrl + '/Sum?a=' + a + '&b=' + b,
method: 'GET'
}).then(function(resp){
$log.log(resp.data);
cb(resp.data);
},function(resp){
$log.error("ERROR occurred");
});
};
return oCalcService;
}];
});
app.config(['calcServiceProvider', function(calcServiceProvider){
calcServiceProvider.config("http://localhost:4467");
}]);
Finalmente, la interfaz de usuario que funciona con cualquiera de los servicios anteriores:
<html>
<head>
<title></title>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.15/angular.min.js" ></script>
<script type="text/javascript" src="t03.js"></script>
</head>
<body ng-app="app">
<div ng-controller="emp">
<div>
Value of a is {{a}},
but you can change
<input type=text ng-model="a" /> <br>
Value of b is {{b}},
but you can change
<input type=text ng-model="b" /> <br>
</div>
Sum = {{sum}}<br>
<button ng-click="doSum()">Calculate</button>
</div>
</body>
</html>
Solo para aclarar las cosas, desde la fuente AngularJS, puede ver que un servicio solo llama a la función de fábrica que a su vez llama a la función del proveedor:
function factory(name, factoryFn) {
return provider(name, { $get: factoryFn });
}
function service(name, constructor) {
return factory(name, ['$injector', function($injector) {
return $injector.instantiate(constructor);
}]);
}
Analicemos las tres formas de manejar la lógica de negocios en AngularJS de una manera simple: ( Inspirado en el curso Coursera AngularJS de Yaakov )
Servicio :
Sintaxis:
app.js
var app = angular.module('ServiceExample',[]);
var serviceExampleController =
app.controller('ServiceExampleController', ServiceExampleController);
var serviceExample = app.service('NameOfTheService', NameOfTheService);
ServiceExampleController.$inject = ['NameOfTheService'] //protects from minification of js files
function ServiceExampleController(NameOfTheService){
serviceExampleController = this;
serviceExampleController.data = NameOfTheService.getSomeData();
}
function NameOfTheService(){
nameOfTheService = this;
nameOfTheService.data = "Some Data";
nameOfTheService.getSomeData = function(){
return nameOfTheService.data;
}
}
index.html
<div ng-controller = "ServiceExampleController as serviceExample">
{{serviceExample.data}}
</div>
Características del servicio:
FÁBRICA
Primero echemos un vistazo a la sintaxis:
app.js :
var app = angular.module('FactoryExample',[]);
var factoryController = app.controller('FactoryController', FactoryController);
var factoryExampleOne = app.factory('NameOfTheFactoryOne', NameOfTheFactoryOne);
var factoryExampleTwo = app.factory('NameOfTheFactoryTwo', NameOfTheFactoryTwo);
//first implementation where it returns a function
function NameOfTheFactoryOne(){
var factory = function(){
return new SomeService();
}
return factory;
}
//second implementation where an object literal would be returned
function NameOfTheFactoryTwo(){
var factory = {
getSomeService : function(){
return new SomeService();
}
};
return factory;
}
Ahora usando los dos anteriores en el controlador:
var factoryOne = NameOfTheFactoryOne() //since it returns a function
factoryOne.someMethod();
var factoryTwo = NameOfTheFactoryTwo.getSomeService(); //accessing the object
factoryTwo.someMethod();
Características de la fábrica:
.service()
método es una fábrica que siempre produce el mismo tipo de servicio, que es un singleton, y sin ninguna forma fácil de configurar su comportamiento. Ese .service()
método generalmente se usa como un acceso directo para algo que no requiere ninguna configuración.PROVEEDOR
Veamos nuevamente la sintaxis primero:
angular.module('ProviderModule', [])
.controller('ProviderModuleController', ProviderModuleController)
.provider('ServiceProvider', ServiceProvider)
.config(Config); //optional
Config.$inject = ['ServiceProvider'];
function Config(ServiceProvider) {
ServiceProvider.defaults.maxItems = 10; //some default value
}
ProviderModuleController.$inject = ['ServiceProvider'];
function ProviderModuleController(ServiceProvider) {
//some methods
}
function ServiceProvider() {
var provider = this;
provider.defaults = {
maxItems: 10
};
provider.$get = function () {
var someList = new someListService(provider.defaults.maxItems);
return someList;
};
}
}
Características del proveedor:
.service
u otro .factory
método.$get
es una función que se adjunta directamente a la instancia del proveedor. Esa función es una función de fábrica . En otras palabras, es como el que usamos para proporcionar al .factory
método. En esa función, creamos nuestro propio servicio. Esta $get
propiedad, que es una función, es lo que hace al proveedor un proveedor . AngularJS espera que el proveedor tenga una propiedad $ get cuyo valor es una función que Angular tratará como una función de fábrica. Pero lo que hace que toda la configuración de este proveedor sea muy especial es el hecho de que podemos proporcionar algún config
objeto dentro del proveedor de servicios, y eso generalmente viene con valores predeterminados que luego podemos sobrescribir en el paso, donde podemos configurar toda la aplicación.Fábrica: la fábrica en la que realmente crea un objeto dentro de la fábrica y la devuelve.
servicio: el servicio solo tiene una función estándar que utiliza la palabra clave this para definir la función.
proveedor: El proveedor tiene un $ get que usted define y puede usarse para obtener el objeto que devuelve los datos.
Esencialmente, Proveedor, Fábrica y Servicio son todos Servicios. Una Fábrica es un caso especial de un Servicio cuando todo lo que necesita es una función $ get (), que le permite escribir con menos código.
Las principales diferencias entre servicios, fábricas y proveedores son sus complejidades. Los servicios son la forma más simple, las fábricas son un poco más robustas y los proveedores son configurables en tiempo de ejecución.
Aquí hay un resumen de cuándo usar cada uno:
Fábrica : el valor que proporciona debe calcularse en función de otros datos.
Servicio : está devolviendo un objeto con métodos.
Proveedor : desea poder configurar, durante la fase de configuración, el objeto que se va a crear antes de crearlo. Use el proveedor principalmente en la configuración de la aplicación, antes de que la aplicación se haya inicializado completamente.
1.Los servicios son objetos únicos que se crean cuando es necesario y nunca se limpian hasta el final del ciclo de vida de la aplicación (cuando el navegador está cerrado). Los controladores se destruyen y limpian cuando ya no se necesitan.
2. La forma más fácil de crear un servicio es mediante el método factory (). El método factory () nos permite definir un servicio devolviendo un objeto que contiene funciones de servicio y datos de servicio. La función de definición de servicio es donde colocamos nuestros servicios inyectables, como $ http y $ q. Ex:
angular.module('myApp.services')
.factory('User', function($http) { // injectables go here
var backendUrl = "http://localhost:3000"; var service = {
// our factory definition
user: {},
setName: function(newName) {
service.user['name'] = newName;
},
setEmail: function(newEmail) { service.user['email'] = newEmail;
},
save: function() {
return $http.post(backendUrl + '/users', { user: service.user
}); }
};
return service; });
Usando la fábrica () en nuestra aplicación
Es fácil usar la fábrica en nuestra aplicación, ya que simplemente podemos inyectarla donde la necesitamos en tiempo de ejecución.
angular.module('myApp')
.controller('MainController', function($scope, User) {
$scope.saveUser = User.save;
});
El azúcar sintáctico es la diferencia . Solo se necesita proveedor. O, en otras palabras, el único proveedor es el angular real, todos los demás se derivan (para reducir el código). También hay una versión simple, llamada Value () que devuelve solo el valor, sin cálculo ni función. ¡Incluso el valor se deriva del proveedor!
Entonces, ¿por qué tales complicaciones, por qué no podemos simplemente usar el proveedor y olvidar todo lo demás? Se supone que nos ayuda a escribir código fácilmente y a comunicarnos mejor. Y la respuesta de toungue-in-cheek sería, cuanto más complejo se vuelva, mejor será vender un marco.
La inyección angular nos da la primera pista para llegar a esta conclusión.
"$ injector se utiliza para recuperar instancias de objetos según lo definido por el proveedor ", no servicio, no fábrica sino proveedor.
Y una mejor respuesta sería esta: "Un servicio angular es creado por una fábrica de servicios. Estas fábricas de servicios son funciones que, a su vez, son creadas por un proveedor de servicios. Los proveedores de servicios son funciones de constructor. Cuando se instancian, deben contener una propiedad llamado $ get, que mantiene la función de fábrica de servicios ".
Entonces el proveedor principal y el inyector y todo encajará en su lugar :). Y se vuelve interesante en Typecript cuando $ get puede implementarse en un proveedor heredando de IServiceProvider.