Saltar al contenido

AngularJS Paso a Paso: Servicios

< Vuelve a..: AngularJS Paso a Paso: Controladores

Esta es la tercera de una serie de entradas de blog destinadas a darte una probada de AngularJS sin mucha lectura. Si no has leído los dos primeros, tal vez quieras empezar con este post. Y si has estado siguiendo, y te gusta lo que ves, asegúrate de revisar el curso de Fundamentos de AngularJS para una introducción mucho más profunda a Angular. A lo largo de esta serie, enlazo con los violines en jsFiddle. Recomiendo abrirlos en pestañas o ventanas separadas para que puedan seguirlos fácilmente.

AngularJS Paso a Paso: Servicios
AngularJS Paso a Paso: Servicios

¿Qué es un servicio en AngularJS?

La palabra servicio es un término sobrecargado. Quiero aclarar que cuando uso el término servicio, en realidad me refiero a un simple objeto que hace algún tipo de trabajo. (Para una introducción a los objetos de JavaScript mira este post). El tipo de servicio del que hablo es típicamente apátrida y encapsula algún tipo de funcionalidad. Los principales miembros públicos de un servicio son funciones no propiedades, especialmente porque tener propiedades sugiere algún tipo de estado. No me refiero a los servicios «over-the-wire» como los servicios web, los servicios RESTful o los servicios WCF, aunque estos sí entran dentro de mi definición de servicio. Cuando digo servicio, ese servicio puede o no ser accedido a través de la red. La mayoría de las veces, son sólo objetos llamados directamente dentro de su JavaScript.

Por qué necesitamos los servicios de AngularJS

En mi anterior post sobre los controladores AngularJS, hablé un poco sobre la necesidad de separar las preocupaciones en las aplicaciones JavaScript de hoy en día, que están mucho más involucradas que las de hace una década. Con la cantidad de JavaScript necesaria en una aplicación de hoy en día, la arquitectura de su JavaScript toma mucha más importancia. Dos de los cinco sólidos principios del diseño orientado a objetos están directamente relacionados con los servicios: el Principio de Responsabilidad Única (SRP) y el Principio de Inversión de la Dependencia (DIP).

El principio de responsabilidad única enseña que cada objeto debe tener una responsabilidad única. Si utilizamos el ejemplo de un controlador, su responsabilidad consiste esencialmente en conectar el alcance (que sostiene sus modelos) a su punto de vista; es esencialmente el puente entre sus modelos y sus puntos de vista. Si su controlador también es responsable de hacer llamadas de ajax para obtener y actualizar datos, esto es una violación del principio de SRP. Una lógica como esa (y otras lógicas de negocios) debería en cambio ser abstraída en un servicio separado, y luego inyectada en los objetos que necesitan usarla.

Aquí es donde entra en juego el Principio de Inversión de la Dependencia. El DIP establece que los objetos deben depender de las abstracciones, no de las concreciones. En lenguajes como C# y Java, esto significa que sus objetos dependen de Interfaces en lugar de Clases. En JavaScript se puede mirar cualquier parámetro de cualquier función (función constructora o no) como una abstracción, ya que se puede pasar cualquier objeto por ese parámetro siempre y cuando tenga los miembros en él que se usan dentro de ese método. Pero la clave aquí es la capacidad de utilizar la inyección de dependencia – la capacidad de inyectar en otros objetos. Esto significa que todos sus controladores, directivas, filtros y otros servicios deberían tomar cualquier dependencia externa como parámetro durante la construcción. Esto le permite tener un código acoplado libremente y tiene el beneficio añadido de hacer las pruebas de la unidad mucho más fáciles.

Creando un servicio personalizado en AngularJS

Empecemos con este violín que es donde terminamos en mi último post. Noten que en el controlador estamos configurando $scope.users1 a un conjunto estático de usuarios. Cambiémoslo para que se acceda a él a través de una llamada de Ajax. Pero, por supuesto, esto no debería ser responsabilidad de nuestro controlador, así que vamos a crear un servicio para manejarlo, y nuestro controlador llamará al servicio.

Para crear el servicio, usamos el método de fábrica del módulo. Añade este código al fondo del JavaScript del violín:

myModule.factory($0027userRepository$0027, function() { return { };});

Con este código, estamos creando un nuevo servicio llamado userRepository. Fíjese que el primer parámetro del método de la fábrica es el nombre que queremos dar a nuestro servicio, y también fíjese que el segundo parámetro del método de la fábrica es una función. El objeto devuelto por esa función representa nuestro servicio. En este caso simplemente estamos devolviendo un objeto vacío literalmente, por lo que nuestro servicio no hace nada. Así que esto plantea la pregunta: si simplemente devolvemos el objeto que va a ser nuestro servicio, ¿por qué necesitamos toda la otra ceremonia de myModule.factory? Usando el método de la fábrica estamos registrando nuestro servicio con Angular. Esto permite que el contenedor de inyección de dependencia de Angular inyecte una instancia de nuestro servicio cuando lo solicitamos en otros lugares. Lo demostraré en breve cuando usemos este servicio en nuestro controlador.

Por ahora, dejemos que nuestro servicio exponga nuestro conjunto estático de usuarios. Actualiza esa declaración de retorno para que se vea así:

retorno { getAllUsers: function() { return [ { firstName: $0027Jane$0027, apellido: $0027Doe$0027, edad: 29 }, { nombre: $0027John$0027, apellido: $0027Doe$0027, edad: 32 } ]; }};

Noten que ahora estamos devolviendo un objeto con una función «getAllUsers()». Esta función devuelve nuestra matriz. Tu violín debería verse ahora algo como esto. Así que ahora, podemos llamar a userRepository.getAllUsers() para obtener nuestro array de usuarios. Veamos cómo lo haríamos en nuestro controlador.

Usando sus servicios personalizados

Manejar la inyección de dependencia es algo que AngularJS hace muy bien. Es increíblemente simple, pero muy efectivo. Lo primero que tienes que hacer es registrar tu servicio con Angular que acabamos de demostrar arriba. Ahora todo lo que tienes que hacer para usarlo es inyectarlo. Pero antes de hacer eso, vamos a eliminar el código duro de la matriz de usuarios que está en nuestro controlador. De esa manera puedes ver que la página no está funcionando actualmente. Quita la línea «$scope.users = …» de tu controlador, y notarás que los datos desaparecen de la página. Ahora inyecta nuestro nuevo servicio personalizado añadiéndolo como un parámetro donde ya estamos inyectando $scope. Cambia la primera línea de la definición de tu controlador para que se vea así:

myModule.controller($0027miControlador$0027, function($scope, userRepository) {

Eso es todo. Angular se encargará ahora de crear un depósito de usuario para que lo uses en tu controlador. Ahora vamos a usar nuestro repositorio. Añade esta línea dentro del controlador:

$scope.users = userRepository.getAllUsers();

Tu violín debería verse ahora algo así. Y ahora echa un vistazo a la salida, ahora tenemos a nuestros usuarios apareciendo en nuestra página de nuevo, sólo que esta vez, los datos vienen de nuestro servicio. ¡Felicidades! Acabas de crear y utilizar tu primer servicio personalizado.

Usando un servicio de AngularJs incorporado

Ahora echemos otro vistazo a la utilización de un servicio angular incorporado, y sustituya nuestra matriz de usuarios estáticos por una llamada http. Para ello he creado una base de datos de MongoLab con una colección de usuarios. Puedes ver los datos aquí. Vamos a darle a esa URL con el servicio AngularJS $http. Primero, inyecta el servicio $http en nuestro nuevo servicio:

myModule.factory($0027userRepository$0027, function($http) {

Ahora, reemplaza el código dentro del método getAllUsers en el servicio userRepository con esto:

var url = "https://api.mongolab.com/api/1/databases/angularjs-intro/collections/users?apiKey=terrPcifZzn01_ImGsFOIZ96SwvSXgN9";return $http.get(url);

Por supuesto, ahora que hemos hecho ese cambio estamos recibiendo una salida muy extraña en nuestra página. Eso es porque esencialmente estamos devolviendo una promesa de nuestro servicio, no los datos reales. La llamada $http es asincrónica, y lo que se está devolviendo es un objeto que tiene funciones de éxito y fracaso en él. Necesitaremos utilizar esto dentro de nuestro controlador, así que cambia la línea «$scope.users =» en el controlador para que se vea así:

userRepository.getAllUsers().success(function(users) {$scope.users = users});

Ahora los datos deberían aparecer en la página web de nuevo, sólo que esta vez vienen de una llamada http a los mongoles! Bastante impresionante, ¿no? Tu violín debería verse así. Si miras la línea que acabamos de modificar en nuestro controlador, puedes ver que estamos especificando una función que se llamará cuando la llamada http/ajax tenga éxito, y esa función toma los datos devueltos de la llamada ajax. Dentro de esa función estamos configurando $scope.users a los datos de la llamada ajax.

Hay muchos servicios incorporados de AngularJS que se discuten en el curso de Fundamentos de AngularJS, y la creación de servicios personalizados también se cubre con mayor detalle. Compruébalo si quieres saber más.

El siguiente: Enrutamiento

Para convertir una aplicación de JavaScript en una verdadera aplicación de una sola página (SPA), es necesario manejar el enrutamiento del lado del cliente (haciendo que parezca que el usuario está navegando hacia nuevas páginas, cuando en realidad todo está sucediendo localmente, y respaldado por llamadas de ajax). En el próximo post les presentaré el soporte incorporado de AngularJS para el enrutamiento y cómo pueden usarlo para crear sus propios SPA.

¿Necesitas un repaso de los fundamentos de Angular? Echa un vistazo a nuestra completa guía de Angular hoy.