Salta el contingut

Rutes a Angular

Angular s'utilitza habitualment per desenvolupar Single Page Applications (SPA). Tot i ser una SPA, l'aplicació ha de comportar-se de manera similar als llocs web tradicionals pel que fa a les URLs (Uniform Resource Identifiers). Això implica que necessitem poder referenciar externament les diferents parts de l'aplicació, tenir la capacitat de navegar enrere i endavant a l'historial del navegador i gestionar rutes virtuals adequadament.

Les rutes a Angular es defineixen al fitxer app-routing.module.ts. Les rutes són objectes que contenen el camí (path) i el component al qual fan referència. Les pàgines en una SPA d'Angular estan representades per components, i el router carrega les rutes dins d'un <router-outlet> a la plantilla principal de l'aplicació.

Exemple Bàsic de Rutes

El següent és un exemple de com configurar rutes bàsiques a Angular:

import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { HomeComponent } from './home/home.component';
import { PlanetListComponent } from './planets/planet-list/planet-list.component';
import { SunComponent } from './sun/sun.component';
import { PlanetDetailComponent } from './planets/planet-detail/planet-detail.component';
import { PlanetEditComponent } from './planets/planet-edit/planet-edit.component';
import { LoginComponent } from './auth/login/login.component';
import { AuthGuard } from './auth/auth.guard';
import { PlanetResolveService } from './planets/planet-resolve.service';

const routes: Routes = [
  { path: 'home', component: HomeComponent },
  { path: 'planets', canActivate: [AuthGuard], component: PlanetListComponent },
  { path: 'suns', canActivate: [AuthGuard], component: SunComponent },
  { path: 'planet/:id', canActivate: [AuthGuard], component: PlanetDetailComponent },
  { path: 'planet/edit/:id', canActivate: [AuthGuard], resolve: { planet: PlanetResolveService }, component: PlanetEditComponent },
  { path: 'login', component: LoginComponent },
  { path: '**', pathMatch: 'full', redirectTo: 'home' }
];

@NgModule({
  imports: [RouterModule.forRoot(routes, { useHash: true })],
  exports: [RouterModule]
})
export class AppRoutingModule { }
  • path: Defineix l'URI per la ruta.
  • component: El component que es carrega quan es navega a aquesta ruta.
  • canActivate: Defineix guardes que protegeixen les rutes.
  • resolve: Permet precarregar dades abans que el component es carregui.

Rutes amb Hash

Una manera d'implementar SPA sense manipular el servidor és emprar una almohadilla (#) al principi de la ruta:

http://localhost:4200/#/home

Això és més antic, però funciona a tots els navegadors, simplifica l'enviament de paràmetres i evita la manipulació del servidor. Perquè funcioni, cal afegir withHashLocation() als providers del bootstrap:

bootstrapApplication(AppComponent, {
  providers: [
    provideRouter(routes, withHashLocation()),
  ],
});

Creació de Rutes

  • Ruta Bàsica: Defineix un camí i el component que s'activa.
{ path: 'home', component: HomeComponent }
  • Ruta amb Guard: Protegeix rutes usant guardes.
{ path: 'planets', canActivate: [AuthGuard], component: PlanetListComponent }
  • Ruta amb Paràmetres: Permet passar paràmetres a la URL.
{ path: 'planet/:id', canActivate: [AuthGuard], component: PlanetDetailComponent }
  • Ruta amb Resolve: Precarrega dades necessàries pel component.
{ path: 'planet/edit/:id', canActivate: [AuthGuard], resolve: { planet: PlanetResolveService }, component: PlanetEditComponent }
  • Ruta per Defecte: Redirigeix a una ruta específica si la ruta no existeix.
{ path: '**', pathMatch: 'full', redirectTo: 'home' }

Enllaços de Navegació

Per crear enllaços de navegació a Angular, s'utilitza [routerLink] en lloc de href:

<a class="nav-link active" aria-current="page" [routerLink]="['home']">Home</a>

Si la ruta té més nivells, s'utilitzen elements addicionals a l'array.

<a class="nav-link" aria-current="page" [routerLink]="['home']" [routerLinkActive]="['active']">Home</a>

L'atribut routerLinkActive pot estar sense claudàtors i aplicar-se a l'element pare de l'enllaç si cal.

Per navegar per codi a Angular, s'importa el Router i s'injecta al constructor:

import { Router } from '@angular/router';

constructor(private router: Router) {}

detailsProduct(id: number): void {
  this.router.navigate(['/product', id]);
}

Obtenir Paràmetres de les Rutes

Les rutes poden contenir paràmetres, com un id. Per obtenir aquests paràmetres, s'utilitza ActivatedRoute:

import { ActivatedRoute } from '@angular/router';

constructor(private activatedRoute: ActivatedRoute) { }

ngOnInit(): void {
  this.activatedRoute.params.subscribe(params => {
    console.log(params);
  });
}

Els paràmetres (params) són un observable al qual ens hi subscrivim per rebre els valors passats a la URL.

A partir d'Angular 16, es poden configurar els paràmetres del router per acceptar-los mitjançant @Input() utilitzant withComponentInputBinding().

bootstrapApplication(App, {
  providers: [
    provideRouter(routes,
        //... other features
        withComponentInputBinding() // <-- enable this feature
    )
  ],
});
Al component:

 @Input() query?: string; // Normal
 @Input('q') queryParam?: string; // Renombrar el paràmetre

Al router:

{ path: 'planet/:query', canActivate: [AuthGuard], component: PlanetDetailComponent }

Transicions a Angular (2024)

Les transicions a Angular permeten crear animacions suaus entre diferents estats de l'aplicació. Actualment, la funcionalitat completa d'aquestes transicions només està totalment disponible a Google Chrome. A continuació es detalla com implementar i personalitzar les transicions a Angular.

Definició d'Animacions en CSS

Primer, definim les animacions CSS utilitzant @keyframes. Això permet fer rotacions dels elements durant les transicions.

@keyframes rotate-out {
  to {
    transform: rotate(90deg);
  }
}

@keyframes rotate-in {
  from {
    transform: rotate(-90deg);
  }
}

::view-transition-old(count),
::view-transition-new(count) {
  animation-duration: 200ms;
  animation-name: -ua-view-transition-fade-in, rotate-in;
}

::view-transition-old(count) {
  animation-name: -ua-view-transition-fade-out, rotate-out;
}

En aquest exemple: - rotate-out: fa girar un element 90 graus. - rotate-in: fa girar un element des de -90 graus fins a la seva posició original. - ::view-transition-old(count) i ::view-transition-new(count): apliquen les animacions durant la transició.

Configuració de Rutes a Angular

Per habilitar les transicions, configurem les rutes a app-routing.module.ts utilitzant provideRouter i afegint withViewTransitions().

providers: [
  provideRouter(
    [
      { path: '', pathMatch: 'full', redirectTo: '/0' },
      { path: ':count', component: Counter },
    ],
    withViewTransitions(),
    withComponentInputBinding()
  ),
],
  • provideRouter: Defineix les rutes de l'aplicació.
  • withViewTransitions(): Habilita les transicions de vista.
  • withComponentInputBinding(): Permet l'enllaç d'entrada del component.

Personalització de les Transicions

És possible personalitzar les transicions al fitxer CSS.

::view-transition-old(count),
::view-transition-new(count) {
  animation-duration: 200ms;
  animation-name: -ua-view-transition-fade-in, rotate-in;
}

::view-transition-old(count) {
  animation-name: -ua-view-transition-fade-out, rotate-out;
}

Aquí es defineixen les durades i els noms de les animacions per a les transicions d'entrada i sortida.

Ús de onViewTransitionCreated

withViewTransitions accepta un objecte amb la funció onViewTransitionCreated per gestionar esdeveniments de transició.

withViewTransitions({
  onViewTransitionCreated: ({ transition }) => {
    const router = inject(Router);
    const targetUrl = router.getCurrentNavigation()!.finalUrl!;

    const config = {
      paths: 'exact',
      matrixParams: 'exact',
      fragment: 'ignored',
      queryParams: 'ignored',
    };

    if (router.isActive(targetUrl, config)) {
      transition.skipTransition();
    }
  },
})

En aquest exemple:

  • onViewTransitionCreated: s'invoca quan es crea una transició de vista.

  • transition.skipTransition(): cancel·la l'animació si només canvien el fragment o els paràmetres de consulta.