
* Toh pt4 - Se agrego la versión en ingles .en.md - Se Probo previamente * corrección ortografica y de enlaces colgantes
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.
- Inyecte en HeroService, que utiliza el servicio para enviar un mensaje.
- 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
.
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.
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 Observable
s.
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 elHeroService
- 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
.
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étodoHeroService
, 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
.