Empleando servicios

En este artículo vamos a pasar a la práctica con el tema de los servicios. Para ello vamos a recrear una aplicación que ya hemos usado anteriormente, cuando hablábamos de compartir datos entre componentes. En este caso, la estructura de componente maestro – componentes esclavos la vamos a incluir toda en un módulo, en lugar de emplear dos módulos como hicimos anteriormente, para simplificar la aplicación, y poder centrarnos, así, en lo que nos atañe ahora, que son los servicios.

Crearemos un servicio a nivel de módulo, para que esté disponible para todos los componentes del módulo interesado. Este servicio se ocupará de algunas de las operaciones que hacemos con este formulario, como incluir un nuevo socio en la lista de socios, mostrar dicha lista o eliminar los socios que deseemos. Es decir. Todas las operaciones que, en el ejercicio que ya conocemos, se llevaban a cabo en el componente maestro se llevarán a cabo ahora a cargo del servicio que vamos a inyectar.

PREPARANDO EL ESCENARIO

Esto es un breve resumen de la parte que ya conocemos, en la que vamos a preparar el escenario con el componente maestro para gestionar los datos de los socios, así como los componentes esclavos necesarios, la barra de menús, la cabecera, etc. Empezamos creando un proyecto nuevo, así:

ng new ang-services -p asrv --routing true

Instalamos jQuery y Bootstrap en package.json, dentro del apartado Dependencies, así:

Modificamos .angular-cli.json para referenciar jQuery y Bootstrap (claves styles y scripts), así:

Debemos actualizar las dependencias, para que jQuery y Bootstrap se integren en el proyecto:

npm update

Modificaremos el archivo principal de estilos (src/styles.css), añadiéndole los mismos estilos que ya teníamos en el proyecto anterior:

Creamos los módulos que vamos a usar (sólo serán dos):

ng g m commons
ng g m members

Dentro del módulo commons creamos los componentes, ya típicos, para la barra de navegación, la página de inicio, la cabecera y la página de Not found, así:

ng g c commons/navbar --export
ng g c commons/header --export
ng g c commons/home
ng g c commons/notFound

Las vistas de estos componentes son las que ya conocemos del proyecto anterior, por lo que no las reporducimos aquí. Lo único que debes modificar es el rotulo en home.component.html y la ruta de la opción de socios en navbar.component.html.

En la envolvente de CommonsModule (commons.module.ts) añadimos la importación del RouterModule de Angular, tal como aprendimos en este artículo:

Dentro del módulo members creamos los tres componentes que vamos a usar: el componente maestro, que es el gestor de los socios, y los componentes esclavos para el formulario y el listado. Aún no les ponemos código alguno, sólo los creamos, así:

ng g c members/membersMaster
ng g c members/membersForm
ng g c members/membersList

En el enrutador (src/app/app-routing-module.ts) añadimos las rutas necesarias:

En la envoltura del módulo principal de la aplicación (AppModule), llamado app.module.ts, debemos asegurarnos de que se importen los dos módulos que vamos a emplear, así:

Dentro del directorio del módulo MembersModule (src/app/members) crearemos dos directorios llamados classes y services.

Dentro del directorio classes crearemos la clase member, así:

ng g class members/classes/member

Su código lo modificaremos como se ve a continuación:

Dentro del directorio services crearemos el servicio que vamos a emplear, así:

ng g s members/services/members

Como sabemos, esto creará dos scripts de los que, por ahora, sólo nos interesa members.service.ts, que define la clase MembersService. Por ahora lo dejaremos como ha sido creado, sin modificarlo. Sin embargo, en el artículo anterior te comentaba que debemos referenciar el servicio en la clave providers del módulo donde lo vamos a emplear (MembersModule, en este caso). Lo haremos en members.module.ts, así:

Y, aunque esto ya lo sabemos, a modo de recordatorio observa las líneas 3 y 13, donde se importa el FormsModule de Angular, que necesitaremos para usar la directiva ngModel en el formulario.

TRABAJANDO EN MembersModule

Con lo que hemos hecho en el apartado anterior hemos dejado el escenario preparado para montar nuestro módulo MembersModule, que nos permitirá gestionar la lista de socios. Reflexionemos sobre lo que vamos a hacer. Vamos a recrear la misma aplicación que hicimos para ilustrar el paso de datos entre componentes. Sin embargo, la lógica de la aplicación, es decir, las operaciones que se hacen con los datos, las vamos a sacar de los componentes y las vamos a alojar en el servicio que hemos creado.

Y ¿para qué ibamos a hacer una cosa así? Bien. La utilidad de los servicios es contener funcionalidades relativas a una necesidad específica, de forma que no estén «desperdigadas» por la aplicación. De este modo, cuando tengamos que modificar la operativa de una funcionalidad, no tendremos que ir a buscar donde está, en que componente, de donde recibe los datos… La buscaremos en el servicio y ahí la encontraremos y trabajaremos sobre ella. Estamos agrupando toda la lógica del módulo en un mismo sitio, facilitándonos el mantenimiento y escalado.

Otra cosa es el caso de servicios que pueden afectar a toda la aplicación, donde la utilidad es aún mayor. Tal sería el caso de un servicio que, por ejemplo, llame a un proceso de servidor para enviar un correo electrónico a un usuario. Es muy posible que nuestra aplicación contenga muchos procesos que deban enviar correos electrónicos. En lugar de definir una llamada a una API en cada componente donde se necesite, la definiríamos en un solo servicio, consumible de forma global por toda la aplicación.

Pero de las llamadas a API’s y otras cosas ya nos ocuparemos. Vamos a centrarnos en lo que tenemos ahora entre manos. Lo primero que debemos hacer es importar el servicio en el componente maestro (en nuestro ejemplo, members-master.component.ts). Al principio del código añadiremos la siguiente línea:

import { MembersService } from '../services/members.service';

En el constructor de la clase tenemos que declarar una variable cuyo tipo sea, precisamente, el que define el servicio (MembersService), así:

constructor(private membersService: MembersService) { }

Esto permite que la clase MembersService esté presente, mediante una propiedad llamada membersService en todo el ámbito de vida de la clase MembersMasterComponent. Es una sintaxis específica de la declaración de propiedades en un constructor de TypeScript, que está comentada en este artículo. Observa que a la propiedad la hemos dado el nombre empezando por minúscula, para que nos sea fácil distinguirla, visualmente, del nombre del servicio.

¿Qué logramos con esto? Pues, precisamente, lo que pretendíamos. Ahora ya podemos desviar toda la lógica al servicio y, como este está implementado en el componente maestro, la tenemos disponible. Es decir. Justamente lo que pretendíamos. Pero logramos mucho más que eso. Al colocar toda la lógica del módulo en el servicio, nos ahorramos todo el trabajo de andar pasando datos de un componente a otro. Nos basta con consumir el servicio en cada uno de los componentes del módulo. Y, si me vas a decir que es posible que en el módulo haya componentes que no necesiten del servicio, pues sí. Podrías tener razón (no es el caso, pero podría serlo). En un caso así, nos bastaría con no consumir el servicio en el componente donde no lo necesitemos. Sin embargo, ten en cuenta una cosa. Un módulo, por cuestión de buenas prácticas de programación, debería envolver a todos los componentes, servicios, clases, etc. relacionados con una funcionalidad concreta. Es una cuestión organizativa. Por lo tanto, será raro el caso en que en un módulo, si está bien concebido y organizado, tengamos un servicio que no necesitemos usar en algunos de sus componentes. Después de todo, el objetivo del servicio es agrupar la lógica de los componentes del módulo.

Y ¿cómo consumimos un servicio en un componente? Bien. Lo primero es importarlo. En los archivos members-master.component.ts, members-form.component.ts y members-list.component.ts tienes, en las importaciones, la siguiente línea:

import { MembersService } from '../services/members.service';

Además, en cada uno de los tres scripts mencionados, tienes, en el constructor, lo siguiente:

constructor(public membersService: MembersService) { }

Y con esto, ya lo tenemos. Observa los tres scripts que acabamos de mencionar, que son los que, en principio, tendrían la lógica de sus respectivos componentes. Verás que no tienen ninguna lógica programada. Absolutamente todo, desde la declaración de propiedades que se usan en el módulo, hasta los métodos para realizar cada operación, están en el servicio. De esta forma, optimizamos el código, nos ahorramos pasos, ahorramos tiempo y trabajo y nos queda todo mucho más desacoplado y mantenible.

CONCLUYENDO

El uso de servicios puede parecer un poco engorroso al principio pero, como ves, nos ahorra mucho trabajo, y nos permite localizar todo mucho más fácilmente. El código completo de esta aplicación lo tienes para descarga en este enlace. En el servicio he añadido los comentarios necesarios para que sepas lo que hace cada propiedad y cada método. Con esos comentarios, y la lectura de este artículo, no tendrás ningún problema para entender como usar los servicios en Angular. Encontrarás algunos autores que te dirán que un servicio sólo debería consumirse en el componente maestro, y no en los componentes esclavos. Podrías hacerlo así, pero eso te obligaría a añadir algo de lógica en los componentes, para comunicar datos de unos a otros, con lo que estarías infrautilizando la potencia de los servicios. También hay quienes son partidarios de declarar los componentes en el constructor como privados. Puedes hacerlo, pero eso nos llevaría al mismo punto. Y, dado que la mayoría de los servicios se suelen proveer a nivel de módulo, eso, de por sí, ya nos proporciona un nivel adecuado de encapsulamiento.

   

Deja un comentario