Kurs Angular · Moduł 5: Serwisy i DI

Services w Angular - Gildie Rzemieślnicze

1 min czytania ·
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/katana
Kod 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. 1. Jaki dekorator oznacza klasę jako serwis w Angular?

  2. 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):

Przydatne artykuły