Tutorial Angular – 6. Servicios

      No hay comentarios en Tutorial Angular – 6. Servicios

Servicios


El componente HeroesComponent de Tour de Héroes actualmente obtiene y muestra datos simulados.
Después de la refactorización de este tutorial, HeroesComponent estará enfocado en dar soporte a la vista. Además, esto facilitará los tests unitarios a través de un servicio simulado (mock).

¿Por qué servicios?

Los componentes no debería recuperar o guardar datos directamente, y desde luego, no debería mostrar datos simulados. Debería dedicarse a sólo a presentar los datos y delegar el acceso de datos a un servicio.
En este tutorial, crearemos un servicio HeroService que todas las clases de la aplicación podrán usar para obtener los héroes. En lugar de crear ese servicio con new, usaremos la inyección de dependencias de Angular para inyectarlo en el constructor de HeroesComponent.
Los servicios son una forma estupenda de compartir información entre clases que no se conocen. Crearemos un servicio MessageService y lo inyectaremos en dos sitios:

  1. en HeroService que usa el servicio para enviar un mensaje.
  2. en MessagesComponent que muestra el mensaje.

Crea HeroService

Usando el CLI de Angular, crea un servicio llamado hero.

Este comando genera un esqueleto de la clase HeroService en rc/app/hero.service.ts. El servicio HeroService debería ser así:

Servicios @Injectable()

Observa que el nuevo servicio importa el símbolo de Angular Injectable y anota la clase con el decorador @Injectable().
El decorador @Injectable() le indica a Angular que este servicio puede tener dependencias inyectadas. Ahora mismo no tiene dependencias, pero las tendrá pronto. Tanto si tiene como si no, es una buena práctica mantener el decorador.

La guía de estilos de Angular recomienda encarecidamente mantenerlo y el optimizador (linter) obliga a respetar esta regla.

Obtener los datos del héroe

El servicio HeroService puede obtener los datos de cualquier parte – un servicio web, almacenamiento local o un origen de datos simulado (mock).
Eliminar el acceso de dato de los componentes implica que puedas cambiar de idea acerca de la implmentación en cualquier momento, sin tener que modificar ningún componente. Estos desconocen el funcionamiento del servicio.
La implementación en este tutorial seguirá ofreciendo héroes simulados.
Importa hero y HEROES.

Añade un método getHeroes para devolver los héroes simulados.

Provee el servicio HeroService

Debemos proveer el servicio HeroService en el sistema de inyección de dependencias antes de que Angular pueda inyectarlo en HeroesComponent, como verás más abajo.
Hay diferentes maneras de proveer HeroService: en HeroesComponent, en AppComponent y en AppModule. Cada opción tiene sus pros y sus contras.
Este tutorial elige proveerlo en AppModule.
Esta opción es tan habitual que podríamos haberle indicado al CLI que provea el servicio automáticamente en AppModule añadiendo --module=app.

Como no lo hicimos así, habrá que proveerlo ahora manualmente.
Abre la clase AppModule, importa HeroService y añadelo al array @NgModule.providers.

El array providers le indica a Angular que tiene que crear una única y compartida instancia de HeroService e inyectarla en cualquier clase que lo solicite.
HeroService ya está listo para conectarse con HeroesComponent.

Este es un ejemplo de código provisional que nos va a permitir proveer y usar el servicio HeroService. En este punto, el código será diferente del de HeroServiceen la revisión final de código de esta página

Actualiza HeroesComponent

Abre el fichero de la clase HeroesComponent.
Borra la importación de HEROES ya que no la vamos a necesitar más. Importa el servicio HeroService en su lugar.

Reemplaza la definición de la propiedad heroes por una simple declaración.

Inyecta HeroService

Añade un parámetro privado llamado heroService y de tipo HeroService al constructor.

El parámetro define simultáneamente una propiedad privada heroService y lo identifica como una inyección HeroService.
Cuando Angular crea un HeroComponent, el sistema de Inyección de Dependencias establece el parámetro heroService como la instancia única (singleton) de HeroService.

Añade getHeroes()

Crea una función que recupere los héroes del servicio.

Llámala en ngOnInit

Aunque podrías llamar a getHeroes() en el constructor, eso no es una buena práctica.
Reserva el constructor para inicializaciones simples, como asignar los parámetros del constructor a la propiedades. El constructor no debería hacer nada. A buen seguro, no debería llamar a una función que hace peticiones HTTP a un servidor remoto, como el haría un servicio real.
En su lugar, llama a getHeroes() dentro del ‘enganche del ciclo de vida’ (lifecycle hook) de ngOnInit y deja que Angular llame a ngOnInit en el momento apropiado después de construir una instancia de HeroesComponent.

Comprueba como se ejecuta

Después de que el navegador se actualice, la aplicación debería ejecutarse como antes, mostrando un listado de héroes y el detalle del héroe sobre el que hagamos clic.

Datos Observable

El método HeroService.getHeroes() tiene una firma síncrona, lo que implica que HeroService puede recuperar héroes síncronamente. HeroesComponent consume el resultado de getHeroes() como si los héroes pudieran ser recuperados síncronamente.

Esto no funcionaría en una aplicación real. Ahora funciona porque el servicio actualmente devuelve héroes simulados. Pero pronto la aplicación recuperará los héroes desde un servidor remoto, lo cual es una operación asíncrona.
HeroService debe esperar a que el servidor responda, getHeroes() no puede devolver datos inmediatamente y el navegador no se bloqueará mientras el servicio espera.
HeroService.getHeroes() debe tener algún tipo de firma asíncrona.
Puede aceptar una retrollamada (callback). Puede devolver una Promesa. Puede devolver un Observable.
En este tutorial HeroService.getHeroes() va a devolver un Observable. En parte porque finalmente usará el método de Angular HttpClient.get para recuperar los héroes y HttpClient.get devuelve un Observable.

Observable HeroService

Observable es una de las clases clave de la librería RxJS.
En el próximo tutorial HTTP, aprenderemos que los métodos de HttpClient de Angular devuelven Observables de RxJS. En este tutorial simularemos la obtención de datos de un servidor con la función de RxJS of().
Abre el fichero HeroService e importa los símbolos Observable y of de RxJS.

Reemplaza el método getHeroes() por este.

of(HEROES) devuelve un Observable<Hero[]> que emite un único valor, el array de héroes simulado.

En el tutorial HTTP llamaremos a HttpClient.get<Hero[]>() el cual también devuelve un Observable[] que emite un único valor, un array de héroes dentro del cuerpo de la respuesta HTTP

Suscribe a HeroesComponent

El método HeroService.getHeroes() antes retornaba Hero[]. Ahora devuelve un Observable[].
Tendrás que modificar HeroesComponent para que se ajuste a ese cambio.
Encuentra el método getHeroes() y reemplázalo con el siguiente código (se muestra también la versión anterior para su comparación)

La diferencia principal es Observable.subscribe()
La versión anterior asigna un array de héroes a la propiedad heroes del componente. La asignación ocurre síncronamente, como si el servidor pudiera devolver héroes instantáneamente o el navegador pudiera bloquear la interfaz del usuario mientras espera la respuesta del servidor.
Esto no funcionará cuando HeroService en realidad está haciendo peticiones a un servidor remoto.
La nueva versión espera que un Observable emita un array de héroes – lo cual puede suceder ahora o dentro de varios minutos. Entonces, suscribe para el array emitido a la retrollamada, la cual establece la propiedad heroes del componente.
Este enfoque asíncrono funcionará cuando HeroService solicite héroes al servidor.

Mostrar mensajes

En está sección:

  • añadiremos MessagesComponent que muestre mensajes de la aplicación al final de la pantalla.
  • crearemos un MessageService inyectable y que abarque toda la aplicación para enviar los mensajes a mostrar.
  • inyectaremos MessageService en HeroService.
  • mostraremos un mensaje cuando MessageService recupere con éxito los héroes.

Crear MessagesComponent

Usa el CLI para crear el componente MessagesComponent

El CLI crea los archivos del componente en la carpeta src/app/messages y declara MessagesComponent en AppModule.
Modifica la plantilla AppComponent para mostrar el componente MessagesComponent generado.

Deberías ver el texto por defecto de MessagesComponent al final de la página.

Crea MessageService

Usa el CLI para generar MessageService en src/app. La opción --module=app le indica al CLI que proporcione este servicio en AppModule.

Abre MessageService y reemplaza su contenido por el siguiente.

Este servicio expone su caché de mensajes y dos métodos: uno es add() para añadir un mensaje a la caché y el otro es clear(), para vaciar la caché.

Inyéctalo en HeroService

Vuelve a abrir HeroService e importa MessageService.

Modifica el constructor con un parámetro que declare una propiedad privada messageService. Angular inyectará la instancia única (singleton) MessageService en esa propiedad cuando crea HeroService.

Este es un típico escenario ‘servicio-en-servicio’ : inyectamos MessageService en HeroService el cual está inyectado en HeroesComponent

Enviar un mensaje desde HeroService

Modifica el método getHeroes() para que envíe un mensaje cuando se recuperan los héroes.

Muestra el mensaje desde HeroService

MessagesComponent debería mostrar todos los mensajes, incluyendo el mensaje enviado por HeroService cuando recupera héroes.
Abre MessagesComponent e importa MessageService

Modifica el constructor con un parámetro que declare una propiedad pública messageService. Angular inyectará la instancia única (singleton) MessageService en esa propiedad cuando crea HeroService.

La propiedad messageService debe ser pública porque vamos a vincularla en una plantilla.

Angular sólo permite vincular con las propiedades públicas de los componentes.

Vincular con MessageService

Reemplaza la plantilla generada por el CLI MessagesComponent por lo siguiente.

Esta plantilla se vincula directamente con messageService del componente.

  • *ngIf sólo muestra el área de mensajes si hay mensajes que mostrar.
  • *ngFor presenta el listado de mensajes en elementos <div> repetidos.
  • Un evento de vinculación de Angular vincula el evento clic de un botón a MessageService.clear().

Los mensajes tendrán un mejor aspecto cuando añadas los estilos CSS privados, tal y como verás en la revisión final de código.
El navegador se actualizará y la página mostrará un listado de héroes. Haz scroll hasta el final para ver el mensaje de HeroService en el área de mensajes. Haz clic en el botón de ‘clear’ y el área de mensajes desparecerá.

Revisión final de código

Aquí está el código de los ficheros tratados en esta página y tu aplicación debería parecerse a este ejemplo / descarga ejemplo

Resumen

  • Hemos refactorizado el acceso a datos en la clase HeroService
  • Hemos proporcionado HeroService en el AppModule raíz, de modo que se pueda inyectar en cualquier lugar.
  • Hemos usado la Inyección de Dependencias de Angular para inyectarlo en un componente
  • Le hemos dado al método de obtención de datos de HeroService una firma asíncrona.
  • Hemos descubierto Observable y la librería RxJS Observable
  • Hemos usado of() de RxJS para devolver un Observable de héroes simulados (Observable<Hero[]>).
  • El ‘enganche de ciclo de vida’ (lifecycle hook) ngOnInit del componente llama al método de HeroService, no el contructor.
  • Hemos creado un MessageService para la comunicación desacoplada entre clases.
  • EL servicio HeroService inyectado en un componente se crea con otro servicio inyectado, MessageService

Nota: puedes encontrar el documento original de esta entrada en https://angular.io/tutorial/toh-pt4

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *