Kurs Angular · Moduł 5: Serwisy i DI
Services w Angular - Gildie Rzemieślnicze
W tej lekcji3
Serwisy w Angular są jak gildie rzemieślnicze - wyspecjalizowane jednostki dostarczające usługi dla całego królestwa. Każda gildia ma swoją specjalizację: kowale wykuwają miecze, krawcy szyją kimona.
Czym jest Service?
Service to klasa odpowiedzialna za:
- Logikę biznesową - obliczenia, walidacje
- Komunikację z API - pobieranie/wysyłanie danych
- Zarządzanie stanem - przechowywanie danych między komponentami
- Współdzielenie kodu - unikanie duplikacji
Tworzenie Serwisu
import { Injectable } from '@angular/core';
@Injectable({
providedIn: 'root' // Singleton dostępny w całej aplikacji
})
export class KatanaService {
private katany: Katana[] = [];
getAll(): Katana[] {
return [...this.katany]; // Zwracamy kopię
}
getById(id: number): Katana | undefined {
return this.katany.find(k => k.id === id);
}
forge(name: string, type: string): Katana {
const katana: Katana = {
id: Date.now(),
name,
type,
damage: Math.floor(Math.random() * 100) + 50,
createdAt: new Date()
};
this.katany.push(katana);
return katana;
}
destroy(id: number): boolean {
const index = this.katany.findIndex(k => k.id === id);
if (index > -1) {
this.katany.splice(index, 1);
return true;
}
return false;
}
}Generowanie przez CLI
ng generate service services/katana
# lub krócej:
ng g s services/katanaKod do tej lekcji: src/app/app.component.ts
import { Component, inject } from '@angular/core';
import { SamuraiService } from './services/samurai.service';
import { LoggerService } from './services/logger.service';
@Component({
selector: 'app-root',
standalone: true,
template: `
<div class="guild-hall">
<header>
<h1>Gildia Rzemieślników</h1>
<p>Moduł 5: Services i Dependency Injection</p>
</header>
<main>
<section class="info-panel">
<h2>O Serwisach</h2>
<p>Serwisy to gildie rzemieślników - specjaliści od konkretnych zadań.</p>
<ul>
<li>@Injectable() - rejestracja w DI</li>
<li>providedIn: 'root' - singleton</li>
<li>inject() - nowoczesne wstrzykiwanie</li>
</ul>
</section>
<section class="demo-panel">
<h2>Lista Samurajów (z Serwisu)</h2>
<div class="samurai-list">
@for (s of samuraiList; track s.id) {
<div class="samurai-card">
<strong>{{ s.name }}</strong>
<span>Klan: {{ s.clan }}</span>
<span>Poziom: {{ s.level }}</span>
</div>
}
</div>
<div class="actions">
<button (click)="addSamurai()">Dodaj Samuraja</button>
<button (click)="showLogs()">Pokaż Logi</button>
</div>
</section>
<section class="logs-panel" *ngIf="logs.length">
<h3>Logi (z LoggerService)</h3>
<ul>
@for (log of logs; track $index) {
<li>{{ log }}</li>
}
</ul>
</section>
</main>
</div>
`,
styles: [`
.guild-hall {
font-family: 'Segoe UI', sans-serif;
max-width: 900px;
margin: 0 auto;
padding: 20px;
background: linear-gradient(135deg, #1a1a2e 0%, #16213e 100%);
min-height: 100vh;
color: #eee;
}
header {
text-align: center;
border-bottom: 2px solid #c9a227;
padding-bottom: 20px;
}
h1, h2, h3 { color: #c9a227; }
.info-panel, .demo-panel, .logs-panel {
background: rgba(255,255,255,0.05);
border: 1px solid #c9a227;
border-radius: 8px;
padding: 20px;
margin: 20px 0;
}
ul { list-style: none; padding: 0; }
li::before { content: "» "; }
.samurai-card {
background: #16213e;
border: 1px solid #333;
border-radius: 8px;
padding: 15px;
margin: 10px 0;
display: flex;
justify-content: space-between;
}
.actions {
display: flex;
gap: 10px;
margin-top: 15px;
}
button {
background: #c9a227;
color: #1a1a2e;
border: none;
padding: 10px 20px;
border-radius: 4px;
cursor: pointer;
}
button:hover { opacity: 0.9; }
.logs-panel ul {
background: #0d1117;
padding: 15px;
border-radius: 4px;
font-family: monospace;
font-size: 0.9rem;
}
.logs-panel li::before { content: "› "; color: #4ade80; }
`]
})
export class AppComponent {
private samuraiService = inject(SamuraiService);
private logger = inject(LoggerService);
samuraiList = this.samuraiService.getAll();
logs: string[] = [];
addSamurai() {
const newSamurai = {
id: Date.now(),
name: 'Nowy Wojownik',
clan: 'Ronin',
level: 1
};
this.samuraiService.add(newSamurai);
this.samuraiList = this.samuraiService.getAll();
this.logger.log('Dodano nowego samuraja: ' + newSamurai.name);
}
showLogs() {
this.logs = this.logger.getLogs();
}
}Hasła z dokumentacji w tej lekcji
Widzisz błąd w tej lekcji?
Sprawdź się
Odpowiedz na pytania z tej lekcji. Wybierz odpowiedź, a od razu zobaczysz, czy jest poprawna.
1. Jaki dekorator oznacza klasę jako serwis w Angular?
2. Za co według lekcji odpowiada serwis (Service) w Angular?
To 2 z 3 pytań do tej lekcji. Pozostałe rozwiążesz w grze.
Zadania praktyczne w grze
- Układanie w poziomie
Ułóż import dekoratora Injectable w jednej linii:
- Edytor kodu
Uzupełnij serwis SamuraiService tak, by Angular mógł go wstrzykiwać i tworzył jedną wspólną instancję dla całej aplikacji.
- Klikanie w kolejności
Zbuduj polecenie Angular CLI generujące serwis (klikaj po kolei):