Angular - Routing
Diseño para todys
Single Page Application revisited
¿Por qué “Single Page”? Porque inicialmente vamos a cargar una única página de nuestra app (es el index.html)
Single Page Application revisited
El primer pedido de nuestro navegador al server trae además otros archivos necesarios para levantar la app: css, js compilados, etc.
¿Cómo cambiar de página?
Aquí es donde aparece el concepto de routing.
El mecanismo de ruteo de Angular
Es muy similar en otras tecnologías, consiste en poder asociar una ruta particular con un componente
clientes
ClientesComponent
clientes/:id
EditarClienteComponent
**
PageNotFoundComponent
IMPORTANTE, que no empiece con /
Definiendo un proyecto con routing
Solo debemos agregar esa opción al crear un nuevo proyecto:
Esto resuelve de antemano algunas cosas.
ng new proyecto-angular --routing
Proyecto con routing: 1) app-routes.ts
En este archivo vamos a definir las rutas y a asociarlo con componentes de Angular:
export const routes: Routes = [
{ path: 'listaTareas', component: ListaTareasComponent },
{ path: 'editarTarea/:id', component: EditarTareaComponent },
{ path: '**', redirectTo: '/listaTareas', pathMatch: 'full' },
]
Las rutas aceptan wildcards.
Se asocian a componentes o redireccionan a otras rutas.
Es importante el orden, donde primero aparezca la ruta más específica
Proyecto con routing: 2) app.config.ts
En este archivo vamos a definir las rutas y a asociarlo con componentes de Angular:
import { ApplicationConfig, provideZoneChangeDetection } from '@angular/core'
import { provideRouter } from '@angular/router'
import { routes } from './app.routes'
import { provideHttpClient } from '@angular/common/http'
export const appConfig: ApplicationConfig = {
providers: [
provideZoneChangeDetection({ eventCoalescing: true }), provideRouter(routes),
provideHttpClient()
]
}
No hay mucho para comentar, el provider inyecta la dependencia
Proyecto con routing: 3) app.component.ts
En nuestro módulo principal debemos declarar los componentes a los que queremos navegar y también importar el componente definido en el archivo app-routing.module:
import { Component } from '@angular/core'
import { RouterOutlet } from '@angular/router'
@Component({
selector: 'app-root',
standalone: true,
imports: [RouterOutlet],
templateUrl: './app.component.html',
styleUrl: './app.component.css'
})
export class AppComponent {
title = 'app'
}
Proyecto con routing: 4) cambios al app.component.html
Separamos la parte estática y la dinámica, determinada por la ruta de la aplicación:
<div style="text-align:center">
<br>
<h1>
Tareas - Routing
<small>Angular</small>
</h1>
<br>
</div>
<router-outlet></router-outlet>
parte estática
parte dinámica
Proyecto con routing: 4) cambios al app.component.html
Eso lo podemos ver cuando levantamos la aplicación, el router-outlet es un contenedor que va cambiando dinámicamente:
Tip 1: ver los tags que se crean como hermano de router-outlet.
Tip 2: manipular el DOM mediante document.getElementByTagName(...)
Cómo navegar a otra página desde la vista html
Podemos utilizar el binding routerLink, que nos permite editar una tarea específica:
Recordemos que la ruta editarTarea necesita dentro de la URL el id:
Los corchetes solo son necesarios para evaluar código typescript, de lo contrario podés utilizarlo en forma directa:
@for (tarea of tareas | ... ; track tarea; ...) { <tr [routerLink]="['/editarTarea, tarea.id]">
export const routes: Routes = [
{ path: 'listaTareas', component: ListaTareasComponent },
{ path: 'editarTarea/:id', component: EditarTareaComponent },
{ path: '**', redirectTo: '/listaTareas', pathMatch: 'full' },
]
<nav>
<a class="button" routerLink="/crisis-list">Crisis Center</a> |
<a class="button" routerLink="/heroes-list">Heroes</a>
</nav>
Cómo navegar a otra página desde la vista html (II)
De esa manera se construye la url final: http://localhost:4200/editarTarea/1
ActivatedRoute para poder capturar parámetros
El componente principal debe registrarse como interesado en recibir los parámetros desde la URL. Así puede capturar el valor del identificador de la tarea:
export class EditarTareaComponent implements OnInit {
tarea!: Tarea
descripcionTarea!: string
constructor(private tareaService: TareaService, private router: Router,
private route: ActivatedRoute) {}
ngOnInit() {
this.route.params.subscribe((editarTareaParameters) => {
const tarea = this.tareaService.getTareaById(editarTareaParameters.id)
{ path: 'editarTarea/:id', component: EditarTareaComponent },
id es el nombre del parámetro como lo definimos en la ruta
Navegando en forma programática
Podemos disparar la navegación desde nuestro componente si le inyectamos previamente el router y enviamos el mensaje navigate:
export class EditarTareaComponent implements OnInit {
constructor(private tareaService: TareaService, private router: Router,
private route: ActivatedRoute) { ...
navegarAHome() {
this.router.navigate(['/listaTareas'])
}
siempre trabajamos con el path
otra variante aquí abajo redireccionaría a “la siguiente tarea”...
this.router.navigate(['/editarTarea', this.tarea.id + 1])
Navegación con query parameters
En lugar de pasar el id como parámetro podemos generar un query param que construye una ruta como un par clave/valor
<a [routerLink]="['product-list']" [queryParams]="{ page: 99 }">Go to Page 99</a>v
ngOnInit() {
this.sub = this.route.paramMap.subscribe(params => { this.page = +params['page'] || 0 })
}
ngOnDestroy() {
this.sub.unsubscribe();
}
nextPage() {
this.router.navigate(['product-list'], { queryParams: { page: this.page + 1 } });
}
Base href en el index.html
Como parte del checklist hay que verificar que dentro del header de tu index.html principal tengas esta configuración:
Esto habilita los “deep links” o forma de acceder a un componente directamente.
<!doctype html>
<html lang="en">
<head>
...
<base href="/">
Base href en el index.html
Y para que funcionen tus tests de frontend vas a necesitar agregar esa configuración a TestBed:
describe('ListaTareasComponent', () => {
...
beforeEach(waitForAsync(() => {
TestBed.configureTestingModule({
declarations: [ ... ],
imports: [
FormsModule,
RouterModule.forRoot(routes)
],
providers: [
{ provide: APP_BASE_HREF, useValue: '/' }
representa el
<base href="/">
¡Gracias!