Por fin llegamos al provider
el último de los tipos de servicios en AngularJS y con el que espero que se entiendan por fin las diferencias entre todos ellos.
Un provider
es como un factory
pero permite que se configure antes de crear el valor del servicio. En el tema anterior vimos el ejemplo del servicio de hash
que se configuraba a través de un value
llamado algoritmo
. Aunque el ejemplo funciona, la verdad es que es un poco chapucero ya que aparentemente no hay relación entre algoritmo
y hash
. La relación entre ambos queda poco orientada al objeto. El provider
viene en nuestra ayuda creando un objeto previo que permite configurar el factory antes de que cree el valor del servicio. Este nuevo objeto se llama Provider y en un bloque config
podremos acceder a él para poder configurar nuestro servicio.
Como siempre hacemos, vamos a ver un ejemplo:
function HashProvider() { var _algoritmo=""; this.setAlgoritmo=function(algoritmo) { _algoritmo=algoritmo; }; this.$get=function() { var hashFunction; if (_algoritmo==="MD5") { hashFunction=CryptoJS.MD5; } else if (_algoritmo==="SHA-1") { hashFunction=CryptoJS.SHA1; } else if (_algoritmo==="SHA-2-256") { hashFunction=CryptoJS.SHA256; } else if (_algoritmo==="SHA-2-512") { hashFunction=CryptoJS.SHA512; } else { throw Error("El tipo de algoritmo no es válido:"+_algoritmo); } var hash=function(message) { var objHashResult=hashFunction(message); var strHashResult=objHashResult.toString(CryptoJS.enc.Base64); return strHashResult; } return hash; } } app.provider("hash",HashProvider);
HashProvider
. Posteriormente AngularJS creará una instancia de esta clase.algoritmo
la cual contendrá el algoritmo a usar.$get
. Es una obligación que impone AngularJS para que él sepa cuál es el método factory. Podemos ver que este método es exactamente igual al del tema anterior de factory excepto que ahora usa la propiedad privada _algoritmo
en vez de llamar al servicio algoritmo
. Éste es el método que en el tema 3.4 Tipos de servicios llamábamos “factory-provider”.provider
con el nombre hash
y le pasamos como argumento el nombre de la clase HashProvider
.
Resumiendo, hemos creado una clase JavaScript llamada HashProvider
con distintas propiedades y métodos 1) que permitirán configurar el factory-provider. El factory-provider es la función $get
del provider. Por lo tanto dicha función hará uso de las propiedades que se han definido en la clase 2).
Ya tenemos definido el provider pero ahora es necesario poder configurarlo para establecer cuál es el algoritmo a usar. Los bloques config son los únicos que permiten configurar el provider.
En el bloque config será necesario inyectar el provider , no el factory-provider para poder configurarlo.
app.config(["hashProvider",function(hashProvider) { hashProvider.setAlgoritmo("SHA-1"); }]);
setAlgoritmo
para configurar el algoritmo.
Por ejemplo si el servicio se llama “login” ,al inyectarlo en un bloque config habrá que poner “loginProvider”
Una vez configurado el provider en el bloque config ya podremos inyectar el servicio donde queramos , en un controlador, en otro servicio, en un bloque run, etc.
Volvamos ahora a repasar la diferencia entre los bloques config y los bloques run. Un bloque config sólo existe para poder configurar un provider y ninguno de los servicios está aún creado 3).
Mientras que en un bloque run todos los servicios ya está configurados y se pueden usar. Por ello el bloque run es más parecido a un método Main
mientras que el bloque config es más parecido a un trozo de código de preinicialización de la aplicación.
Ya hemos dicho que un provider está definido por 2 funciones:
AngularJS nos permite en ambas funciones que podamos inyectar dependencias aunque en cada uno de ellos de tipos distintos. Veamos qué podemos inyectar en cada uno de ellos:
constant
y otros providers pero definidos en otros módulos.constant
, value
, service
, factory
y factory-provider
Veamos ahora un pequeño ejemplo de ello:
app.constant("provincia","Madrid"); app.factory("municipio",function() { return "Mostoles"; }); app.provider("direccion",['provincia',function(provincia) { this.$get=['municipio',function(municipio) { return provincia+","+municipio; }] }]);
provincia
.municipio
El ejemplo no tiene mucho sentido pero se ha puesto simplemente para ver que se pueden inyectar dependencias en ambas funciones.
Otra cosa interesante de este ejemplo es que en vez de crear la función del Provider aparte , se ha definido como una función anónima de JavaScript. Son cosas que permite el propio lenguaje y no tienen nada que ver con AngularJS.
Aún nos queda un pequeño cambio por hacer para mejorar la arquitectura del ejemplo.Al configurar el provider hemos puesto directamente en el bloque config el algoritmo a usar:
app.config(["hashProvider",function(hashProvider) { hashProvider.setAlgoritmo("SHA-1"); }]);
Como todos sabemos no es buena idea poner texto fijos en el código, así que lo ideal es modificarlo añadiendo una constante de la siguiente forma:
app.constant("algoritmo","SHA-1"); app.config(["hashProvider","algoritmo",function(hashProvider,algoritmo) { hashProvider.setAlgoritmo(algoritmo); }]);
provider
llamado hash
con el valor de la constante.
Pero, ¿no habíamos hecho todo ésto para evitar usar la constante?!!!!!! Sí, y seguimos sin hacerlo. Lo que queríamos era que el provider
no usara directamente la constante y sigue sin hacerlo. Esta nueva constante realmente formaría parte de nuestra aplicación y no del provider
, así que lo que estamos mejorando es nuestra propia aplicación y no el provider
, que gracias a ser un provider
es perfectamente personalizable y no depende de ninguna constante.
Podemos ver ahora el ejemplo completo.
<!DOCTYPE html> <html ng-app="app"> <head> <script src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.19/angular.min.js"></script> <script src="script.js"></script> <script src="http://crypto-js.googlecode.com/svn/tags/3.1.2/build/rollups/md5.js"></script> <script src="http://crypto-js.googlecode.com/svn/tags/3.1.2/build/rollups/sha1.js"></script> <script src="http://crypto-js.googlecode.com/svn/tags/3.1.2/build/rollups/sha256.js"></script> <script src="http://crypto-js.googlecode.com/svn/tags/3.1.2/build/rollups/sha512.js"></script> <script src="http://crypto-js.googlecode.com/svn/tags/3.1.2/build/components/enc-base64-min.js"></script> </head> <body ng-controller="PruebaController"> Contraseña:<input ng-model="password" /> <br> El hash de la contraseña es: <br> {{getHash(password)}} </body> </html>
var app=angular.module("app",[]); app.constant("algoritmo","SHA-1"); function HashProvider() { var _algoritmo=""; this.setAlgoritmo=function(algoritmo) { _algoritmo=algoritmo; }; this.$get=function() { var hashFunction; if (_algoritmo==="MD5") { hashFunction=CryptoJS.MD5; } else if (_algoritmo==="SHA-1") { hashFunction=CryptoJS.SHA1; } else if (_algoritmo==="SHA-2-256") { hashFunction=CryptoJS.SHA256; } else if (_algoritmo==="SHA-2-512") { hashFunction=CryptoJS.SHA512; } else { throw Error("El tipo de algoritmo no es válido:"+_algoritmo); } var hash=function(message) { var objHashResult=hashFunction(message); var strHashResult=objHashResult.toString(CryptoJS.enc.Base64); return strHashResult; } return hash; } } app.provider("hash",HashProvider); app.config(["hashProvider","algoritmo",function(hashProvider,algoritmo) { hashProvider.setAlgoritmo(algoritmo); }]); app.controller("PruebaController",["$scope","hash",function($scope,hash) { $scope.password="s3cret"; $scope.getHash=function(message) { var hashResult=hash(message); return hashResult; } }]);
Ahora es hora de que vuelvas a repasar el tema 3.4 Tipos de servicios y ver si ahora lo entiendes todo perfectamente.