3.8 factory

En este tema vamos a ver el servicio de factory. La principal diferencia es que al método factory le pasamos ahora una función para que ésta retorne ahora el valor del servicio. Es decir que tenemos una función JavaScript que actúa como factoría, retornando la propia función de factoría el valor del servicio.

Para que se entienda, vamos a ver los ejemplos que teníamos con el value ahora como un factory.

var app=angular.module("app",[]);
 
app.factory("idioma",function() {
  return "es-es";
});
 
app.factory("matematicas_simples",function() { 
  return {
    sumar:function(a,b) {
      return a+b;
    },
    restar:function(a,b) {
      return a-b;
    }
  }
});
 
app.factory("radio",function() {
  return 10;
});
 
app.factory("area",function() {
  return function(radio) {
    return 3.1416*radio*radio;
  }
});
 
app.controller("PruebaController",["$scope","idioma","matematicas_simples","radio","area",function($scope,idioma,matematicas_simples,radio,area) {
  $scope.idioma=idioma;
  $scope.suma=matematicas_simples.sumar(3,6);
  $scope.area=area(radio);
}]);

  • Líneas 3-5: Ahora al servicio de “idioma”, le pasamos una función anónima y dicha función retorna el String “es-es”, que es el verdadero valor del sevicio. Es decir la función anónima es una factoría que crea el valor . Aunque crearlo es simplemente crear el String.
  • Líneas 7-16: El valor del servicio “matematicas_simples” era un objeto javaScript con los métodos sumar y restar. Al ser un factory ahora le pasamos la función anónima de factoría que retorna el objeto JavaScript.
  • Líneas 18-20: En el servicio de “radio” , la función anónima retorna el valor del servicio que es un 10.
  • Líneas 22-26: El valor del servicio “area” es una función que calcula el área, por lo tanto la función anónima debe retornar la función que calcula el área. Es decir es una función que retorna otra función.
  • Línea 28: Por último vemos que en el controlador se usan igual los servicios independientemente de si son value o factory, etc.

Inyectando dependencias

Al igual que en el service se podían inyectar dependencias en el constructor , en un factory se pueden inyectar dependencias en la función de factoría.

Vamos a hacer el ejemplo del tema anterior del service con la clase Rectangulo usando un servicio:

var app=angular.module("app",[]);

app.value("tamanyoInicialRectangulo",{
  ancho:2,
  alto:3
});

function Rectangulo(tamanyoInicial) {
  this.ancho=tamanyoInicial.ancho;
  this.alto=tamanyoInicial.alto;
  
  this.setAncho=function(ancho) {
    this.ancho=ancho;
  }
  
  this.setAlto=function(alto) {
    this.alto=alto;
  }  
  
  this.getArea=function() {
    return this.ancho * this.alto;
  }
}

app.factory("rectangulo",['tamanyoInicialRectangulo',function(tamanyoInicialRectangulo) {
  var rectangulo=new Rectangulo(tamanyoInicialRectangulo);
  
  return rectangulo;
}]);


app.controller("PruebaController",["$scope","rectangulo",function($scope,rectangulo) {
  $scope.area=rectangulo.getArea();
}]);

  • Línea 25: Como ya hemos visto en las funciones de los controladores, inyectamos la dependencia tamanyoInicialRectangulo simplemente añadiendo el array con el nombre y poniendo la variable como argumento de la función.
  • Línea 26: Aquí es cuando se ve realmente qué es una función de factoría. Estamos creando el valor del servicio al crear el objeto rectangulo. Vemos cómo le estamos pasando al constructor, el valor que inyectamos a la función.
  • Línea 28: como ya sabemos se debe retornar el valor del servicio.

La necesidad del factory

Vamos a explicar ahora un ejemplo en el que necesitamos la función factory ya que la creación del valor del servicio va a ser un poco compleja.

Vamos a suponer que necesitamos enviar el hash de una contraseña a un servidor. Vamos a tener un servicio de AngularJS llamado hash que es una función. Esta función aceptará como parámetro un String y nos retornará el hash en formato Base64. Pero queremos que este servicio sea reutilizable por lo que va a soportar varios tipos de funciones de Hash: MD5, SHA1, SHA2-256 y SHA-2-512.

Para implementar las distintas funciones de Hash vamos a usar la librería CryptoJS. Las distintas funciones de Hash que tiene son:

y la forma de generar el resultado en Base64 es mediante The Hasher Output usando la función: hash.toString(CryptoJS.enc.Base64)

Un ejemplo de todo ello es el siguiente:

<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/components/enc-base64-min.js"></script>
<script>
    var hash = CryptoJS.SHA256("Message");

    alert(hash.toString(CryptoJS.enc.Base64)); // L3dmip37+NWEi57rSnFFypTG7ZI25Kdz9tyvpRMrL5E=
</script>

Para configurar el servicio es necesario crear un value llamado algoritmo con el algoritmo que queremos usar.

app.value("algoritmo","SHA-1");
Es este caso hemos configurado el algoritmo para que sea “SHA-1”.

Gastar el servicio hash es tan sencillo como pasar el String y nos retorna otro.

app.controller("PruebaController",["$scope","hash",function($scope,hash) {
  $scope.password="s3cret";
  $scope.getHash=function(message) {
    var hashResult=hash(message);
    return hashResult;
  }
}]);

  • Línea 1: En el controlador inyectamos la función de hash llamada hash.
  • Línea 3: Se ha creado en el $scope una función llamada getHash que acepta un String como parámetro y retorna el resultado de llamar a la función de Hash.

El código HTML es el siguiente:

<!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&ntilde;a:<input ng-model="password" />
    <br>
    El hash de la contrase&ntilde;a es:
    <br>
    {{getHash(password)}}
  </body>
</html>

  • Líneas 7-11: Cargamos la librería con los 4 algoritmos de Hash a usar y la codificación en Base64.
  • Línea 15: Tenemos un tag <input> para que el usuario escriba la contraseña.
  • Línea 18: Se llama a la función getHash para que muestre el hash.

Ahora viene la parte interesante, tenemos que crear un servicio mediante un factory que según el valor del value “algoritmo” utilice una función de Hash u otra pero además queremos que el resultado de llamar a dicha función siempre esté codificado en Base64.

El factory

Como ya hemos dicho nuestro servicio llamado hash nos retornará una función que realiza el hash pero con el algoritmo configurable y el resultado codificado en Base64.

Veamos como queda la función:

app.factory("hash",['algoritmo',function(algoritmo) {
  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;
  
}]);

  • Línea 1: Inyectamos el valor del algoritmo a usar.
  • Líneas 4 a 14: En función del valor de algoritmo usamos una función u otra de Hash. Pero ésta no va a ser la verdadera función que vamos a retornar ya que el resultado de esta función es un Objeto pero nosotros queremos que sea un String codificado en Base64.
  • Línea 16: Ahora creamos la verdadera función , la cual es la que retornará el servicio.Y que es la que realmente será llamada desde el controlador. Vemos que acepta como único argumento el mensaje.
  • Línea 17: Llamamos a la función que verdaderamente genera el Hash
  • Línea 19: Ahora transformamos el objeto en un String en Base64 llamando al método toString
  • Línea 24: Aquí es donde la función factory retorna la función de hash que hemos creado.

Si has llegado hasta aquí y lo has entendido todo: ¡Felicidades! pero sino , tampoco pasa nada. Era un ejemplo para ver la potencia de las funciones factory y lo útiles que pueden llegar a ser. Así que lo realmente útil siempre es el factory ya que nos permite crear cualquier tipo de valor de un servicio por muy complejo que sea.

value vs service vs factory

Esta es la típica pregunta que aparece siempre en los foros de AngularJS y la respuesta es bastante sencilla. Realmente da igual cuál uses porque todos acaban siendo un provider para AngularJS. Pero aun así ¿cuál es más recomendable?

  1. Si tienes una clase de la que es necesario crear una instancia mejor usa un service: Sólo tendrás que pasarle el nombre de la clase y ya está.
  2. Si tienes directamente el valor, mejor usar el value: Solo tendrás que pasarle ese valor y ya está.
  3. Si no queda más remedio entonces usa el factory: Es lo más complejo de usar así que debería ser siempre la última opción.

En el siguiente tema por fin veremos el provider el cual tiene una funcionalidad extra al factory que si la necesitamos estaremos obligados a usarlo.

En esta discusión no hemos hablado del constant ya que éste es distinto a los 3 anteriores 1) puesto que permite ser inyectado en un bloque config y en un provider, cosa que ninguno de los 3 anteriores permite, por lo que si necesitamos esa funcionalidad deberemos usar obligatoriamente constant.

Referencias

1) value , service y factory
unidades/03_servicios/08_factory.txt · Última modificación: 2014/09/22 18:57 por admin
Ir hasta arriba
CC Attribution-Share Alike 3.0 Unported
chimeric.de = chi`s home Valid CSS Driven by DokuWiki do yourself a favour and use a real browser - get firefox!! Recent changes RSS feed Valid XHTML 1.0