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.