Angular

Lab 1: Extract Components

arrow_back Alle labs
timer 25 minutter
sell module-1-start → module-2-start
Mål: Opdel den monolitiske app.ts i separate, feature baserede components. Forstå hvorfor styles går i stykker, og lær om ViewEncapsulation.

Udgangspunkt

Checkout tag module-1-start. Hele applikationen lever i én fil: src/app/app.ts med inline template og inline styles (~600 linjer).

git checkout module-1-start

Åbn src/app/app.ts og studér strukturen. Du kan se header, herd input cards, result panel og footer i én stor template.

Trin for trin

looks_one Generér layout components

ng generate component layout/header
ng generate component layout/footer

Bemærk filstrukturen der oprettes:

src/app/layout/
├── header/
│   ├── header.ts
│   ├── header.html
│   ├── header.css
│   └── header.spec.ts
└── footer/
    ├── footer.ts
    ├── footer.html
    ├── footer.css
    └── footer.spec.ts

Læg mærke til at klasserne hedder Header og Footer. Angular genererer korte, beskrivende klassenavne uden "Component" suffix. Se ng generate dokumentationen.

looks_two Generér calculator components

ng generate component calculator/calculator --inline-style --inline-template --skip-tests
ng generate component calculator/herd-input
ng generate component calculator/result-panel

Calculator bruger --inline-style og --inline-template fordi den kun er en layout wrapper.

looks_3 Flyt Header HTML og CSS

Kopiér header sektionen fra app.ts template til header.html:

<!-- header.html -->
<div class="site-header">
  <div class="header-inner">
    <a class="logo" href="/">
      <img src="logo.png" alt="Logo" width="150" height="55" />
    </a>
  </div>
</div>

Flyt de tilhørende styles fra app.ts til header.css.

looks_4 Flyt Footer HTML og CSS

<!-- footer.html -->
<footer>
  <span class="material-symbols-outlined footer-icon">eco</span>
  <p>Landbrugets Klimaberegner &copy; 2026</p>
</footer>

looks_5 Flyt HerdInput og ResultPanel

Flyt det store grid med dyrekort til herd-input.html og herd-input.css. Flyt klimaregnskab panelet til result-panel.html og result-panel.css.

looks_6 Opsæt Calculator som layout wrapper

// calculator.ts
import { Component } from '@angular/core';
import { HerdInput } from './herd-input/herd-input';
import { ResultPanel } from './result-panel/result-panel';

@Component({
  selector: 'app-calculator',
  imports: [HerdInput, ResultPanel],
  template: `
    <div class="layout">
      <app-herd-input />
      <app-result-panel />
    </div>
  `,
  styles: `
    .layout {
      max-width: 80rem;
      margin: 0 auto;
      padding: 2.5rem 1rem;
      display: grid;
      grid-template-columns: 1fr;
      gap: 2rem;
      align-items: start;
    }
    @media (min-width: 1024px) {
      .layout { grid-template-columns: 7fr 5fr; }
    }
  `,
})

counter_7 Opdater app.ts

// app.ts
import { Component } from '@angular/core';
import { Footer } from './layout/footer/footer';
import { Header } from './layout/header/header';
import { Calculator } from './calculator/calculator';

@Component({
  selector: 'app-root',
  template: `
    <div class="shell">
      <app-header />
      <main>
        <app-calculator />
      </main>
      <app-footer />
    </div>
  `,
  styles: [`
    .shell { min-height: 100vh; display: flex; flex-direction: column; }
    main { flex: 1; }
  `],
  imports: [Footer, Header, Calculator],
})
export class App {}

warning ViewEncapsulation

Når du flytter HTML til child components, vil du opdage at styles går i stykker. Headeren mister sit layout, kortene ser forkerte ud osv.

lightbulb
Hvorfor? Angular isolerer CSS pr. component (kaldet View Encapsulation). Det betyder at styles i en component kun gælder for den pågældende components template. Styles fra app.ts virker ikke længere på HTML der nu bor i child components. Læs mere i style scoping dokumentationen.

Løsningen: Flyt de relevante styles med til den component hvor HTML'en bor. Header styles → header.css, card styles → herd-input.css, panel styles → result-panel.css osv.

Hint: Komplet filstruktur efter lab 1
src/app/
├── app.config.ts
├── app.ts
├── calculator/
│   ├── calculator.ts          (inline template + styles)
│   ├── herd-input/
│   │   ├── herd-input.ts
│   │   ├── herd-input.html
│   │   ├── herd-input.css
│   │   └── herd-input.spec.ts
│   └── result-panel/
│       ├── result-panel.ts
│       ├── result-panel.html
│       ├── result-panel.css
│       └── result-panel.spec.ts
└── layout/
    ├── header/
    │   ├── header.ts
    │   ├── header.html
    │   ├── header.css
    │   └── header.spec.ts
    └── footer/
        ├── footer.ts
        ├── footer.html
        ├── footer.css
        └── footer.spec.ts
Hint: Hvordan ser header.ts ud?
import { Component } from '@angular/core';

@Component({
  selector: 'app-header',
  imports: [],
  templateUrl: './header.html',
  styleUrl: './header.css',
})
export class Header {}

Bemærk: styleUrl (ental) og templateUrl peger på eksterne filer. Klassen hedder Header (kort og beskrivende, uden "Component" suffix).


Dokumentation