Każdy początkujący programista Angulara zgodzi się, że komponenty to jedna z „pierwszych rzeczy”, których trzeba się nauczyć przy budowaniu swojej pierwszej aplikacji. Od Angulara 2 komponenty stały się podstawowymi blokami konstrukcyjnymi aplikacji Angularowych.
Komponenty składają się z wielu narzędzi, które obsługują zarówno warstwę wizualną aplikacji, jak i logikę, która za nią odpowiada. Dlatego tak ważne jest, aby wiedzieć, czym są komponenty, do czego służą, jak z nimi pracować i jak je wykorzystywać, aby lepiej dostosowywać aplikację.
Czym jest komponent?
Komponenty w Angularze są podstawowymi blokami konstrukcyjnymi, które kontrolują poszczególne części widoku, obejmujące templatki, style i logikę. Dzięki komponentom możesz łatwo strukturyzować interfejs użytkownika, ponieważ każdy z nich odpowiada za inny fragment całości widoku.
Wyróżnia się komponenty UI (interfejsowe) i funkcjonalne, które kontrolują odpowiednio aspekty wizualne i funkcjonalne programu. Tworząc komponenty, możesz zoptymalizować niektóre z nich pod kątem prezentacji, bez logiki, a inne – do obsługi logiki, zarządzania danymi przekazywanymi do komponentów UI. Są one również nazywane odpowiednio dumb i smart komponentami.
Wyobraź sobie dużą skrzynkę, w której chcesz uporządkować zakupione produkty spożywcze. Masz różne rodzaje owoców, warzyw i przekąsek, które chcesz pogrupować (np. owoce w jednym miejscu, a każdy ich rodzaj razem). Zamiast wypełniać jedną dużą skrzynkę według planu w głowie, łatwiej jest użyć mniejszych pudełek do pogrupowania produktów na zewnątrz, a następnie włożyć je do dużej skrzynki. Na przykład: małe pudełko na wszystkie jabłka, kolejne na gruszki, banany, kiwi itd., większe pudełko, w którym umieszczasz wszystkie mniejsze pudełka z owocami, tworząc jedną dużą skrzynkę z owocami, a następnie wkładasz ją do skrzynki z zakupami wraz z pudełkami na warzywa i przekąski uporządkowanymi w ten sam sposób.
Ten przykład ilustruje, jak możesz organizować komponenty w aplikacji Angularowej, gdzie każde pudełko reprezentuje komponent, a sposób umieszczania elementów w pudełku to szablon (template). Jeśli chodzi o rolę komponentów, każde mniejsze pudełko to komponent UI, a duża skrzynka z zakupami pełni funkcję komponentu funkcjonalnego (feature), ponieważ gdy chcesz przenieść jakieś elementy, wyjmujesz je z własnego pudełka (emitujesz zdarzenie output) do innego pudełka.
W typowej aplikacji, jeśli umieścisz swoje zadeklarowane komponenty bez deklaracji, wyglądałoby to jak zagnieżdżone szablony – co technicznie rzecz biorąc, jest prawdą. Jednak komponenty robią znacznie więcej niż zastępowanie kodu HTML. Choć znacząco redukują liczbę linii kodu, zarządzają całą funkcjonalnością poszczególnych części aplikacji.
Struktura komponentu
Każdy komponent tworzony za pomocą Angular CLI zawiera pliki odpowiadające za szablon, style, logikę oraz opcjonalnie testy jednostkowe.
- Szablony (Templates) to pliki HTML, które definiują układ widoku, zawierający elementy DOM oraz komponenty UI.
- Style są definiowane w pliku CSS, SCSS, Sass lub Less, w zależności od wybranego podczas tworzenia projektu formatu.
- Logika komponentu jest definiowana w kodzie TypeScript i obejmuje właściwości, metody oraz lifecycle hooki, które kontrolują zachowanie komponentu i jego interakcje.
- Testy (opcjonalnie) to również pliki TypeScript, zawierające testy jednostkowe logiki komponentu.
Do Angulara 20 pliki komponentów były nazywane zgodnie z nazwą komponentu, po której następowała końcówka component oraz rozszerzenie pliku odpowiadające danej części komponentu. Na przykład, dla głównego komponentu o nazwie App, który domyślnie znajduje się w plikach projektu, pliki komponentu nosiły następujące nazwy:
app.component.html – templatka
app.component.css – style
app.component.ts – logika
app.component.spec.ts – testy (opcjonalnie)
W Angularze 20, jeśli używasz Angular CLI do generowania plików, czasami może ono pominąć końcówkę .component oraz inne rozszerzenia. W takim przypadku pliki komponentu Angular mogą mieć następujące nazwy:
app.html – templatka
app.css – style
app.ts – logika
app.spec.ts – testy (opcjonalnie)
Chociaż nadal można korzystać ze starego schematu nazewnictwa, warto o tym wiedzieć, aby się nie pogubić.
Tworzenie komponentu
Komponent można utworzyć na dwa sposoby: za pomocą Angular CLI lub ręcznie. Angular CLI zajmuje się wieloma kwestiami, w tym tworzeniem komponentów. Polecenie do utworzenia komponentu wygląda następująco:
ng generate component ComponentName
lub w skrócie:
ng g c ComponentName
To polecenie utworzy folder wewnątrz katalogu app/, a w nim następujące pliki:
- component-name.component.css
- component-name.component.html
- component-name.component.spec.ts
- component-name.component.ts
Angular CLI stosuje praktykę zwaną Separation of Concerns (SoC), dzięki czemu tworzy osobno szablon i arkusz stylów wraz z plikiem komponentu. Ta praktyka sprzyja uporządkowanemu kodowi i poprawia czytelność, ponieważ zapobiega tworzeniu się bardzo długich plików. W przypadku małych komponentów czasami można zobaczyć cały komponent zdefiniowany w jednym pliku .ts. Jest to całkowicie możliwe, a więcej na ten temat omówię później w artykule.
Teraz omówię, jak można modyfikować polecenia Angular CLI w zależności od różnych ustawień.
Jeśli chcesz pominąć pliki testów jednostkowych, dodaj flagę –skip-tests na końcu polecenia:
ng generate component ComponentName --skip-tests
To polecenie utworzy komponent w taki sam sposób jak powyżej, ale bez pliku .spec.ts.
Jeśli chcesz utworzyć pliki komponentu w bieżącym katalogu, dodaj flagę –flat:
ng generate component ComponentName --flat
To polecenie utworzy komponent w tym samym katalogu, w którym się znajdujesz, i nie utworzy osobnego folderu na pliki komponentu.
Jeśli chcesz zobaczyć podgląd tego, co polecenie utworzyłoby, dodaj flagę –dry-run, lub w skrócie -d:
ng generate component ComponentName --dry-run
To polecenie pokaże Ci pliki, które zostałyby utworzone, bez faktycznego ich tworzenia.
Jeśli chcesz wygenerować komponent z osadzonymi (inline) stylami i/lub szablonem:
ng generate component ComponentName --inline-styles --inline-template
To polecenie utworzy komponent ze stylami i szablonem w pliku .ts, pomijając pliki .html i .css. Możesz również użyć skrótów -s i -t odpowiednio dla stylów i szablonu.
Inne flagi obejmują metadane, moduły oraz flagi związane z kontrolą:
| Flag | Alias | Description | Example |
| –change-detection | -c | Ustawia strategię wykrywania zmian na wartość przekazaną w parametrze, OnPush lub Default. | ng g c ComponentName –change-detection=OnPush |
| –standalone | Ustawia właściwość standalone na true lub false. | ng g c ComponentName –standalone=true | |
| –selector | Nadpisuje domyślną nazwę selektora (zwykle w formie app-component). | ng g c ComponentName –selector=app-comp | |
| –view-encapsulation | Ustawia strategię enkapsulacji widoku. Może być Emulated (domyślnie), None lub ShadowDom. | ng g c ComponentName –view-encapsulation=None | |
| –prefix | -p | Ustawia prefiks dla selektora komponentu, nadpisując domyślny prefiks w konfiguracji angular.json (zwykle app). | ng g c ComponentName –prefix=proj |
| –module | -m | Określa moduł, w którym ma zostać zadeklarowany nowy komponent. | ng g c ComponentName –module=app.module |
| –export | Dodaje komponent do tablicy exports wskazanego modułu, udostępniając go innym komponentom. | ng g c ComponentName –module=app.module –export | |
| –skip-import | Pomija dodawanie komponentu do tablicy imports lub declarations w module. | ng g c ComponentName –skip-import | |
| –skip-selector | Generuje komponent bez selektora. Nie wszystkie komponenty wymagają selektora, w zależności od złożoności aplikacji 🙂 | ng g c ComponentName –skip-selector | |
| –style | Określa rozszerzenie pliku dla arkuszy stylów. Normalnie CLI pyta o to podczas tworzenia. | ng g c ComponentName –style=scss | |
| –type | Dodaje sufiks do wygenerowanych nazw plików. Domyślny sufiks to component. W przykładzie użyto ui, więc plik będzie nazwany component-name.ui.ts. | ng g c ComponentName –type=ui |
Podstawowy komponent
Podstawowy komponent w Angularze to komponent bez stanu, bez rozszerzeń, usług ani zaawansowanych funkcji.
Przykład podstawowego komponentu:
// profile-photo.ts
import { Component } from '@angular/core';
@Component({
selector: 'app-profile-photo',
template: `<img src="profile-photo.jpg" alt="Your profile photo">`,
styles: `img { border-radius: 50%; }`,
changeDetection: ChangeDetectionStrategy.OnPush
})
export class ProfilePhoto { }
// profile-photo.ts
import { Component } from '@angular/core';
@Component({
selector: 'app-profile-photo',
templateUrl: './profile-photo.html',
styleUrl: './profile-photo.css',
changeDetection: ChangeDetectionStrategy.OnPush
})
export class ProfilePhoto { }
// profile-photo.html
<img src="profile-photo.jpg" alt="Your profile photo">
// profile-photo.css
img { border-radius: 50%; }
Źródło przykładu: https://angular.dev/guide/components
Uwaga: Wszystkie przykłady pokazane w tym artykule pochodzą z najnowszej wersji Angulara.
Bazując na tych przykładach, przejdziemy od podstaw do składni pliku komponentu.
Metadane
Każdy plik komponentu jest generowany z dekoratorem @Component, importowanym z @angular/core. Wewnątrz tego dekoratora znajduje się obiekt, który nazywamy metadanymi komponentu.
Domyślnie metadane zawierają przede wszystkim właściwości selector, templateUrl i styleUrls, gdy komponent jest generowany za pomocą Angular CLI.
Jeśli zdecydujesz się nie używać zewnętrznych plików szablonu ani arkuszy stylów, możesz zdefiniować osadzony (inline) szablon i style, używając właściwości template: i styles: odpowiednio. W takim przypadku pliki szablonu i stylów nie są potrzebne, ale plik TypeScript jest wymagany.
Metadane komponentu mogą zawierać również bardziej zaawansowane właściwości, które nie są od razu konieczne, ale w dalszej części wyjaśnię te najczęściej używane.
Selector
Celem selektora w komponencie jest zdefiniowanie konkretnego tagu, który będzie używany w szablonach, stylach itp. Selektor musi być unikalny dla komponentu, aby Angular nie pomylił ich ze sobą.
Selektor może być użyty jako selektor CSS albo jako element wewnątrz innego komponentu, na przykład:
// profile-photo.ts
@Component({
selector: 'app-profile-photo',
template: `<img src="profile-photo.jpg" alt="Your profile photo">`,
styles: `img { border-radius: 50%; }`,
changeDetection: ChangeDetectionStrategy.OnPush
})
export class ProfilePhoto { }
// user-profile.ts
@Component({
template: `
<profile-photo />
<button>Upload a new profile photo</button>`,
...,
imports: [ProfilePhoto],
changeDetection: ChangeDetectionStrategy.OnPush
})
export class UserProfile { }
W takim układzie Angular renderuje zawartość komponentu ProfilePhoto wewnątrz szablonu komponentu UserProfile.
Rodzaje selektorów to: element, atrybut i klasa. Selektor, który jest najczęściej używany w komponentach, to tzw. selektor elementu. Przykład użycia selektora elementu został pokazany powyżej.
Przejdźmy teraz do selektorów atrybutów. Są one szczególnie przydatne, gdy komponent zawiera dziecko swojego elementu nadrzędnego w DOM, ponieważ niektóre elementy DOM nie mogą mieć dodatkowych węzłów pomiędzy sobą. Dzięki temu selektory atrybutów mogą przekształcić niezbędny element w komponent, bez naruszania hierarchii.
Na przykład, rozważmy spis treści:

<h1>Database of state library</h1>
<table class="table table-striped">
<thead>
<tr>
<th scope="col">#</th>
<th scope="col">Title</th>
<th scope="col">Author</th>
</tr>
</thead>
<tbody>
@for(book of books(); track book.id){
<tr>
<th scope="row">{{ book.id }}</th>
<td>{{ book.title }}</td>
<td>{{ book.author }}</td>
</tr>
}
</tbody>
</table>
Zróbmy zatem komponent z wierszy, które wyświetlają zawartość:
// table-row-content.component.ts
@Component({
selector: 'app-table-row-content',
imports: [],
templateUrl: './table-row-content.html',
styleUrl: './table-row-content.css',
changeDetection: ChangeDetectionStrategy.OnPush
})
export class TableRowContent {
readonly book = input.required<Book>();
}
// table-row-content.component.html
<tr>
<th scope="row">{{ book().id }}</th>
<td>{{ book().title }}</td>
<td>{{ book().author }}</td>
</tr>
// app.component.html
<h1>Database of state library</h1>
<table class="table table-striped">
<thead>
<tr>
<th scope="col">#</th>
<th scope="col">Title</th>
<th scope="col">Author</th>
</tr>
</thead>
<tbody>
@for(book of books(); track book.id){
<app-table-row-content [book]="book"/>
}
</tbody>
</table>
Przyjrzyjmy się tabeli:

Co jest nie tak? Tabela HTML to jeden z elementów DOM, który musi posiadać określone elementy podrzędne. Dlatego zastąpienie tagu <tr> komponentem nie zadziała.
Zamiast tego możesz użyć selektora atrybutu:
// table-row-content.component.ts
@Component({
selector: '[app-table-row-content]',
imports: [],
templateUrl: './table-row-content.html',
styleUrl: './table-row-content.css',
changeDetection: ChangeDetectionStrategy.OnPush
})
export class TableRowContent {
readonly book = input.required<Book>();
}
Teraz przenieś <tr> z komponentu potomnego z powrotem do <tbody> w komponencie nadrzędnym i umieść selektor w następujący sposób:
// app.component.html
<h1>Database of state library</h1>
<table class="table table-striped">
<thead>
<tr>
<th scope="col">#</th>
<th scope="col">Title</th>
<th scope="col">Author</th>
</tr>
</thead>
<tbody>
@for(book of books(); track book.id){
<tr app-table-row-content [book]="book"></tr>
}
</tbody>
</table>

[Wejdź w Nowy Rok z wiedzą o rewolucyjnych Signal Forms, skalowalnej architekturze i nowoczesnej reaktywności. A to wszystko podczas 2 dniowych warsztatów. Data warsztatów: 02-03.03.2026]
Selektory klas są podobne, ale ich głównym celem jest dodawanie zachowań do wystylizowanych elementów. Podobnie jak selektory atrybutów, są idealne dla wielokrotnego użytku komponentów, które składają się z pojedynczego elementu DOM, np. przycisk, tabela itd.
Spójrzmy na powyższe rozwiązanie, ale z użyciem selektorów klas:
// table-row-content.component.ts
@Component({
selector: '.app-table-row-content',
imports: [],
templateUrl: './table-row-content.html',
styleUrl: './table-row-content.css',
changeDetection: ChangeDetectionStrategy.OnPush
})
export class TableRowContent {
readonly book = input.required<Book>();
}
// app.component.html
<h1>Database of state library</h1>
<table class="table table-striped">
<thead>
<tr>
<th scope="col">#</th>
<th scope="col">Title</th>
<th scope="col">Author</th>
</tr>
</thead>
<tbody>
@for(book of books(); track book.id){
<tr class="app-table-row-content" [book]="book"></tr>
}
</tbody>
</table>
Tutaj deklarujemy selektor komponentu jako selektor klasy CSS i przypisujemy go do atrybutu class elementu. W powyższym programie demonstracyjnym zastosowano oba podejścia, z których jedno jest zakomentowane.
Importy
W każdym komponencie dostępna jest tablica imports, w której możesz importować inne komponenty, dyrektywy, pipe’y itp. i używać ich wewnątrz swojego komponentu. Oto przykład:
import { Component } from '@angular/core';
import { CounterComponent } from './counter/counter.component';
import { MessagesComponent } from './messages/messages.component';
@Component({
selector: 'app-root',
standalone: true,
templateUrl: './app.component.html',
styleUrl: './app.component.css',
imports: [CounterComponent, MessagesComponent],
})
export class AppComponent {/* ... */}
Standalone
Określenie, czy komponent jest standalone, odbywa się poprzez ustawienie właściwości standalone:, która przyjmuje wartość true lub false.
Od Angulara 14 komponenty mogą być definiowane jako standalone, a od Angulara 19 komponenty są domyślnie standalone. Dzięki temu komponenty można bezpośrednio importować za pomocą tablicy imports w innych komponentach standalone.
W starszych wersjach konieczne było dodanie standalone: true wewnątrz dekoratora. Komponenty mogą również mieć właściwość standalone ustawioną od razu na false, co oznacza, że trzeba importować moduł NgModule.
Providers
Komponenty Angulara posiadają tablicę providers, która pozwala dostarczać zależności na poziomie komponentu. Przekazanie klasy jako tokena providera informuje injector, aby utworzył nową instancję klasy danego typu.
import { Injectable } from '@angular/core';
@Injectable()
export class LoggerService {
id = Math.random();
log(message: string) {
console.log(`[${this.id}] ${message}`);
}
}
import { Component } from '@angular/core';
import { LoggerService } from './logger.service';
@Component({
selector: 'child-a',
template: `<p>Child A works!</p>`,
providers: [LoggerService]
})
export class ChildAComponent {
// private _logger = inject(LoggerService);
constructor(private _logger: LoggerService) {
this._logger.log('Child A created');
}
}
Tutaj komponent ChildAComponent dodaje LoggerService do swojej tablicy providers, aby utworzyć nową instancję klasy LoggerService w drzewie injectorów komponentu. W ten sposób będzie ona dostępna dla komponentu do wstrzyknięcia wewnątrz samego siebie.
Uwaga: W nowszych aplikacjach zaleca się używanie funkcji inject() do tworzenia instancji zależności. Jest to alternatywa dla klasycznego wstrzykiwania zależności (DI), więcej możesz dowiedzieć się tutaj.
Warto też pamiętać, że czasami możesz zobaczyć serwisy tworzone przez Angular CLI z właściwością providedIn: 'root’ wewnątrz dekoratora @Injectable. Automatycznie tworzy to instancję klasy w root injectorze, co oznacza, że dostarczanie tego serwisu na poziomie komponentu nie będzie konieczne.
Serwisy to nie jedyne rzeczy, które można dostarczać. Typy providerów obejmują: class providers, value providers, factory providers, aliases oraz injection tokens.
Klasy można również dostarczać za pomocą useClass:
providers: [{ provide: LoggerService, useClass: LoggerService}]
Jest to pełna wersja przykładowego kodu, który dostarcza LoggerService do komponentu.
Value providers oraz injection tokens mogą być używane do wstrzykiwania zależności niebędących klasami. Najczęstsze zastosowania injection tokens to definiowanie tokenów do wstrzykiwania dla adresów URL API lub endpointów, obiektów konfiguracyjnych oraz wartości specyficznych dla środowiska.
import { InjectionToken, Inject, Component } from '@angular/core';
export const API_URL = new InjectionToken<string>('API URL');
@Component({
selector: 'app-root',
template: `<p>Check the console</p>`,
providers: [
{ provide: API_URL, useValue: 'https://api.example.com' } // token is API_URL
]
})
export class AppComponent {
constructor(@Inject(API_URL) private apiUrl: string) {
console.log('API URL:', this.apiUrl);
}
}
W tym przykładzie API_URL jest unikalnym tokenem, a useValue łączy go z wartością. Kiedy jest wstrzykiwany, API_URL posiada tę wartość.
Factory providers używają klucza useFactory, aby poinformować Angular, że ma uruchomić funkcję, która tworzy obiekt zależności.
providers: [
{
provide: LoggerService,
useFactory: () => new LoggerService()
}
]
Alias providers używają useExisting, aby mapować jeden token na inny. W rezultacie powstaje dodatkowy sposób dostępu do tego tokena. Na przykład:
providers: [
BetterLoggerService,
{ provide: LoggerService, useExisting: BetterLoggerService }
]
Injector wstrzykuje BetterLoggerService jako singleton i mapuje go na LoggerService, który pełni rolę aliasu dla BetterLoggerService. Innymi słowy, za każdym razem, gdy zostanie poproszony o LoggerService, tworzona jest instancja BetterLoggerService.
viewProviders
Jest to konceptualnie podobne do providers, ale główna różnica polega na tym, że providerzy skonfigurowani w viewProviders są dostępni tylko dla view children, a nie dla content children projektowanych przy użyciu ng-content. Natomiast providers dają dostęp wszystkim dzieciom.
Dzięki temu viewProviders jest idealny do tworzenia prywatnej instancji serwisu o ograniczonym dostępie (dzieci treści nie mogą jej używać) lub gdy chcemy zapobiec konfliktom wstrzykiwania z dzieckiem treści, które wstrzykuje ten sam serwis.
Encapsulation
Podczas tworzenia arkuszy stylów dla komponentów Angular nie umieszcza ich w globalnym stylu. Zamiast tego korzysta z mechanizmu scope’owania stylów, aby nie zakłócały one innych części aplikacji. Mechanizm ten jest ustawiany przez właściwość encapsulation.
Istnieją trzy ustawienia enkapsulacji widoku:
- Emulated (domyślne)
- ShadowDom
- None
ViewEncapsulation.Emulated
W tym trybie Angular przepisuje selektory CSS, aby były ograniczone do komponentu, i dodaje unikalne atrybuty do klas DOM. Dzięki temu CSS styluje wyłącznie dedykowany komponent. Jednak globalne style zdefiniowane poza komponentem mogą nadal wpływać na elementy wewnątrz komponentu z enkapsulacją emulowaną.
Na przykład, wyobraź sobie, że zdefiniowałeś komponent ze stylami w następujący sposób:
@Component({
selector: 'app-hello',
template: `<p>Hello</p>`,
styles: [`p { color: red; }`],
encapsulation: ViewEncapsulation.Emulated
})
Po wyrenderowaniu komponentu i sprawdzeniu narzędzi deweloperskich, możesz zobaczyć, że CSS i DOM są wygenerowane w następujący sposób:
<p _ngcontent-abc="">Hello</p>
p[_ngcontent-abc] { color: red; }
ViewEncapsulation.ShadowDom
W tym trybie Angular tworzy shadow tree, który zawiera wyłącznie elementy dedykowanego komponentu. Nie jest to wbudowane bezpośrednio w Angular, dlatego należy użyć odpowiedniego API, aby to obsłużyć.
Zaletą tej metody jest to, że nawet globalne style nie mogą zakłócać elementów dedykowanego komponentu.
Po wyrenderowaniu komponentu Twój komponent i wygenerowany DOM mogą wyglądać następująco:
import { Component, ViewEncapsulation } from '@angular/core';
@Component({
selector: 'app-profile-card',
template: `
<div class="card">
<h2>John Doe</h2>
<p>Frontend Developer</p>
</div>
`,
styles: [`
.card {
border: 2px solid blue;
padding: 1rem;
background: lightyellow;
font-family: Arial, sans-serif;
}
h2 {
color: darkblue;
}
`],
encapsulation: ViewEncapsulation.ShadowDom
})
export class ProfileCardComponent {}
<app-profile-card>
#shadow-root
<style>
.card {
border: 2px solid blue;
padding: 1rem;
background: lightyellow;
font-family: Arial, sans-serif;
}
h2 {
color: darkblue;
}
</style>
<div class="card">
<h2>John Doe</h2>
<p>Frontend Developer</p>
</div>
</app-profile-card>
W tym trybie nie ma enkapsulacji, a wszystkie zarejestrowane style są traktowane jako style globalne.
@Component({
encapsulation: ViewEncapsulation.None
})
Lifecycle hooki komponentu
Komponenty w Angularze mają również cykl życia! Ich cykl życia rozpoczyna się od fazy tworzenia, przez wykrywanie zmian, renderowanie, aż do zniszczenia komponentu.
Angular udostępnia specyficzne hooki, w których można uruchamiać kod w różnych fazach cyklu życia. Dla początkujących najważniejsze w tym rozdziale są dwa hooki: ngOnInit i ngOnDestroy (oprócz constructor()).
Omówimy te dwa hooki, ponieważ pozwalają one na wykonywanie kodu w najprostszych projektach Angulara.
ngOnInit()
Ta metoda wykonywana jest dokładnie raz w cyklu życia komponentu i dzieje się to po zainicjalizowaniu wszystkich inputów komponentu, czyli po wywołaniu konstruktora (constructor()).
Spójrzmy na podstawowy przykład aplikacji wyświetlającej powitanie:
// greeting.component.ts
import { Component, input, OnInit } from '@angular/core';
@Component({
selector: 'app-greeting',
template: `
<h2>{{ greetingMessage }}</h2>
`
})
export class GreetingComponent implements OnInit {
readonly name = input.required<string>();
greetingMessage = '';
constructor() {}
ngOnInit(): void {
this.greetingMessage = `Hello and welcome, ${this.name()}! 👋`;
}
}
// app.component.ts
import { Component } from '@angular/core';
import { GreetingComponent } from './greeting.component';
@Component({
selector: 'app-root',
template: `
<h1>My App</h1>
<app-greeting [name]="'Alice'"></app-greeting>
`,
imports: [GreetingComponent],
})
export class AppComponent {}
Konstruktor w GreetingComponent jest pusty, ponieważ po pierwsze, nie ma tam nic do zdefiniowania, a po drugie, input name nie jest jeszcze dostępny na etapie konstruktora. Możesz przetestować ten kod tutaj.
ngOnDestroy()
Ta metoda wykonywana jest dokładnie raz tuż przed zniszczeniem komponentu, czyli po jego usunięciu z widoku. Jej główną funkcją jest sprzątanie po komponentach, w szczególności zwalnianie zasobów, które nie zostaną automatycznie oczyszczone.Dodajmy teraz ngOnDestroy() do aplikacji powitalnej:
// greeting.component.ts
import { Component, input, OnInit, OnDestroy } from '@angular/core';
@Component({
selector: 'app-greeting',
template: `
<h2>{{ greetingMessage }}</h2>
`
})
export class GreetingComponent implements OnInit, OnDestroy {
name = input.required<string>();
greetingMessage = '';
ngOnInit(): void {
console.log(`Fetched ${this.name()}`);
this.greetingMessage = `Hello and welcome, ${this.name()}! 👋`;
}
ngOnDestroy(): void {
console.log(`Greeting for ${this.name()} destroyed. Goodbye! 🛑`);
}
}
// app.component.ts
import { Component } from '@angular/core';
import { GreetingComponent } from './greeting.component';
@Component({
selector: 'app-root',
standalone: true,
template: `
<h1>My App</h1>
<button (click)="showGreeting = !showGreeting">
Toggle Greeting
</button>
@if (showGreeting) {
<app-greeting [name]="'Alice'"></app-greeting>
}
`,
imports: [GreetingComponent]
})
export class AppComponent {
showGreeting = true;
}
Do poprzedniego przykładu dodano przycisk przełączania (toggle button), aby zobaczyć działanie ngOnDestroy, który uruchomi się po usunięciu komponentu z widoku aplikacji za pomocą przycisku.
Sprawdź ten przykład tutaj. Hooki cyklu życia najlepiej obserwować w konsoli narzędzi deweloperskich (naciśnij F12).
Odwiedź tę stronę, aby poznać więcej hooków cyklu życia oraz uzyskać dodatkowe informacje.
Podsumowanie
Komponenty są podstawowymi elementami projektów w Angularze. Najczęściej będziesz korzystać z Angular CLI do generowania komponentów, ale ważne jest, aby znać składnię plików komponentów i wiedzieć, co robi każda ich część.
Komponenty są definiowane przez dekorator, który zawiera metadane, ze wstępnie ustawionymi właściwościami, które można dostosować lub pominąć, jeśli nie są potrzebne.
Dzięki komponentom standalone wprowadzonym w Angularze 14, każdy komponent może importować inne komponenty standalone, niezależnie od ich położenia w folderze projektu (warto jednak utrzymywać dobrą organizację plików).
Nauczyłeś się również o cyklu życia komponentu i o tym, że można uruchamiać pewną logikę w różnych jego fazach.
Powodzenia w kodowaniu!