Przejdź do treści
School IT / Przestrzeń do nauki

Własny komponent

Jeden wielki plik app.html szybko robi się nieczytelny. Podzielisz ekran na części, z których każda ma własną nazwę i własne pliki — i użyjesz tej samej części kilka razy.

Angular 22 ng generate component imports selector style komponentu 45 min
CEL LEKCJI

Czego się dziś nauczysz

  • Wygenerujesz komponent poleceniem ng generate component i wymienisz pliki, które powstały.
  • Użyjesz komponentu w szablonie innego komponentu.
  • Wyjaśnisz, po co klasa komponentu pojawia się dwa razy: w import i w tablicy imports.
  • Rozpoznasz błąd NG8001 i ostrzeżenie NG8113 i powiesz, co je wywołuje.
  • Wstawisz ten sam komponent kilka razy i powiesz, dlaczego wszystkie wyglądają tak samo.
  • Sprawdzisz, że style komponentu nie wychodzą poza jego szablon.
  • Uruchomisz u siebie przykład z kursu, wstawiając jego komponent demo do app.html.

Przygotowanie: lekcje 01–03 — masz projekt z Bootstrapem i wiesz, czym jest selektor. Przewidywany czas: 45 min z zadaniami.

TEORIA

Po co dzielić ekran na części

Strona klubu ma pokazywać dziesięć wydarzeń. Możesz dziesięć razy skopiować ten sam kawałek HTML-a karty — i przy pierwszej poprawce zmieniać go w dziesięciu miejscach.

Komponent to taki kawałek opisany raz, z własną nazwą. Używasz go tyle razy, ile potrzeba, a poprawiasz w jednym pliku. Przy okazji nazwa mówi, co to jest: <app-karta /> czyta się lepiej niż <div class="card p-3">.

Znasz już jeden komponent — App z lekcji 02. Każdy nowy wygląda tak samo: klasa, szablon, dekorator @Component z selektorem.

TEORIA

Trzy kroki, zawsze te same

KrokCo robiszGdzie
1. Wygenerujng generate component kartaterminal w folderze projektu
2. Zaimportujimport { Karta } from './karta/karta'; i Karta w tablicy importsplik .ts komponentu, który ma użyć karty
3. Wstaw znacznik<app-karta />szablon tego samego komponentu

Krok 2 jest podwójny i to nie przypadek: import na górze pliku mówi TypeScriptowi, skąd wziąć klasę; wpis w tablicy imports mówi Angularowi, że wolno jej użyć w tym szablonie.

Znacznik krótki i długi

<app-karta /> i <app-karta></app-karta> znaczą w Angularze to samo. Krótsza forma działa tylko dla komponentów — zwykłego <div /> tak nie zapiszesz. W kursie używamy krótszej.

NG8001 — najczęstszy błąd tej lekcji

’app-karta’ is not a known element pojawia się, gdy pominiesz krok 2. Komunikat brzmi groźnie, a oznacza po prostu: „nie powiedziałeś mi, że wolno mi tego użyć”. Odwrotna sytuacja — klasa w imports, a znacznika brak w szablonie — daje tylko ostrzeżenie NG8113, znane z lekcji 01.

PRZYKŁAD 1

Wygeneruj komponent jednym poleceniem

CLI utworzy folder z kompletem plików. Nie twórz ich ręcznie — łatwo o literówkę.

terminal — w folderze projektu
ng generate component karta
wynik w terminalu
CREATE src/app/karta/karta.css (0 bytes)
CREATE src/app/karta/karta.spec.ts (525 bytes)
CREATE src/app/karta/karta.ts (181 bytes)
CREATE src/app/karta/karta.html (20 bytes)
src/app/karta/karta.ts — tak wygląda po wygenerowaniu
import { Component } from '@angular/core';

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

Skrót ng g c karta robi to samo. CLI dopisuje do selektora przedrostek app-, a klasie nadaje nazwę wielką literą. Plik karta.spec.ts to test jednostkowy — w tym kursie go nie używamy, ale nie przeszkadza. Kolejność ustawień w dekoratorze nie ma znaczenia.

Zatrzymaj się

Dlaczego selektory komponentów mają myślnik w nazwie? Podpowiedź: nazwy znaczników bez myślnika (card, header) są zarezerwowane dla HTML-a.

PRZYKŁAD 2

Użyj komponentu w innym komponencie

Od tej lekcji każdy przykład ma komponent Demo — to rodzic, w którym pokazujemy nowy mechanizm. Tutaj rodzic wstawia kartę.

src/app/nowa-04-1/karta.ts — pełny plik
import { Component } from '@angular/core';

@Component({
    selector: 'app-karta',
    imports: [],
    templateUrl: './karta.html'
})
export class Karta {}
src/app/nowa-04-1/karta.html — pełny plik
<div class="card p-3 mb-2" style="width: 20rem">
  <h5>Turniej retro</h5>
  <p class="mb-0">Piątek, sala 12.</p>
</div>
src/app/nowa-04-1/demo.ts — pełny plik
import { Component } from '@angular/core';
import { Karta } from './karta';

@Component({
    selector: 'app-demo',
    imports: [Karta],
    templateUrl: './demo.html'
})
export class Demo {}
src/app/nowa-04-1/demo.html — pełny plik
<div class="p-3">
  <h4>Nasze wydarzenia</h4>
  <app-karta />
</div>
wynik w przeglądarce
Nasze wydarzenia
┌────────────────────────┐
│ Turniej retro          │
│ Piątek, sala 12.       │
└────────────────────────┘

Karta i rodzic leżą w tym samym folderze, dlatego ścieżka importu to './karta'. Komponent nie ma pliku .css, więc w dekoratorze nie ma styleUrl — to dozwolone.

Jak uruchomić przykład z kursu u siebie

  1. Skopiuj folder przykładu (tu: nowa-04-1 z wszystkimi plikami) do src/app swojego projektu.
  2. W src/app/app.ts dopisz import { Demo } from './nowa-04-1/demo'; i wpisz Demo do tablicy imports.
  3. W src/app/app.html zostaw tylko <app-demo />.

Przy następnym przykładzie zmieniasz tylko nazwę folderu w imporcie. To są dokładnie trzy kroki z tej lekcji — rodzicem Demo jest App.

Zatrzymaj się

Usuń Karta z tablicy imports (zostaw import na górze) i zapisz. Jaki kod błędu podał terminal? Potem zrób odwrotnie: przywróć wpis, ale usuń <app-karta /> z szablonu. Co się zmieniło — błąd czy ostrzeżenie?

PRZYKŁAD 3

Ten sam komponent trzy razy

Tu widać sens dzielenia ekranu na części: jeden opis, wiele wystąpień. Karta trafia do siatki z lekcji 03.

src/app/nowa-04-2/karta.html — pełny plik
<div class="card p-3">
  <h5>Wydarzenie</h5>
  <p class="mb-0">Szczegóły wkrótce.</p>
</div>
src/app/nowa-04-2/demo.html — pełny plik
<div class="container mt-3">
  <div class="row g-3">
    <div class="col-12 col-md-4"><app-karta /></div>
    <div class="col-12 col-md-4"><app-karta /></div>
    <div class="col-12 col-md-4"><app-karta /></div>
  </div>
</div>

Pliki karta.ts i demo.ts są takie same jak w przykładzie 2 — zmieniają się tylko szablony.

wynik w przeglądarce
[Wydarzenie        ] [Wydarzenie        ] [Wydarzenie        ]
[Szczegóły wkrótce.] [Szczegóły wkrótce.] [Szczegóły wkrótce.]

Trzy identyczne karty. Zmień tekst w karta.html raz — zmieni się we wszystkich trzech naraz.

Zatrzymaj się

Wszystkie karty są identyczne, bo skąd miałyby wiedzieć, że mają się różnić? Zapamiętaj to pytanie — odpowiedzią jest input() z lekcji 29, gdzie rodzic powie każdej karcie, co ma pokazać.

PRZYKŁAD 4

Styl komponentu nie wycieka na zewnątrz

Każdy komponent może mieć własny CSS, który obowiązuje wyłącznie w jego szablonie. Krótki styl można wpisać wprost w dekoratorze, zamiast w osobnym pliku .css.

src/app/nowa-04-3/karta.ts — pełny plik
import { Component } from '@angular/core';

@Component({
    selector: 'app-karta',
    imports: [],
    templateUrl: './karta.html',
    styles: `h5 { color: #d63384; }`
})
export class Karta {}
src/app/nowa-04-3/karta.html — pełny plik
<div class="card p-3 mb-2">
  <h5>Nagłówek w karcie</h5>
</div>
src/app/nowa-04-3/demo.html — pełny plik
<div class="p-3">
  <h5>Nagłówek poza kartą</h5>
  <app-karta />
</div>

Plik demo.ts jest taki sam jak w przykładzie 2.

wynik w przeglądarce
Nagłówek poza kartą      (czarny)
┌──────────────────────┐
│ Nagłówek w karcie    │ (różowy)
└──────────────────────┘

Oba nagłówki to h5, ale różowy jest tylko ten w karcie. Reguła h5 { color: … } działa wyłącznie w szablonie komponentu, który ją zdefiniował. Styl dla całej aplikacji wpisujesz w src/styles.css.

Zatrzymaj się

To dlatego Bootstrapa podłączyliśmy w angular.json, a nie w komponencie. Przenieś regułę h5 { color: #d63384; } z dekoratora do src/styles.css. Ile nagłówków jest teraz różowych?

ELEMENTY WBUDOWANE

Zestawienie elementów

ElementZnaczenieUwagi
ng generate component nazwanowy komponent w folderze nazwaSkrót: ng g c nazwa. Tworzy 4 pliki.
import { Karta } from './karta/karta';skąd TypeScript ma wziąć klasęŚcieżka względna, bez .ts.
imports: [Karta]czego wolno użyć w szablonieBrak wpisu → NG8001.
<app-karta />wstawienie komponentuTo samo co <app-karta></app-karta>.
selector: 'app-karta'nazwa znacznikaMałe litery i myślnik.
styleUrl: './karta.css'plik stylów komponentuMożna pominąć, gdy brak stylów.
styles: `...`krótki styl wpisany w dekoratorzeZamiast osobnego pliku.
src/styles.cssstyle całej aplikacjiDziała we wszystkich komponentach.
NG8001nieznany elementBrak w imports albo literówka w znaczniku.
NG8113nieużywany importOstrzeżenie, aplikacja działa.
CZĘSTE BŁĘDY

Zanim utkniesz

Co widziszPrzyczyna i naprawa
NG8001: 'app-karta’ is not a known elementBrak klasy w tablicy imports dekoratora rodzica.
To samo NG8001 przy <app-Karta />Literówka w znaczniku — wielkość liter ma znaczenie. Selektor jest małymi literami.
TS2304: Cannot find name 'Karta’Wpisałeś Karta do imports, ale brak import { Karta } … na górze pliku.
Cannot find module ’./karta’Zła ścieżka w imporcie. Karta w podfolderze to './karta/karta', w tym samym folderze — './karta'.
CLI utworzyło pliki w złym miejscuTerminal stał w innym folderze. Uruchamiaj ng g c z głównego folderu projektu.
Komponent użyty w Demo, a dodany do imports w AppImportuje ten komponent, w którego szablonie znacznik naprawdę stoi.
Styl z komponentu nie działa w innymTak ma być. Wspólne style idą do src/styles.css.
ZADANIA

Zadania

ZAD 1Komponent stopki★☆☆

Wygeneruj komponent stopka z nazwą szkoły i rokiem szkolnym w szarym, wyśrodkowanym tekście. Wstaw go w app.html pod dotychczasową treścią.

Kryterium sukcesu: stopka jest widoczna na dole strony, terminal nie pokazuje błędów ani ostrzeżeń.

Podpowiedź

Trzy kroki: generowanie, import w app.ts (dwa miejsca), znacznik w app.html. Klasy: text-center text-muted.

ZAD 2Pasek u góry★☆☆

Dodaj komponent naglowek z nazwą klubu, ostylowany klasami Bootstrapa bg-dark text-white p-3. Umieść go nad całą resztą strony.

Kryterium sukcesu: ciemny pasek zajmuje całą szerokość i stoi nad pozostałą treścią.

Podpowiedź

Klasy Bootstrapa wpisujesz w szablonie komponentu (naglowek.html), nie w jego pliku .css.

ZAD 3Trzy różne karty — na razie po staremu★★☆

Potrzebujesz trzech kart z różnymi wydarzeniami: „Turniej retro”, „Noc robotów”, „Warsztat druku 3D”. Zrób to jedynym sposobem, który dziś znasz: trzy osobne komponenty karta-turniej, karta-roboty, karta-druk, ustawione w siatce z lekcji 03. Potem zmień w każdej karcie kolor ramki na border-success i policz, ile plików musiałeś edytować. Zapisz w komentarzu na końcu app.html, co by się stało przy 20 wydarzeniach.

Kryterium sukcesu: trzy różne karty w rzędzie, a w komentarzu liczba edytowanych plików i Twój wniosek.

Podpowiedź

Selektor karta-turniej po wygenerowaniu to app-karta-turniej, a klasa — KartaTurniej. Rozwiązanie tego problemu poznasz w lekcji 29.

ZAD 4Gdzie działa styl★★☆

W pliku stopka.css (z zadania 1) dopisz regułę dla p: kursywa i rozmiar czcionki 0.9rem. Sprawdź, czy zmieniły się akapity poza stopką. Następnie przenieś tę samą regułę do src/styles.css i znów sprawdź. Zapisz w zeszycie oba wyniki i jednozdaniową zasadę: kiedy styl wpisać do komponentu, a kiedy do styles.css.

Kryterium sukcesu: w pierwszej wersji kursywa jest tylko w stopce, w drugiej — we wszystkich akapitach strony.

Podpowiedź

p { font-style: italic; font-size: 0.9rem; }

ZAD 5Dwa poziomy zagnieżdżenia★★★

Zrób komponent lista-wydarzen, który w swoim szablonie zawiera siatkę z trzema komponentami karta. W app.html użyj tylko <app-lista-wydarzen />. Zapisz w komentarzu, w którym pliku musiała pojawić się klasa Karta w tablicy imports i dlaczego akurat tam, a nie w app.ts.

Kryterium sukcesu: ekran pokazuje trzy karty, a app.html zawiera jeden znacznik listy; terminal bez błędów i ostrzeżeń.

Podpowiedź

Importuje ten komponent, w którego szablonie znacznik naprawdę występuje. App importuje tylko ListaWydarzen.

ZAD 6Strona klubu z klocków★★★

Złóż całą stronę klubu z komponentów z zadań 1, 2 i 5: naglowek, lista-wydarzen (z kartami) i stopka. app.html ma zawierać tylko te trzy znaczniki, owinięte w jeden div. Narysuj w zeszycie drzewo komponentów: App na górze, pod nim jego dzieci, pod listą — karty. Przy każdym komponencie zapisz, co ma w tablicy imports.

Kryterium sukcesu: strona wygląda jak kompletna wizytówka klubu, terminal nie pokazuje ostrzeżeń, a drzewo w zeszycie zgadza się z tablicami imports w kodzie.

Podpowiedź

Każdy komponent w drzewie importuje tylko swoje bezpośrednie dzieci. Karta nie importuje niczego.

PODSUMOWANIE

Co trzeba zapamiętać

  • ng generate component nazwa (ng g c) tworzy folder z klasą, szablonem, stylami i testem.
  • Użycie komponentu to trzy kroki: wygeneruj, zaimportuj (import + tablica imports), wstaw znacznik.
  • import na górze pliku jest dla TypeScriptu, tablica imports — dla Angulara.
  • Brak wpisu w imports to błąd NG8001; nieużywany wpis — ostrzeżenie NG8113.
  • Importuje ten komponent, w którego szablonie znacznik stoi.
  • Ten sam komponent można wstawić wiele razy — na razie wszystkie wystąpienia wyglądają tak samo.
  • Styl komponentu działa tylko w jego szablonie; wspólne style idą do src/styles.css.
  • Przykład z kursu uruchamiasz, wstawiając jego Demo do App.

Sprawdź się: wstawiłeś <app-karta /> i terminal pokazuje NG8001. Wymień dwie rzeczy, które sprawdzisz w pierwszej kolejności.

Dokumentacja: angular.dev — komponenty.

Postęp zapisuje się w tej przeglądarce.