Tutorial Angular – 7. Enrutamiento.

Enrutamiento


Hemos recibido nuevos requisitos para nuestra aplicación Tour de Héroes.

  • Añadir un Cuadro de Mandos (dashboard).
  • Añadir la posibilidad de navegar entre el Cuadro de Mandos y la página de los héroes.
  • Cuando un usuario haga clic en cualquiera de las dos vistas, navegar a una vista de detalles del héroe
  • Cuando los usuarios hagan clic en un enlace profundo (deep link) en un correo, abre la vista de detalle para un héroe en particular

Cuando esté listo los usuarios serán capaces de navegar por la aplicación de este modo:

Añadir AppRoutingModule

Una buena práctica de Angular es cargar y configurar el enrutador en un módulo separado y al nivel más alto, que se dedique sólo al enrutamiento y que se importe en AppModule.
Por convenio, el nombre de este módulo es AppRoutingModule y pertence al fichero app-routing.module.ts en la carpeta src/app.
Usaremos el CLI para generarlo.

--flat deja el fichero en src/app
en lugar de su propia carpeta.
--module=app le indica al CLI que lo registre en el array de imports de AppModule.

El fichero generado debería ser así:

En general, no se declaran componentes en un módulo de enrutamiento, así que podemos borrar el array @NgModule.declarations y también CommonModule.
Configuraremos el enrutador con Routes en RouterModule así que importaremos estos dos símbolos de la librería @angular/router.
Añade un array @NgModule.exports con RouterModel en él. Exportar RouterModule hace que las directivas de enrutamiento estén disponibles para los componentes de AppModule que lo necesiten.
AppRoutingModule quedaría así:

Añade rutas

Las rutas indican al enrutador que vistas mostrar cuando un usuario hace clic en un enlace o pegar una URL en el navegador.
Un Route típico de Angular tiene dos propiedades:

  1. path: una cadena que coincide con la URL del navegador.
  2. component: el componete que el enrutador debe crear cuando navega a esta ruta.

La idea es navegar hasta HeroesComponent cuando la URL sea localhost:4200/heroes.
Importa HeroesComponent de modo que puedas referenciarlo en una Route. Después, define un array de rutas con un único route a ese componente.

Una vez que hemos terminado esta configuración, el enrutador enlazará esa URL con path: 'heroes' y mostrará HeroesComponent.

RouterModule.forRoot()

Primero debemos inicializar el enrutador y empezar a escuchar los cambios del navegador.
Añade RouterModule al array @NgModule.imports y configuralo con routes en un solo paso llamando a RouterModule.forRoot() dentro del array imports, tal que así:

El método se llama forRoot() porque el enrutador se condfigura al nivel raíz (root) de la aplicación.
El método forRoot() proporciona al servicio directivas y proveedores necesarios para el enrutamiento y realiza la navegación inicial basándose en la URL actual del navegador.

Añade RouterOutlet

Abre la plantilla de AppComponent y reemplaza el elemento <app-heroes> por el elemento <router-outlet>.

Hemos eliminado <app-heroes> porque sólo mostraremos HeroesComponent cuando el usuario navegue hasta él.
<router-outlet> le indica al enrutador donde mostrar las vistas enrutadas.

RouterOutlet es una de las directivas que están disponibles en AppComponent porque AppModule importa AppRoutingModule, el cual exportó RouterModule.

Probando

Aún deberíamos estar ejecutando la aplicación con este comando del CLI.

El navegador debería actualizarse y mostrar el título de la aplicación pero no el listado de héroes.
Observa la barra de direcciones del navegador. La URL termina en /. La ruta a HeroesComponent es /heroes.
Añade /heroes a la URL en la barra de direcciones. Deberías ver la ya conocida vista maestro/detalle de héroes.

Añade un enlace de navegación (routerLink)

Los usuarios no deberían pegar una URL en la barra de direcciones. Debería poder hacer clic en un enlace para navegar.
Añade un elemento <nav> y dentro de él un elemento <a> que al hacer clic, accione la navegación a HeroesComponent. La plantilla AppComponent revisada quedaría así:

AL atributo routerLink se le asigna el valor /heroes, la cadena que el enrutador enlaza con la ruta a HeroesComponent. routerLink es el selector para la directiva RouterLink que convierte los clics del usuario en navegación del enrutador. Es otra de las directivas públicas dentro de RouterModule.
El navegador se actualiza y muestra el título de la aplicación y el enlace a héroes, pero no la lista de héroes.
Haz clic en el enlace. La barra de direcciones se actualiza a /heroes y la lista de héroes aparece.

Puedes conseguir que tanto este enlace como los siguientes tengan mejor aspecto añadiendo estilos CSS privados a app.component.css, como verás en la revisión final de código.

Añadir una vista para el Cuadro de Mandos

EL enroutamiento tiene más sentido cuando hay varias vistas. Por ahora, sólo tenemos la vista de héroes.
Añade un componente para el Cuadro de Mandos DashboardComponent usando el CLI:

El CLI generará los archivos para DashboardComponent y lo declarará en AppModule.
Reemplaza el contenido por defecto de los ficheros por los siguientes y ahora mismo los explicamos:

La plantilla presenta una cuadrícula (grid) de héroes con sus enlaces.

  • *ngFor crea tantos enlaces como haya en el array heroes del componente.
  • Los enlaces serán bloques de color, como indica dashboard.component.css
  • Los enlaces aún no llevan a ninguna parte, pero pronto lo harán.

La clase se parece a la clase de HeroesComponent.

  • Define una propiedad array heroes.
  • El constructor espera que Angular inyecte HeroService en una propiedad privada heroService.
  • El ‘enganche de ciclo de vida’ (lifecycle hook) ngOnInit()
    llama a getHeroes

getHeroes reduce el número de héroes mostrados a cuatro (2º, 3º, 4º y 5º).

Añade la ruta del Cuadro de Mandos

Para navegar al Cuadro de Mandos, el enrutador necesita una ruta apropiada.
Importa DashboardComponent en AppRoutingModule.

Añade una ruta al array AppRoutingModule.routes que enlaza una ruta al DashboardComponent.

Añade una ruta por defecto

Cuando la aplicación arranca, la barra de direcciones del navegador apunta a la raíz de la página web. Esto no coincide con con ninguna ruta existente, así que el enrutador no navega a ninguna parte. El espacio debajo de router-outlet está en blanco.
Para hacer que la aplicación navegue automáticamente al Cuadro de Mandos, añade la siguiente ruta al array AppRoutingModule.Routes.

Esta ruta redirige una URL que coincide con el path vacío a la ruta del path '/dashboard'.
Cuando el navegador se actualiza, el enrutador carga DashboardComponent y la barra de direcciones muestra la URL /dashboard.

Añade el enlace al Cuadro de Mandos al caparazón

El usuario debería ser capaz de navegar de ida y vuelta entre los componentes DashboardComponent y HeroesComponent haciendo clic en los enlaces en el área de navegación en la parte de arriba de la página.
Añada un enlace de navegación al Cuadro de Mandos en la plantilla del caparazón AppComponent, justo sobre el enlace a Heroes.

Después de que el navegador se actualice puedes navegar libremente entre las dos vistas haciendo clic sobre los enlaces.

Navegando a los detalles del héroe

HeroDetailsComponent muestra los detalles del héroe seleccionado. En este momento, code>HeroDetailsComponent sólo puede verse en la parte de abajo de code>HeroesComponent.
El usuario debería ser capaz de llegar a estos detalle de tres maneras:

  1. Haciendo clic sobre un héroe en el Cuadro de Mandos.
  2. Haciendo clic sobre un héroe en el listado de héroes.
  3. Pegando un enlace profundo (deep link) en la barra de direcciones que identifique al héroe a mostrar.

En esta sección, activaremos la navegación a HeroeDetailsComponent y la eliminaremos de HeroesComponent.

Borrar los detalles del héroe de HeroesComponent

Cuando el usuario hace clic en un héroe en HeroesComponent, la aplicación debería navegar a HeroDetailComponent, reemplazando la vista del listado de héroes por la vista del detalle del héroe. EL listado de héroes ya no debería mostrar detalles, tal y como hace ahora.
Abre la plantilla HeroesComponente (heroes/heroes.component.html) y borra el elemento <app-hero-detail> del final.
Hacer clic sobre un héroe ahora no hace nada. Arreglaremos esto pronto, después de activar el enrutamiento a HeroDetailComponent.

Añade una ruta para el detalle del héroe

Una URL como ~/detail/11 sería una buena URL para navegar hasta la vista de detalle del héroe con id igual a 11.
Abre AppRoutingModule e importa HeroDetailComponent.

Después añade una ruta parametrizada al array AppRoutingModule.routes que coincida con el patrón de la ruta a la vista del detalle del héroe.

Los dos puntos (:) en path indican que :id es un marcador de posición (placeholder) para un id concreto de héroe.
En este punto, todas las rutas de la aplicación están listas.

Enlaces al héroe en el Cuadro de Mandos

Los héroes de HeroesComponent son elementos <li> cuyos eventos clic están vinculados al método onSelect() de los componentes.

Haz que <li> sólo use *ngFor, envuelve el badge y el nombre en un elemento <a> y añadele un atributo RouterLink que es el mismo que en la plantilla del Cuadro de Mandos.

Tendrás que arreglar la hoja de estilos privada heroes.component.css) para hacer que el listado tenga el mismo aspecto que antes. Los estilos revisados están en la revisión final de código al final de esta página.

Eliminar el código no usado (opcional)

Aunque la clase HeroesComponent aún funciona, el método onSelect() y la propiedad selectedHero ya no se usan.
Es buena idea ser ordenado y te lo agradecerás en un futuro. Así queda la clase tras quitar el código que ya no se usa.

HeroDetailComponent enrutable

Anteriormente, el padre HeroesComponent estableció la propiedad HeroDetailComponent.hero y HeroDetailComponent mostró la propiedad.
GeroesComponent ya no hace eso. Ahora el enrutador crea HeroDetailComponent en respuesta a una URL del tipo ~/detail/11.
HeroDetailComponent necesita una nueva manera de obtener el héroe a mostrar.

  • Obtener la ruta que lo creó.
  • Extraer el id de la ruta.
  • Obtener el héroe con ese id del servidor a través de HeroService.

Añade las siguientes importaciones:

Inyecta ActivatedRoute, HeroService y Location en el constructor, guardando sus valores en campos privados:

ActivatedRoute guarda información acerca de la ruta a esta instancia de HeroDetailComponent. Este componente toma en cuenta los parámetros de la ruta extraídos de la URL. El parámetro «id» es el id del héroe a mostrar.
HeroService obtiene datos del héroe desde un servidor remoto y este componente los usará para obtener el héroe a mostrar.
location es un servicio de Angular para interactuar con el navegador. Lo usaremos más adelante para navegar de vuelta a la vista que nos llevó hasta aquí.

Extraer el id del parámetro de la ruta

En el ‘enganche de ciclo de vida’ (lifecycle hook) ngOnInit() llama a getHero() y defínelo así:

route.snapshot es una imagen estática de la información de la ruta justo después de que el componente se haya creado.
paramMap es un diccionario de valores de los parámetros de la ruta, extraídos de la URL. La clave id devuelve el id del héroe a recuperar.
Los parámetros de la ruta son siempre cadenas (strings). El operador de JavaScript (+) convierte la cadena a un número, que es lo que el id del héroe debería ser.
El navegador se actualiza y la aplicación fallará con un error del compilador. HeroService no tiene el método getHero().Ahora lo añadiremos.

La comilla invertida (`) en JavaScript define un literal de plantilla para embeber el id.

Al igual que getHeroes(), getHero() tiene firma asíncrona. Devuelve un héroe simulado como un Observable, usando la función de RxJS of().
Será posible reimplmentar getHero() como una petición Http real sin tener que cambiar el componente HeroDetailComponent que lo invoca.

Pruébalo

El navegador se actualiza y la aplicación funciona de nuevo. Puedes hacer clic sobre un héroe en el Cuadro de Mandos o en la lista de héroes y navegar a la vista de detalle de ese héroe.
Si pegas localhost:4200/detail/11 en la barra de direcciones, el enrutador navega hasta la vista de detalle del héroe con id:11, «Mr. Nice».

Encontrar el camino de vuelta

Haciendo clic en el botón ‘volver’ del navegador, puedes volver a la lista de héroes o el Cuadro de Mandos, dependiendo de que vista nos redirigió a la vista detalle.
Sería conveniente tener un botón en la vista HeroDetail que nos permitiera hacer esto.
Añade un botón volver al final de la plantilla del componente y vincúlalo al método goBack() del componente.

Añade un método goBack() a la clase del componente que navegue un paso hacia atrás en el historial del navegador usando el servicio Location que hemos inyectado previamente.

Actualiza el navegador y haz clics. Los usuarios puedes navegar por toda la aplicación, desde el Cuadro de Mandos a los detalles del héroe y volver, del listado de héroes al mini detalle, a los detalles del héroe y volver al listado de nuevo.
Hemos cumplido todos los requisitos de navegación que se propuesieron en esta página.

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

AppRoutingModule, AppModule, y HeroService

AppComponent

DashboardComponent

HeroesComponent

HeroDetailComponent

Resumen

  • Hemos añadido un enrutador de Angular para navegar entre diferentes componentes.
  • Hemos convertido a AppComponent en un caparazón (shell) de navegación con enlaces <a> y un <router-outlet>.
  • Hemos configurado el enrutador en AppRoutingModule.
  • Hemos definido rutas sencillas, una ruta de redirección y una ruta parametrizada.
  • Hemos usado la directiva RouterLink en elementos <a>.
  • Hemos refactorizado una vista maestro/detalle fuertemente acoplada en una vista de detalle enrutada.
  • Hemos usado los parámetros del enlace de la ruta para navegar a la vista del detalle de un héroe seleccionado por el usuario.
  • Hemos compartido el servicio HeroServices entre múltiples componentes.

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

Deja un comentario

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