Lige nu viser vores app kun én ting: CO₂ beregneren. Men de fleste apps har flere sider. Tænk på et website med en forside, en "Om os" side og en kontaktside. Normalt ville browseren hente en helt ny HTML-side hver gang du klikker på et link.
Routing i Angular fungerer anderledes. I stedet for at hente en ny side fra serveren, bytter Angular bare den synlige komponent ud. URL'en i adresselinjen ændrer sig stadig (så du kan dele links og bruge browserens frem/tilbage knapper), men selve siden reloader aldrig. Det føles derfor lynhurtigt.
Angulars Router kigger på URL'en og bestemmer hvilken komponent der skal vises. Du definerer reglerne i en routes konfiguration: "Når URL'en er /, vis Calculator. Når den er /historik, vis History."
Først opretter vi den side brugerne skal kunne navigere til. Kør Angular CLI:
ng generate component history/history
Åbn den nye history/history.ts og giv den et simpelt indhold:
// history/history.ts
import { Component } from '@angular/core';
@Component({
selector: 'app-history',
template: `
<div class="history-page">
<h2>📋 Historik</h2>
<p>Historik — kommer snart</p>
</div>
`,
styles: `
.history-page {
text-align: center;
padding: 2rem;
}
`
})
export class History {}
Nu skal vi fortælle Angular hvilken komponent der hører til hvilken URL. Åbn app.config.ts og tilføj provideRouter(routes):
// app.config.ts
import { ApplicationConfig, provideZonelessChangeDetection } from '@angular/core';
import { provideHttpClient, withFetch } from '@angular/common/http';
import { provideRouter } from '@angular/router';
import { Routes } from '@angular/router';
import { Calculator } from './calculator/calculator';
import { History } from './history/history';
const routes: Routes = [
{ path: '', component: Calculator },
{ path: 'historik', component: History },
{ path: '**', redirectTo: '' }
];
export const appConfig: ApplicationConfig = {
providers: [
provideZonelessChangeDetection(),
provideHttpClient(withFetch()),
provideRouter(routes),
]
};
Her definerer vi tre routes:
path: '' — forsiden (tom URL) viser Calculator komponentenpath: 'historik' — URL'en /historik viser History komponentenpath: '**' — en wildcard der fanger alle ukendte URL'er og sender brugeren tilbage til forsidenRouteren ved nu hvilken komponent der skal vises, men den mangler et sted at placere den. Det gør vi med <router-outlet />. Tænk på det som en pladsholder: "Her skal den aktive sides komponent vises."
Åbn app.ts og erstat den direkte <app-calculator /> med <router-outlet />:
// app.ts
import { Component } from '@angular/core';
import { RouterOutlet } from '@angular/router';
import { Header } from './layout/header/header';
import { Footer } from './layout/footer/footer';
@Component({
selector: 'app-root',
imports: [RouterOutlet, Header, Footer],
template: `
<app-header />
<router-outlet />
<app-footer />
`
})
export class App {}
Bemærk at <app-header /> og <app-footer /> forbliver på plads. De vises altid. Det er kun indholdet mellem dem der skifter ud baseret på URL'en.
Nu skal brugerne kunne klikke sig mellem siderne. I header komponenten tilføjer vi links med Angulars routerLink direktiv. Det virker som et almindeligt <a> tag, men i stedet for at lave et helt page reload, beder det blot routeren om at skifte komponent.
Åbn layout/header/header.ts og tilføj imports:
// layout/header/header.ts
import { Component } from '@angular/core';
import { RouterLink, RouterLinkActive } from '@angular/router';
@Component({
selector: 'app-header',
imports: [RouterLink, RouterLinkActive],
templateUrl: './header.html',
styleUrl: './header.css',
})
export class Header {}
Opdater header.html med navigation:
<!-- layout/header/header.html -->
<div class="site-header">
<div class="header-inner">
<a class="logo" routerLink="/">
<img src="logo.png" alt="Logo" width="150" height="55" />
</a>
<nav>
<a routerLink="/"
routerLinkActive="active"
[routerLinkActiveOptions]="{ exact: true }">
CO2 Beregner
</a>
<a routerLink="/historik"
routerLinkActive="active">
Historik
</a>
</nav>
</div>
</div>
Her bruger vi tre ting fra Angular Router:
routerLink="/" — fortæller Angular at dette link skal navigere til den angivne URL (uden page reload)routerLinkActive="active" — tilføjer CSS-klassen active når linkets route er den aktive side[routerLinkActiveOptions]="{ exact: true }" — på forsiden (/) skal der kun markeres som aktiv ved et eksakt match. Ellers ville / også matche /historik fordi /historik starter med /Start dev-serveren og prøv at klikke mellem de to links i headeren:
ng serve
/ og /historik)active klasse (inspicér med DevTools)Lige nu importeres History komponenten direkte i app.config.ts. Det betyder at dens kode indgår i det JavaScript der hentes ved første sideload, selvom brugeren måske aldrig besøger historik siden.
Med lazy loading kan vi fortælle Angular: "Hent først History komponentens kode når brugeren faktisk navigerer til /historik." Det gør vi ved at erstatte component med loadComponent:
// app.config.ts — lazy loading version
const routes: Routes = [
{ path: '', component: Calculator },
{
path: 'historik',
loadComponent: () =>
import('./history/history').then(m => m.History)
},
{ path: '**', redirectTo: '' }
];
Nu kan du fjerne import { History } fra toppen af filen — den importeres dynamisk i stedet.
Test det:
/)loadComponent routes.
app.config.tshref links med klient-side navigationimport { ApplicationConfig, provideZonelessChangeDetection } from '@angular/core';
import { provideHttpClient, withFetch } from '@angular/common/http';
import { provideRouter } from '@angular/router';
import { Routes } from '@angular/router';
import { Calculator } from './calculator/calculator';
const routes: Routes = [
{ path: '', component: Calculator },
{
path: 'historik',
loadComponent: () =>
import('./history/history').then(m => m.History)
},
{ path: '**', redirectTo: '' }
];
export const appConfig: ApplicationConfig = {
providers: [
provideZonelessChangeDetection(),
provideHttpClient(withFetch()),
provideRouter(routes),
]
};
import { Component } from '@angular/core';
import { RouterOutlet } from '@angular/router';
import { Header } from './layout/header/header';
import { Footer } from './layout/footer/footer';
@Component({
selector: 'app-root',
imports: [RouterOutlet, Header, Footer],
template: `
<app-header />
<router-outlet />
<app-footer />
`
})
export class App {}
import { Component } from '@angular/core';
import { RouterLink, RouterLinkActive } from '@angular/router';
@Component({
selector: 'app-header',
imports: [RouterLink, RouterLinkActive],
template: `
<div class="site-header">
<div class="header-inner">
<a class="logo" routerLink="/">
<img src="logo.png" alt="Logo" width="150" height="55" />
</a>
<nav>
<a routerLink="/"
routerLinkActive="active"
[routerLinkActiveOptions]="{ exact: true }">
CO2 Beregner
</a>
<a routerLink="/historik"
routerLinkActive="active">
Historik
</a>
</nav>
</div>
</div>
`,
styleUrl: './header.css',
})
export class Header {}