Angular Services verstehen

Warum braucht man überhaupt Services?

Stell dir vor, du baust eine App mit mehreren Komponenten - eine Benutzerliste, ein Profil-Dialog und eine Navigation. Alle brauchen Zugriff auf Benutzerdaten. Ohne Services würdest du denselben Code dreimal schreiben. Das ist nicht nur lästig, sondern auch ein Wartungsalptraum.
Services lösen genau dieses Problem. Sie sind zentrale Anlaufstellen für bestimmte Aufgaben - wie ein Spezialist im Team, den alle fragen können.

Die Grundidee

Der Clou: Es gibt nur eine einzige Instanz des UserService in der gesamten App. Wenn Komponente A den aktuellen Benutzer ändert, sehen Komponente B und C das sofort.

Einen eigenen Service bauen

Ein Service ist im Kern einfach eine TypeScript-Klasse mit einem besonderen Decorator:

import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { BehaviorSubject, Observable } from 'rxjs';

@Injectable({
  providedIn: 'root'
})
export class UserService {
  private apiUrl = '/api/users';
  
  // BehaviorSubject speichert den aktuellen Wert
  // und informiert alle Abonnenten bei Änderungen
  private currentUser$ = new BehaviorSubject<User | null>(null);

  constructor(private http: HttpClient) {}

  getUsers(): Observable<User[]> {
    return this.http.get<User[]>(this.apiUrl);
  }

  login(user: User): void {
    this.currentUser$.next(user);
  }

  logout(): void {
    this.currentUser$.next(null);
  }

  getCurrentUser(): Observable<User | null> {
    return this.currentUser$.asObservable();
  }
}

Was bedeutet providedIn: 'root'? Damit sagst du Angular: "Erstelle genau eine Instanz dieses Services und mach sie überall in der App verfügbar." Das ist in 90% der Fälle genau das, was du willst.

Den Service nutzen

Hier passiert die "Magie" der Dependency Injection. Du fragst Angular einfach im Constructor nach dem Service:

import { Component, OnInit } from '@angular/core';
import { UserService } from './user.service';

@Component({
  selector: 'app-user-list',
  template: `
    <ul>
      <li *ngFor="let user of users">{{ user.name }}</li>
    </ul>
  `
})
export class UserListComponent implements OnInit {
  users: User[] = [];

  constructor(private userService: UserService) {}

  ngOnInit(): void {
    this.userService.getUsers().subscribe(users => {
      this.users = users;
    });
  }
}

Du erstellst den Service nie selbst mit new UserService(). Angular kümmert sich darum und gibt dir immer die gleiche Instanz.

Was, wenn ich verschiedene Instanzen brauche?

Manchmal willst du nicht die gleiche Instanz überall. Ein Beispiel: Ein Formular-Service, der den Zustand eines spezifischen Formulars verwaltet.

Für komponentenspezifische Instanzen:

@Component({
  selector: 'app-order-form',
  providers: [FormStateService], // ← Eigene Instanz!
  template: `...`
})
export class OrderFormComponent {
  constructor(private formState: FormStateService) {}
}

Praktische Patterns

Der Facaden-Service

Wenn du mehrere Services kombinieren musst, wird der Code in Komponenten schnell unübersichtlich. Eine Fassade versteckt die Komplexität:

@Injectable({ providedIn: 'root' })
export class CheckoutFacade {
  constructor(
    private cart: CartService,
    private payment: PaymentService,
    private shipping: ShippingService
  ) {}

  // Komponente ruft nur diese eine Methode auf
  async completeOrder(): Promise<OrderResult> {
    const items = this.cart.getItems();
    const shippingCost = await this.shipping.calculate(items);
    const result = await this.payment.charge(items, shippingCost);
    
    if (result.success) {
      this.cart.clear();
    }
    
    return result;
  }
}

Die Komponente muss sich nicht um die Details kümmern - ein Methodenaufruf, fertig.

State mit BehaviorSubject


Für reaktiven State, der sich über die App verteilt:

@Injectable({ providedIn: 'root' })
export class ThemeService {
  private isDarkMode$ = new BehaviorSubject<boolean>(false);

  toggleTheme(): void {
    this.isDarkMode$.next(!this.isDarkMode$.value);
  }

  get darkMode$(): Observable<boolean> {
    return this.isDarkMode$.asObservable();
  }
}

Jede Komponente kann das Theme beobachten und reagiert automatisch auf Änderungen.