Antonio Cardenas 45e6bb4a74
docs: translate tutorial/toh-pt4.md (#86)
* Toh pt4

- Se agrego la versión en ingles .en.md
- Se Probo previamente

* corrección ortografica y de enlaces colgantes
2020-10-21 10:19:21 -05:00

18 KiB

Agregar servicios

El HeroesComponent de Tour de los Heroes actualmente está obteniendo y mostrando datos simulados.

Después de la refactorización en este tutorial, HeroesComponent será sencillo y se centrará en apoyar la vista. También será más fácil realizar pruebas unitarias con un servicio simulado.

Para ver la aplicación de ejemplo que describe esta página, consulte el .

Por qué servicios

Los componentes no deberían buscar ni guardar datos directamente y, desde luego, no deberían presentar a sabiendas datos simulados. Deben centrarse en presentar datos y delegar el acceso a los datos a un servicio.

En este tutorial, crearás un HeroService que todas las clases de aplicación pueden usar para obtener héroes. En lugar de crear ese servicio con la palabra clave new, confiará en la inyección de dependencia de Angular para inyectarlo en el constructor HeroesComponent.

Los servicios son una excelente manera de compartir información entre clases que no se conocen entre sí. Creará un MessageService y lo inyectará en dos lugares.

  1. Inyecte en HeroService, que utiliza el servicio para enviar un mensaje.
  2. Inyecte en MessagesComponent, que muestra ese mensaje, y también muestra la ID cuando el usuario hace clic en un héroe.

Crear el HeroService

Usando la CLI Angular, cree un servicio llamado hero.

ng generate service hero

Este comando generará un archivo base HeroService en src/app/hero.service.ts de la siguiente manera:

Servicio @Injectable()

Observe que el símbolo Inyectable de Angular se importa en el archivo generado, anotando la clase como decorador @Injectable(). Esto marca a la clase como participante en el sistema de inyección de dependencia. La clase HeroService proporcionará servicios inyectables y puede tener dependencias. Aún no hay dependencias, estará pronto.

El decorador @Injectable() acepta el objeto de metadatos de un servicio de la misma manera que el decorador @Component() para las clases de componentes.

Obtener datos del héroe

El HeroService podría obtener datos de héroes desde cualquier lugar— un servicio web, almacenamiento local o una fuente de datos simulada.

Eliminar el acceso a datos de los componentes significa que puede cambiar de opinión acerca de la implementación en cualquier momento, sin tocar ningún componente. No saben cómo funciona el servicio.

La implementación en este tutorial continuará entregando héroes simulados.

Importar Hero and HEROES.

Agregue el método getHeroes y devuelva los héroes simulados.

{@a provide}

Proporcionar el HeroService

Debe poner el HeroService a disposición del sistema de inyección de dependencias. antes de que Angular pueda inyectarlo en el 'Componente de héroes' al registrar un proveedor. Un proveedor es algo que puede crear o prestar un servicio; en este caso, crea una instancia de la clase HeroService para proporcionar el servicio.

Para asegurarse de que el HeroService pueda proporcionar este servicio, regístrelo con el inyector, que es el objeto responsable de elegir e inyectando el proveedor donde la aplicación lo requiere.

Por defecto, el comando Angular CLI ng generate service registra a un proveedor con el inyector raíz para su servicio al incluir los metadatos del proveedor, que se proporcionan en el decorador @Injectable() .

@Injectable({
  providedIn: 'root',
})

Cuando proporciona el servicio en el nivel raíz, Angular crea una única instancia compartida de HeroService e inyecta en cualquier clase que lo solicite. El registro del proveedor en los metadatos @Injectable también le permite a Angular optimizar una aplicación eliminando el servicio si resulta que no se usará después de todo.

Para obtener más información sobre los proveedores, consulte la Sección de proveedores. Para obtener más información sobre los inyectores, consulte la Guía de inyección de dependencia.

El `HeroService` ahora está listo para conectarse al `HeroesComponent`.

Este es un ejemplo de código provisional que le permitirá proporcionar y usar el HeroService. En este punto, el código diferirá del HeroService en la " revisión final del código ".

Si desea obtener más información sobre proveedores, consulte Proveedores.

Actualizar HeroesComponent

Abra el archivo de clase HeroesComponent.

Elimine la importación HEROES, porque ya no la necesitará. Importa el HeroService en su lugar.

Reemplace la definición de la propiedad heroes con una simple declaración.

{@a inject}

Inyectar el HeroService

Agregue un parámetro privado heroService de tipo HeroService al constructor.

El parámetro define simultáneamente una propiedad privada heroService y la identifica como un sitio de inyección HeroService.

Cuando Angular crea un HeroesComponent, el sistema Inyección de dependencia establece el parámetro heroService en la instancia única de HeroService.

Añadir getHeroes()

Crea un método para recuperar a los héroes del servicio

{@a oninit}

Llamarlo en ngOnInit()

Si bien podría llamar a getHeroes() en el constructor, esa no es la mejor práctica.

Reserve el constructor para una inicialización simple, como conectar los parámetros del constructor a las propiedades. El constructor no debe hacer nada. Ciertamente no debería llamar a una función que realiza solicitudes HTTP a un servidor remoto como lo haría un servicio de datos real.

En su lugar, llame a getHeroes() dentro del ngOnInit lifecycle hook (gancho del ciclo de vida) y deje que Angular llame a ngOnInit() en el momento apropiado después de construir una instancia de HeroesComponent.

Verlo correr

Después de que el navegador se actualice, la aplicación debería ejecutarse como antes, mostrando una lista de héroes y una vista detallada de héroe cuando haces clic en el nombre de un héroe.

Datos observables

El método HeroService.getHeroes() tiene una firma sincrónica, lo que implica que el HeroService puede buscar héroes sincrónicamente. El HeroesComponent consume el resultado getHeroes() como si los héroes pudieran ser recuperados sincrónicamente.

Esto no funcionará en una aplicación real. Ahora te saldrás con la tuya porque el servicio actualmente devuelve héroes simulados. Pero pronto la aplicación buscará héroes de un servidor remoto, que es una operación inherentemente asincrónica.

El HeroService debe esperar a que el servidor responda, getHeroes() no puede regresar inmediatamente con los datos del héroe, y el navegador no se bloqueará mientras el servicio espere.

HeroService.getHeroes() debe tener una firma asíncrona de algún tipo.

En este tutorial, HeroService.getHeroes() devolverá un Observable porque eventualmente usará el método angular HttpClient.get para buscar a los héroes y HttpClient.get() devuelve un Observable.

Observable HeroService

Observable es una de las clases clave en la [biblioteca RxJS] (http://reactivex.io/rxjs/).

En un tutorial posterior sobre HTTP, aprenderá que los métodos HttpClient de Angular devuelven RxJS Observables. En este tutorial, simulará obtener datos del servidor con la función RxJS of().

Abra el archivo HeroService e importe los símbolos Observable y of de RxJS.

Reemplace el método getHeroes() con lo siguiente:

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

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

Suscríbirse en HeroesComponent

El método HeroService.getHeroes utilizado para devolver un Hero[]. Ahora devuelve un Observable <Hero[]>.

Tendrás que ajustarte a esa diferencia en HeroesComponent.

Encuentre el método getHeroes y reemplácelo con el siguiente código (Al lado de la versión anterior para comparar)

Observable.subscribe() es la diferencia crítica.

La versión anterior asigna una variedad de héroes a la propiedad 'heroes' del componente. La asignación ocurre sincrónicamente, como si el servidor pudiera devolver héroes al instante o el navegador podría congelar la interfaz de usuario mientras esperaba la respuesta del servidor.

Eso no funcionará cuando el HeroService realmente está haciendo solicitudes a un servidor remoto.

La nueva versión espera a que el 'Observable' emita una serie de héroes,— que podría suceder ahora o varios minutos a partir de ahora. El método subscribe() pasa el arreglo emitida a la devolución de llamada, que establece la propiedad 'heroes' del componente.

Este enfoque asincrónico funcionará cuando el HeroService solicite héroes del servidor.

Mostrar mensajes

Esta sección lo guía a través de lo siguiente:

  • agregando un MessagesComponent que muestra los mensajes de la aplicación en la parte inferior de la pantalla
  • crear un MessageService inyectable para toda la aplicación para enviar mensajes que se mostrarán
  • inyectando MessageService en el HeroService
  • mostrando un mensaje cuando HeroService busca héroes con éxito

Crear MessagesComponent

Use la CLI para crear el MessagesComponent. ng generate component messages

La CLI crea los archivos componentes en la carpeta src/app/messages y declara el MessagesComponent en AppModule.

Modifique la plantilla AppComponent para mostrar el MessagesComponent generado.

Debería ver el párrafo predeterminado de MessagesComponent en la parte inferior de la página.

Crear el MessageService

Use la CLI para crear el MessageService en src/app.

ng generate service message

Abra MessageService y reemplace su contenido con lo siguiente.

El servicio expone su caché de mensajes y dos métodos: uno para agregar() un mensaje al caché y otro para borrar() el caché.

{@a inject-message-service}

Inyectar en el HeroService

En HeroService, importe el MessageService.

Modifique el constructor con un parámetro que declare una propiedad privada messageService. Angular inyectará el singleton MessageService en esa propiedad cuando crea el HeroService.

Este es un escenario típico de "servicio en servicio": inyecta el MessageService en el HeroService que se inyecta en el HeroesComponent.

Enviar un mensaje desde HeroService

Modifique el método getHeroes() para enviar un mensaje cuando se busquen los héroes.

Mostrar el mensaje de HeroService

El MessagesComponent debería mostrar todos los mensajes, incluido el mensaje enviado por el HeroService cuando busca héroes.

Abra MessagesComponent e importe el MessageService

Modifique el constructor con un parámetro que declare una propiedad messageService publica. Angular inyectará el único MessageService en esa propiedad cuando crea el MessagesComponent.

La propiedad messageService debe ser pública porque la vinculará en la plantilla.

Angular solo se une a las propiedades publicas del componente .

Enlazar al MessageService

Reemplace la plantilla MessagesComponent generada por CLI con lo siguiente.  

Esta plantilla se une directamente al componente messageService del componente.

  • *NgIf solo muestra el área de mensajes si hay mensajes para mostrar.

  • Un *ngFor presenta la lista de mensajes en elementos repetidos<div>.

  • Un enlace de evento en angular une el evento de clic del botón a MessageService.clear ().

Los mensajes se verán mejor cuando agregue los estilos CSS privados a messages.component.css como se indica en una de las pestañas "revisión de código final" a continuación.

Agregar mensajes adicionales al servicio de héroe

El siguiente ejemplo muestra cómo enviar y mostrar un mensaje cada vez que el usuario hace clic en un héroe, que muestra un historial de las selecciones del usuario. Esto será útil cuando llegues a siguiente sección sobre Enrutamiento.

El navegador se actualizará y la página mostrará la lista de héroes. Actualiza el navegador para ver la lista de héroes y desplázate hacia abajo para ver mensajes del HeroService. Cada vez que haces clic en un héroe, aparece un nuevo mensaje para grabar la selección. Use el botón "borrar" para borrar el historial de mensajes.

{@a final-code-review}

Revisión final del código

Aquí están los archivos de código discutidos en esta página.

Resumen

  • Refactorizó el acceso a datos a la clase HeroService.
  • Registro el HeroService como el proveedor de su servicio en el nivel raíz para que pueda inyectarse en cualquier lugar de la aplicación.
  • Usó la Inyección de dependencia angular para inyectarlo en un componente.
  • Le dio al HeroService el método get data una firma asincrónica.
  • Descubrio Observable y la biblioteca RxJS Observable.
  • Usó RxJS of () para devolver un observable de héroes simulados (Observable <Hero []>).
  • El lifecycle hook (gancho del ciclo de vida) ngOnInit del componente llama al método HeroService, no al constructor.
  • Creó un MessageService para una comunicación débilmente acoplada entre clases.
  • El HeroService inyectado en un componente se crea con otro servicio inyectado, MessageService.