Bloque 3 – Navegación y estructura de aplicación (Angular moderno)
Sesión 10 – Introducción al Router
Introducción teórica
En Angular moderno, la navegación en aplicaciones SPA se gestiona sin módulos, utilizando una configuración centralizada de rutas basada en objetos Routes.
El Router sigue siendo el encargado de asociar rutas con vistas, pero ahora se apoya en componentes standalone y en la función provideRouter, lo que simplifica la arquitectura y reduce la complejidad inicial del proyecto.
Ejemplo: configuración básica del Router en Panacea
Archivo app.routes.ts:
import { Routes } from '@angular/router';
export const routes: Routes = [
{
path: 'recursos',
loadComponent: () =>
import('./recursos/recursos.component')
.then(c => c.RecursosComponent)
},
{
path: 'activaciones',
loadComponent: () =>
import('./activaciones/activaciones.component')
.then(c => c.ActivacionesComponent)
}
];
Configuración del router en main.ts:
import { bootstrapApplication } from '@angular/platform-browser';
import { provideRouter } from '@angular/router';
import { AppComponent } from './app/app.component';
import { routes } from './app/app.routes';
bootstrapApplication(AppComponent, {
providers: [
provideRouter(routes)
]
});
Sesión 11 – Navegación entre vistas
Introducción teórica
En aplicaciones Angular sin módulos, el componente raíz actúa como contenedor de navegación.
El componente <router-outlet> indica dónde se renderiza la vista asociada a la ruta activa.
La navegación puede realizarse de forma declarativa mediante routerLink o de forma programática usando el servicio Router.
También se definen rutas por defecto y rutas comodín para mejorar la experiencia de navegación.
Ejemplo: navegación entre Recursos y Activaciones
Archivo app.component.ts:
import { Component } from '@angular/core';
import { RouterOutlet, RouterLink } from '@angular/router';
@Component({
selector: 'app-root',
standalone: true,
imports: [RouterOutlet, RouterLink],
template: `
<nav>
<a routerLink="/recursos">Recursos</a>
<a routerLink="/activaciones">Activaciones</a>
</nav>
<router-outlet></router-outlet>
`
})
export class AppComponent {}
Rutas por defecto y wildcard en app.routes.ts:
export const routes: Routes = [
{ path: '', redirectTo: 'recursos', pathMatch: 'full' },
{
path: 'recursos',
loadComponent: () =>
import('./recursos/recursos.component')
.then(c => c.RecursosComponent)
},
{
path: 'activaciones',
loadComponent: () =>
import('./activaciones/activaciones.component')
.then(c => c.ActivacionesComponent)
},
{ path: '**', redirectTo: 'recursos' }
];
Sesión 12 – Rutas con parámetros
Introducción teórica
Las rutas con parámetros permiten adaptar una vista al contexto, como mostrar el detalle de un recurso sanitario concreto.
En Angular moderno, el servicio ActivatedRoute sigue siendo el mecanismo para acceder a parámetros de ruta y query params, manteniendo una API coherente con versiones anteriores.
Ejemplo: detalle de recursos en Panacea
Ruta con parámetro:
{
path: 'recursos/:id',
loadComponent: () =>
import('./recursos/recursos.component')
.then(c => c.RecursosComponent)
}
Navegación con parámetro desde una vista:
<a [routerLink]="['/recursos', recurso.id]">
Ver recurso
</a>
Lectura del parámetro en el componente:
import { ActivatedRoute } from '@angular/router';
constructor(private route: ActivatedRoute) {}
ngOnInit() {
const id = this.route.snapshot.paramMap.get('id');
}
Uso de query params para filtrar recursos:
this.router.navigate(['/recursos'], {
queryParams: { estado: 'activo' }
});
Sesión 13 – Lazy loading y estructuración moderna
Introducción teórica
En Angular moderno, el lazy loading se realiza directamente a nivel de componente mediante loadComponent, sin necesidad de módulos intermedios.
Este enfoque permite dividir la aplicación en áreas funcionales independientes, mejorando el rendimiento inicial y la mantenibilidad del código en aplicaciones grandes como Panacea.
Ejemplo: carga diferida de vistas
{
path: 'activaciones',
loadComponent: () =>
import('./activaciones/activaciones.component')
.then(c => c.ActivacionesComponent)
}
Separación lógica dentro de una vista (ejemplo en Activaciones):
{
path: 'activaciones/:id',
loadComponent: () =>
import('./activaciones/activacion-detalle.component')
.then(c => c.ActivacionDetalleComponent)
}
Este enfoque permite estructurar vistas complejas sin introducir módulos.
Sesión 14 – Guards y control de acceso
Introducción teórica
Angular moderno introduce guards funcionales, que sustituyen a las clases tradicionales y reducen el código necesario. Los guards permiten controlar el acceso a rutas y los resolvers facilitan la precarga de datos antes de mostrar una vista, manteniendo separada la lógica de navegación de la lógica de presentación.
Ejemplo: guard funcional con login mockeado
Servicio de autenticación simulado:
import { Injectable } from '@angular/core';
@Injectable({ providedIn: 'root' })
export class AuthService {
private logged = true;
isLogged(): boolean {
return this.logged;
}
}
Guard funcional CanActivate:
import { inject } from '@angular/core';
import { CanActivateFn, Router } from '@angular/router';
import { AuthService } from './auth.service';
export const authGuard: CanActivateFn = () => {
const auth = inject(AuthService);
const router = inject(Router);
if (auth.isLogged()) {
return true;
}
router.navigate(['/recursos']);
return false;
};
Uso del guard en rutas:
{
path: 'activaciones',
canActivate: [authGuard],
loadComponent: () =>
import('./activaciones/activaciones.component')
.then(c => c.ActivacionesComponent)
}
Introducción a un resolver funcional:
import { ResolveFn } from '@angular/router';
export const recursosResolver: ResolveFn<string[]> = () => {
return ['Recurso A', 'Recurso B'];
};
Aplicación del resolver:
{
path: 'recursos',
resolve: { datos: recursosResolver },
loadComponent: () =>
import('./recursos/recursos.component')
.then(c => c.RecursosComponent)
}