Komponenty Angular

1. Czym są Komponenty?

W ekosystemie Angular, komponenty stanowią kluczowe elementy, które umożliwiają budowanie dynamicznych i interaktywnych aplikacji webowych. Komponent w Angularze to klasa TypeScript z dekoratorem @Component, który dostarcza dodatkowe metadane definiujące szablon HTML, style oraz selektor.

Komponenty są podstawowymi budulcami strukturalnymi w aplikacjach Angulara. Każdy komponent zarządza swoim fragmentem widoku (DOM) poprzez szablon oraz logiką, która definiuje zachowanie tego widoku. Komponenty pozwalają na enkapsulację różnych części aplikacji, co ułatwia ich ponowne użycie, testowanie i utrzymanie.

Przykłady komponentów w aplikacji filmowej:

  • Komponent listy filmów
  • Komponent karty filmu
  • Komponent oceny filmu
  • Komponent nawigacji
  • Komponent stopki

2. Budowa Komponentu

Komponent w Angularze składa się z:

  • Klasy komponentu: Definiuje logikę działania komponentu. Klasa ta jest oznaczona dekoratorem @Component, który informuje Angulara, że dana klasa jest komponentem i dostarcza metadane konfiguracyjne.
  • Szablonu (Template): HTML opisujący, jak ma być wyświetlany komponent. Może zawierać dynamiczne dane za pomocą interpolacji, dyrektyw i wiązania danych.
  • Stylów (Styles): CSS definiujący wygląd komponentu. Style mogą być załączane bezpośrednio w komponencie lub w zewnętrznych plikach.

3. Dekorator @Component

Dekorator @Component przyjmuje obiekt konfiguracyjny, który może zawierać następujące właściwości:

  • selector: CSS selector określający, w jaki sposób użyć komponentu w szablonie. Na przykład, jeśli selektor to app-my-component, w HTML używamy go jako <app-my-component></app-my-component>.
  • templateUrl: Ścieżka do zewnętrznego pliku zawierającego szablon HTML komponentu.
  • template: Szablon HTML w formie stringa, jeśli nie używamy zewnętrznego pliku.
  • styleUrls: Ścieżki do zewnętrznych plików CSS definiujących style komponentu.
  • styles: Style w formie stringa, jeśli nie używamy zewnętrznych plików.
  • standalone: NOWOŚĆ! Określa czy komponent jest standalone (nie wymaga NgModule).
  • imports: NOWOŚĆ! Lista importów dla komponentów standalone.

4. Ewolucja komponentów – tradycyjne vs standalone

Tradycyjne komponenty (przed Angular 14)

Wcześniej wszystkie komponenty musiały być zadeklarowane w NgModule:

// film.component.ts - STARY SPOSÓB
import { Component } from '@angular/core';

@Component({
  selector: 'app-film',
  templateUrl: './film.component.html',
  styleUrl: './film.component.css'
})
export class FilmComponent {
  title = 'Avengers: Endgame';
}
// app.module.ts - WYMAGANY dla tradycyjnych komponentów
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';

import { AppComponent } from './app.component';
import { FilmComponent } from './film/film.component';

@NgModule({
  declarations: [
    AppComponent,
    FilmComponent  // Musi być tutaj zadeklarowany!
  ],
  imports: [BrowserModule],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

Nowoczesne Standalone Components (Angular 14+)

Od Angular 14 możemy tworzyć komponenty standalone, które nie wymagają NgModule:

// film.component.ts - NOWY SPOSÓB (ZALECANY)
import { Component } from '@angular/core';

@Component({
  selector: 'app-film',
  standalone: true,  // 🆕 To czyni komponent standalone!
  imports: [],       // 🆕 Importy bezpośrednio w komponencie
  templateUrl: './film.component.html',
  styleUrl: './film.component.css'
})
export class FilmComponent {
  title = 'Avengers: Endgame';
}

Zalety Standalone Components:

  • Prostsze – nie wymagają NgModule
  • Szybsze – lepsze lazy loading
  • Czytelniejsze – wszystko w jednym miejscu
  • Nowoczesne – przyszłość Angulara
  • Łatwiejsze testowanie – mniej konfiguracji

5. Generowanie komponentów z Angular CLI

Angular CLI to narzędzie, które automatycznie generuje komponenty. Aby stworzyć nowy komponent, użyj Angular CLI w katalogu głównym Twojego projektu.

Podstawowe polecenia

# Generowanie podstawowego komponentu
ng generate component nazwa-komponentu

# Lub w skrócie:
ng g c nazwa-komponentu

Co generuje Angular CLI?

Po wykonaniu polecenia ng g c my-component Angular CLI automatycznie generuje nowy katalog my-component w folderze /src/app/ z czterema plikami:

  • my-component.component.ts – plik klasy komponentu, zawierający logikę komponentu
  • my-component.component.html – plik szablonu, definiujący strukturę HTML komponentu
  • my-component.component.css – plik stylów CSS, określający wygląd komponentu
  • my-component.component.spec.ts – plik testu jednostkowego dla komponentu

Przydatne opcje przy generowaniu

# Bez pliku testowego
ng g c film-list --skip-tests

# Bez pliku CSS (używa inline styles)
ng g c film-list --inline-style

# Bez pliku HTML (używa inline template)
ng g c film-list --inline-template

# W określonym folderze
ng g c components/film-list 

# Bez folderu dla komponentu (wszystkie pliki w jednym miejscu)
ng g c film-list --flat

6. Struktura standalone komponentu – analiza pliku .ts

Komponent MyComponentComponent jest przykładem komponentu standalone w Angularze 17, co oznacza, że został zdefiniowany bez konieczności używania tradycyjnego NgModule. To uproszczone podejście jest nowością w Angularze 17 i ma na celu zmniejszenie złożoności aplikacji.

import { Component } from '@angular/core';

@Component({
  selector: 'app-my-component',
  standalone: true,
  imports: [],
  templateUrl: './my-component.component.html',
  styleUrl: './my-component.component.css'
})
export class MyComponentComponent {

}

Importy

W komponencie zaimportowano jedynie podstawowy moduł:

import { Component } from '@angular/core';

Component jest dekoratorem z biblioteki @angular/core, który pozwala Angularowi rozpoznać klasę jako komponent oraz dostarcza metadane, które konfigurują zachowanie komponentu.

Dekorator @Component

Dekorator @Component jest wykorzystywany do definiowania kluczowych właściwości komponentu:

@Component({
  selector: 'app-my-component',
  standalone: true,
  imports: [],
  templateUrl: './my-component.component.html',
  styleUrl: './my-component.component.css'
})

Wyjaśnienie właściwości:

  • selector: 'app-my-component' – to unikalna nazwa selektora, używana do wstrzykiwania komponentu w szablonach HTML innych komponentów lub stron
  • standalone: true – ustawienie true oznacza, że komponent jest samowystarczalny i nie potrzebuje być deklarowany w żadnym NgModule, co jest znaczącą zmianą w stosunku do wcześniejszych wersji Angulara
  • imports: [] – w tym przykładzie jest pusta tablica, ale można tutaj umieścić inne komponenty, dyrektywy lub moduły, które są używane w szablonie komponentu
  • templateUrl: './my-component.component.html' – ścieżka do pliku HTML, który zawiera szablon widoku komponentu
  • styleUrl: './my-component.component.css' – ścieżka do pliku CSS zawierającego style dla komponentu

Klasa Komponentu

export class MyComponentComponent {
  // Tu można dodać logikę komponentu
}

7. Używanie komponentu w aplikacji

Jeżeli chcesz użyć MyComponentComponent w AppComponent, który również jest komponentem standalone, oto jak to zrobić:

Krok 1: Zaimportuj komponent

Najpierw musisz zaimportować MyComponentComponent w pliku TypeScript komponentu, który go używa:

import { MyComponentComponent } from './path/to/my-component.component';

Krok 2: Dodaj do imports w dekoratorze @Component

Następnie dodaj MyComponentComponent do listy imports w dekoratorze @Component:

// app.component.ts
import { Component } from '@angular/core';
import { MyComponentComponent } from './my-component/my-component.component';

@Component({
  selector: 'app-root',
  standalone: true,
  imports: [MyComponentComponent],  // Importowanie MyComponentComponent
  templateUrl: './app.component.html',
  styleUrl: './app.component.css'
})
export class AppComponent {
  // Tutaj można dodać dodatkową logikę
}

Krok 3: Użycie w HTML

Teraz MyComponentComponent jest dostępny do użycia w szablonie AppComponent, co oznacza, że możesz użyć jego selektora app-my-component w HTML:

<!-- app.component.html -->
<div>
  <h1>Witaj w mojej aplikacji!</h1>
  <app-my-component></app-my-component>
</div>

8. Inline Template vs zewnętrzny plik HTML

Tradycyjny komponent (zewnętrzny plik HTML)

Ma szablon HTML umieszczony w osobnym pliku .html:

  • Jest to standardowe podejście, które pomaga utrzymać porządek w projektach
  • Ułatwia współpracę i utrzymanie kodu, ponieważ HTML i logika komponentu są oddzielone
  • Zalecane dla większych komponentów

Komponent z Inline Template

Cała struktura HTML komponentu znajduje się bezpośrednio w dekoratorze @Component:

  • Umożliwia szybkie tworzenie mniejszych, prostszych komponentów
  • Może ułatwić zarządzanie małymi komponentami
  • Zalecane tylko dla bardzo prostych komponentów

Generowanie komponentu z Inline Template

Aby wygenerować komponent z użyciem inline template przy pomocy Angular CLI:

ng generate component my-inline-component --inline-template

# lub w skróconej formie:
ng g c my-inline-component -t

To spowoduje utworzenie komponentu, w którym definicja HTML jest umieszczona bezpośrednio w dekoratorze @Component:

import { Component } from '@angular/core';

@Component({
  selector: 'app-my-inline-component',
  standalone: true,
  imports: [],
  template: `
    <p>
      my-inline-component works!
    </p>
  `,
  styleUrl: './my-inline-component.component.css'
})
export class MyInlineComponentComponent {

}

Zamiast templateUrl używamy właściwości template do bezpośredniego określenia szablonu HTML.

9. Praktyczne przykłady komponentów

Przykład 1: Prosty komponent powitalny

// welcome.component.ts
import { Component } from '@angular/core';

@Component({
  selector: 'app-welcome',
  standalone: true,
  template: `<h1>Witaj, {{ name }}!</h1>`,
  styles: [`h1 { color: green; }`]
})
export class WelcomeComponent {
  name = 'świecie';
}

Przykład 2: Komponent z zewnętrznymi plikami

// film-info.component.ts
import { Component } from '@angular/core';

@Component({
  selector: 'app-film-info',
  standalone: true,
  imports: [],
  templateUrl: './film-info.component.html',
  styleUrl: './film-info.component.css'
})
export class FilmInfoComponent {
  filmTitle = 'Avengers: Endgame';
  filmYear = 2019;
  filmRating = 8.4;
  
  getRatingColor(): string {
    if (this.filmRating >= 8.0) return 'green';
    if (this.filmRating >= 7.0) return 'orange';
    return 'red';
  }
}
<!-- film-info.component.html -->
<div class="film-card">
  <h2>{{ filmTitle }}</h2>
  <p>Rok produkcji: {{ filmYear }}</p>
  <p class="rating" [style.color]="getRatingColor()">
    Ocena: {{ filmRating }}/10
  </p>
</div>
/* film-info.component.css */
.film-card {
  border: 1px solid #ccc;
  border-radius: 8px;
  padding: 16px;
  margin: 16px;
  box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}

.rating {
  font-weight: bold;
  font-size: 1.2em;
}

Przykład 3: Komponent z dynamicznymi danymi

// film-counter.component.ts
import { Component } from '@angular/core';

@Component({
  selector: 'app-film-counter',
  standalone: true,
  template: `
    <div class="counter">
      <h3>Licznik obejrzanych filmów</h3>
      <p>Obejrzanych filmów: {{ filmCount }}</p>
      <button (click)="addFilm()">Dodaj film</button>
      <button (click)="resetCounter()">Reset</button>
    </div>
  `,
  styles: [`
    .counter {
      text-align: center;
      padding: 20px;
      border: 2px solid #007bff;
      border-radius: 10px;
      margin: 20px;
    }
    button {
      margin: 5px;
      padding: 8px 16px;
      cursor: pointer;
    }
  `]
})
export class FilmCounterComponent {
  filmCount = 0;
  
  addFilm() {
    this.filmCount++;
  }
  
  resetCounter() {
    this.filmCount = 0;
  }
}

10. Ćwiczenia do samodzielnego wykonania

  1. Komponent wizytówki – stwórz komponent wyświetlający swoje dane
  2. Komponent zegara – pokazuje aktualny czas
  3. Komponent licznika – zwiększa/zmniejsza wartość przyciskami
  4. Komponent pogody – wyświetla informacje o pogodzie
  5. Komponent kalkulatora – prosty kalkulator z podstawowymi operacjami