Qué son las factorías de AngularJS, factory en la terminología Angular. Para qué sirven, qué rasgos las caracterizan y cómo crearlas.
Hasta el momento en el Manual de AngularJS nuestro código Javascript lo hemos colocado principalmente dentro de controladores. Nos han servido perfectamente para nuestras aplicaciones, pero ya hemos podido observar que en ciertas ocasiones los controladores no se comportan como nosotros podríamos desear.
Aspectos de esta problemática han debido de quedar claros si te lees con calma el último artículo, donde aprendimos a crear controladores en paralelo y donde introdujimos $location. En ese ejercicio vimos que los controladores se invocan con cada vista donde los estemos usando, ejecutando la función que los define cada vez que se carga la vista. Por ese motivo todos los datos que se se inicializan en los controladores se vuelven a poner a sus valores predeterminados cuando cargo cualquier vista que trabaje con ese controlador.
Terminamos el artículo pasado mencionando que las factorías nos podrían solucionar la pérdida de datos de los controladores cuando cambiamos la vista. De modo que vamos a aprender a crearlas y utilizarlas.
Pero ese no es el único caso donde encontrarás utilidad en las factorías. Por ejemplo, algunas de las necesidades que podríamos tener y que los controladores no nos resuelven son:
1) Compartir datos entre varios controladores, lo que permite tener aplicaciones de verdad, capaces de memorizar estados entre varias de sus pantallas.
2) Compartir datos entre varias vistas distintas. Por supuesto, sin usar las temidas variables globales. Eso es justamente lo que vimos en el artículo anterior, que ya tengas uno o varios controladores, no comparten ni se memorizan estados al pasar de una vista a otra.
Además, quizás el más representativo de los usos de las factorías es:
3) Empaquetar operaciones comunes a varios controladores (por ejemplo en una aplicación de facturación podríamos necesitar calcular el IVA o acceder a los distintos tipos de IVA en varios puntos del sistema). Por supuesto, no queremos colocar el código de esos cálculos u operaciones repetido en todos los controladores que deben utilizarlos y tampoco deseamos crear funciones con ámbito global.
Qué son las factorías
Las factorías son como contenedores de código que podemos usar en nuestros sitios desarrollados con AngularJS. Son un tipo de servicio, "service" en Angular, con el que podemos implementar librerías de funciones o almacenar datos.
Cuando las usamos tienen la particularidad de devolvernos un dato, de cualquier tipo. Lo común es que nos devuelvan un objeto de Javascript donde podremos encontrar datos (propiedades) y operaciones (métodos). Con diferencia de los controladores, las factorías tienen la característica de ser instanciados una única vez dentro de las aplicaciones, por lo que no pierden su estado. Por tanto, son un buen candidato para almacenar datos en nuestra aplicación que queramos usar a lo largo de varios controladores, sin que se inicialicen de nuevo cuando se cambia de vista.
Angular consigue ese comportamiento usando el patrón "Singleton" que básicamente quiere decir que, cada vez que se necesite un objeto de ese tipo, se enviará la misma instancia de ese objeto en lugar de volver a instanciar un ejemplar.
Notas sobre los "services" en Angular
Los "services" en AngularJS incluyen tanto factorías como servicios. Más adelante veremos la diferencia. Lo que queremos mencionar ahora es que estos contenedores de código ya los hemos usado en diversas ocasiones y quizás podrás entender su utilidad mejor si analizamos cosas que ya conoces.
Por ejemplo, cuando estás haciendo Ajax, por los métodos que hemos conocido hasta el momento, usamos $http. Éste no es más que un service de Angular que engloba toda la funcionalidad necesaria para realizar solicitudes asíncronas a un servidor.
Por tanto, algo como Ajax, que se supone que puedes querer realizar a lo largo de tu aplicación en varias partes del código, se ha separado a un "servicio". Esto quiere decir que, cuando quieras hacer Ajax, tendrás que usar el código del "service" $http.
Los servicios y factorías que desees usar en tus controladores o módulos deberás inyectarlos como has visto hacer en diversas partes de nuestros ejemplos. No te preocupes si no lo recuerdas porque a continuación veremos ejemplos.
Ejemplo de factoría en AngularJS
Ahora nos vamos a poner manos a la obra creando nuestra primera factoría. Para ello vamos a continuar con el ejercicio que hemos visto en los artículos anteriores del Manual de AngularJS. Como recordarás, queremos implementar un sistema que nos memorice cierta información de nuestra aplicación a lo largo de diversas vistas.
Implementamos factorías con el método factory() que depende del módulo (objeto module) de nuestra aplicación.
Osea, como cualquier aplicación de Angular, lo primero crearás tu "module" principal:
angular.module("app", ["ngRoute"])
Y sobre el objeto que devuelve esa operación crearemos las factorías.
.factory("descargasFactory", function(){
var descargasRealizadas = ["Manual de Javascript", "Manual de jQuery", "Manual de AngularJS"];
var interfaz = {
nombre: "Manolo",
getDescargas: function(){
return descargasRealizadas;
},
nuevaDescarga: function(descarga){
descargasRealizadas.push(descarga);
}
}
return interfaz;
})
Esta factoría se llama "descargasFactory". El nombre lo hemos definido en la llamada al método factory. Acuérdate de este nombre, pues luego lo tendrás que usar al inyectar la dependencia de esta factoría en tus controladores.
Ese código tiene una serie de detalles interesantes, desde el punto de vista de Angular y también desde el de Javascript en general. Estudiar el código anterior con detalle es suficiente para un único artículo, porque entran en juego diversos conceptos de la programación orientada a objetos en Javascript. De todos modos, te vamos a resumir un poco lo que encuentras.
- Lo más destacado sobre las factorías en AngularJS lo encuentras en la última línea: "return interfaz;" Todas las factorías deben devolver algo. Lo que sea, aunque lo habitual como dijimos es devolver un objeto. Por definición debe de ser así en AngularJS.
- Aquello que devuelves es lo que se conoce desde fuera de la factoría. Por decirlo de otra manera, es la interfaz pública de uso de esa factoría. Por eso hemos llamado a la variable que devolvemos en el return "interfaz", porque es la serie de propiedades y métodos que estás haciendo público para todo el mundo que use esa factoría. Lógicamente, esa "interfaz" no es más que una manera nuestra de llamar a la variable y tú usarás la que quieras.
- Pero fíjate que la variable "descargasRealizadas" es privada a la factoría, pues no se devuelve en la interfaz. Por tanto, ese array no podrá ser accesible desde fuera de la factoría. Podemos entenderlo como una propiedad privada.
- Para acceder al array "descargasRealizadas" se hará uso de los métodos definidos en "interfaz": getDescargas() y nuevaDescarga(). Esos métodos son públicos, por haberlos definido en la interfaz que devolvemos en la función de la factoría y se podrán acceder desde cualquier lugar donde tengamos disponible la factoría.
- Sin embargo no todos los datos que vamos a manejar en las factorías necesitamos hacerlos privados. En concreto encontrarás, la propiedad "nombre" que está dentro de nuestra interfaz y por lo tanto es pública y podrá accederse tal cual desde fuera de la factoría.
Usar una factoría
Ahora podemos ver cómo usar la factoría que acabamos de realizar. El procedimiento es tan simple como, una vez definida, inyectarla en el controlador donde la queremos usar. Usamos el sistema de inyección de dependencias que ya conoces.
Al crear la función del controlador debemos definir un parámetro que se llame exactamente igual al nombre que le has dado en la factoría. En este caso el parámetro que inyectamos en el controlador se llama "descargasFactory" pues así habíamos llamado a la factoría al crearla.
Echa ahora un vistazo a un controlador que usa esta factoría.
.controller("appCtrl", function(descargasFactory){
var vm = this;
vm.nombre = descargasFactory.nombre;
vm.descargas = descargasFactory.getDescargas();
vm.funciones = {
guardarNombre: function(){
descargasFactory.nombre = vm.nombre;
},
agregarDescarga: function(){
descargasFactory.nuevaDescarga(vm.nombreNuevaDescarga);
vm.mensaje = "Descarga agregada";
},
borrarMensaje: function(){
vm.mensaje = "";
}
}
});
Dentro de nuestro controlador la variable descargasFactory que recibimos como parámetro contiene todos los datos y funciones que hemos definido en la interfaz pública de nuestra factoría.
Por tanto:
- descargasFactory.nombre contendrá la propiedad "nombre" definida en la factory.
- descargasFactory.nuevaDescarga() o descargasFactory.getDescargas() serán llamadas a los métodos que habíamos definido en la factoría.
vm.nombre = descargasFactory.nombre;
vm.descargas = descargasFactory.getDescargas();
Creo que con todo esto queda explicado el trabajo con las factorías en AngularJS. Ahora faltaría un poco de tiempo por tu parte para poder ponerlo en práctica.
Alberto Basalo
Alberto Basalo es experto en Angular y otras tecnologías basadas en Javascript,...