Fortgeschritten222025-01-31

Angular Services & Dependency Injection - Logik auslagern

Lerne Services in Angular zu erstellen, Dependency Injection zu verstehen und zentrale Geschäftslogik effizient zu organisieren.

#angular#services#dependency-injection#di#providers#singleton

Angular Services & Dependency Injection

Zeit für wiederverwendbaren Code! Services sind das Herzstück von Angular-Anwendungen. 💉

📋 In diesem Artikel lernst du:

  • Was Services sind und warum du sie brauchst
  • Dependency Injection (DI) verstehen
  • Services erstellen mit ng generate service
  • Services in Komponenten injizieren
  • Singleton Pattern und providedIn
  • Service-Kommunikation zwischen Komponenten
  • Observables und RxJS Basics

Was sind Services?

Services sind Klassen, die Logik auslagern die nicht in Komponenten gehört.

Warum Services?

Problem ohne Services:

// user-profile.component.ts
export class UserProfileComponent {
  user: User;

  ngOnInit() {
    // ❌ HTTP-Call direkt in Komponente
    fetch('/api/user/123')
      .then(res => res.json())
      .then(data => this.user = data);
  }
}

// user-list.component.ts
export class UserListComponent {
  users: User[];

  ngOnInit() {
    // ❌ Doppelter Code! Same API Call!
    fetch('/api/users')
      .then(res => res.json())
      .then(data => this.users = data);
  }
}

Problem:

  • ❌ Code-Duplizierung
  • ❌ Schwer zu testen
  • ❌ Komponenten zu komplex
  • ❌ Keine Wiederverwendbarkeit

Lösung mit Service:

// user.service.ts
@Injectable({
  providedIn: 'root'
})
export class UserService {
  getUser(id: number): Observable<User> {
    return this.http.get<User>(`/api/user/${id}`);
  }

  getAllUsers(): Observable<User[]> {
    return this.http.get<User[]>('/api/users');
  }
}

// user-profile.component.ts
export class UserProfileComponent {
  user: User;

  constructor(private userService: UserService) {}  // ✅ Service injiziert!

  ngOnInit() {
    this.userService.getUser(123).subscribe(user => {
      this.user = user;
    });
  }
}

Vorteile:

  • ✅ Code an einem Ort
  • ✅ Leicht zu testen (Service mocken)
  • ✅ Komponenten bleiben simpel
  • ✅ Wiederverwendbar überall

Wann nutzt du Services?

Services für:

  • HTTP-Requests (API-Calls)
  • Business Logic (Berechnungen, Validierungen)
  • State Management (Daten zwischen Komponenten teilen)
  • Utility Functions (Date-Formatting, String-Helpers)
  • Browser APIs (LocalStorage, Cookies)
  • Logging & Analytics

NICHT für:

  • ❌ UI-Logik (gehört in Komponente)
  • ❌ Template-Rendering (gehört in Template)

Was ist Dependency Injection?

Dependency Injection (DI) ist ein Design Pattern, bei dem Abhängigkeiten von außen "injiziert" werden.

Ohne DI (Bad):

export class UserProfileComponent {
  private userService: UserService;

  constructor() {
    // ❌ Komponente erstellt Service selbst
    this.userService = new UserService();
  }
}

Problem:

  • ❌ Tight coupling (fest verdrahtet)
  • ❌ Schwer zu testen (kannst Service nicht mocken)
  • ❌ Keine Kontrolle über Service-Instanzen

Mit DI (Good):

export class UserProfileComponent {
  // ✅ Angular injiziert Service automatisch
  constructor(private userService: UserService) {}
}

Vorteile:

  • ✅ Loose coupling (flexibel)
  • ✅ Leicht zu testen (Service mocken)
  • ✅ Angular verwaltet Instanzen (Singleton Pattern)

Angular's DI-Container macht die Magie!

Deinen ersten Service erstellen

Mit Angular CLI:

ng generate service services/user

Oder kurz:

ng g s services/user

Angular erstellt:

CREATE src/app/services/user.service.ts (133 bytes)
CREATE src/app/services/user.service.spec.ts (347 bytes)

Service Anatomy:

src/app/services/user.service.ts:

import { Injectable } from '@angular/core';

@Injectable({
  providedIn: 'root'  // ⭐ Wichtig! Macht Service singleton
})
export class UserService {

  constructor() { }

  // Deine Methoden kommen hier hin
}

Erklärung:

  • @Injectable() - Decorator: Macht Klasse zu einem Service
  • providedIn: 'root' - Service ist singleton (eine Instanz für ganze App)
  • constructor() - Hier können andere Services injiziert werden

Service mit Logik

Beispiel: Logger Service

logger.service.ts:

import { Injectable } from '@angular/core';

@Injectable({
  providedIn: 'root'
})
export class LoggerService {

  log(message: string): void {
    console.log(`[LOG] ${new Date().toISOString()}: ${message}`);
  }

  error(message: string): void {
    console.error(`[ERROR] ${new Date().toISOString()}: ${message}`);
  }

  warn(message: string): void {
    console.warn(`[WARN] ${new Date().toISOString()}: ${message}`);
  }
}

Service nutzen:

app.component.ts:

import { Component, OnInit } from '@angular/core';
import { LoggerService } from './services/logger.service';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html'
})
export class AppComponent implements OnInit {

  // ✅ Service im Constructor injizieren
  constructor(private logger: LoggerService) {}

  ngOnInit() {
    this.logger.log('App wurde gestartet!');
  }

  onButtonClick() {
    this.logger.log('Button wurde geklickt');
  }
}

So einfach! Angular injiziert den Service automatisch. 🎉

Service mit Daten

Beispiel: User Service mit Mock-Daten

user.service.ts:

import { Injectable } from '@angular/core';

export interface User {
  id: number;
  name: string;
  email: string;
  role: 'admin' | 'user';
}

@Injectable({
  providedIn: 'root'
})
export class UserService {

  private users: User[] = [
    { id: 1, name: 'Max Mustermann', email: 'max@example.com', role: 'admin' },
    { id: 2, name: 'Anna Schmidt', email: 'anna@example.com', role: 'user' },
    { id: 3, name: 'Tom Weber', email: 'tom@example.com', role: 'user' }
  ];

  // Alle User holen
  getAllUsers(): User[] {
    return this.users;
  }

  // Einzelnen User finden
  getUserById(id: number): User | undefined {
    return this.users.find(user => user.id === id);
  }

  // User hinzufügen
  addUser(user: User): void {
    this.users.push(user);
  }

  // User löschen
  deleteUser(id: number): void {
    this.users = this.users.filter(user => user.id !== id);
  }

  // User updaten
  updateUser(id: number, updates: Partial<User>): void {
    const user = this.getUserById(id);
    if (user) {
      Object.assign(user, updates);
    }
  }
}

Service in Komponente nutzen:

user-list.component.ts:

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

@Component({
  selector: 'app-user-list',
  template: `
    <div class="user-list">
      <h2>User Liste</h2>

      <div *ngFor="let user of users" class="user-card">
        <h3>{{ user.name }}</h3>
        <p>{{ user.email }}</p>
        <span [class]="'badge ' + user.role">{{ user.role }}</span>
        <button (click)="deleteUser(user.id)">Löschen</button>
      </div>

      <button (click)="addNewUser()">Neuen User hinzufügen</button>
    </div>
  `,
  styles: [`
    .user-card {
      border: 1px solid #ddd;
      padding: 1rem;
      margin-bottom: 1rem;
      border-radius: 8px;
    }
    .badge {
      padding: 0.25rem 0.5rem;
      border-radius: 4px;
      font-size: 0.875rem;
    }
    .badge.admin { background: #ef4444; color: white; }
    .badge.user { background: #3b82f6; color: white; }
  `]
})
export class UserListComponent implements OnInit {
  users: User[] = [];

  constructor(private userService: UserService) {}

  ngOnInit() {
    // ✅ Daten vom Service holen
    this.users = this.userService.getAllUsers();
  }

  deleteUser(id: number) {
    this.userService.deleteUser(id);
    // Liste neu laden
    this.users = this.userService.getAllUsers();
  }

  addNewUser() {
    const newUser: User = {
      id: Date.now(),
      name: 'Neuer User',
      email: 'neu@example.com',
      role: 'user'
    };
    this.userService.addUser(newUser);
    this.users = this.userService.getAllUsers();
  }
}

✅ Jede Komponente die den UserService injiziert, arbeitet mit denselben Daten!

Dependency Injection im Detail

Provider Scopes

Es gibt 3 Scopes für Services:

1. Root Scope (Empfohlen)

@Injectable({
  providedIn: 'root'  // ✅ Singleton für ganze App
})
export class UserService { }

Bedeutet:

  • Eine Instanz für ganze App
  • Tree-shakable (unused Services werden aus Build entfernt)
  • Empfohlen für 99% der Fälle

2. Module Scope

// user.service.ts
@Injectable()  // Kein providedIn
export class UserService { }

// user.module.ts
@NgModule({
  providers: [UserService]  // Service hier registriert
})
export class UserModule { }

Bedeutet:

  • Eine Instanz pro Modul
  • Service nur in diesem Modul verfügbar

3. Component Scope

@Component({
  selector: 'app-user-profile',
  templateUrl: './user-profile.component.html',
  providers: [UserService]  // Service nur für diese Komponente
})
export class UserProfileComponent { }

Bedeutet:

  • Neue Instanz für jede Komponente
  • Service-Daten werden NICHT geteilt

Tipps & Tricks

Welchen Scope wann?

Root Scope (providedIn: 'root'):

  • Meistens die richtige Wahl!
  • ✅ Services die Daten zwischen Komponenten teilen
  • ✅ HTTP-Services, API-Services
  • ✅ State Management Services

Module Scope:

  • Feature-spezifische Services
  • Lazy-loaded Module Services
  • Selten gebraucht

Component Scope:

  • ⚠️ Selten nötig!
  • Nur wenn jede Komponente eigene Service-Instanz braucht
  • Z.B.: Form Validators mit State

Faustregel: Start with providedIn: 'root'!

Dependency Injection Best Practices

Constructor Injection:

// ✅ GOOD: Services im Constructor
constructor(
  private userService: UserService,
  private logger: LoggerService
) {}

Property Injection (NICHT empfohlen):

// ❌ BAD: Property Injection
@Inject(UserService) userService!: UserService;

Private vs Public:

// ✅ GOOD: Private (nicht im Template gebraucht)
constructor(private userService: UserService) {}

// ⚠️ OK: Public (wenn im Template genutzt)
constructor(public userService: UserService) {}

Multiple Services:

constructor(
  private userService: UserService,
  private authService: AuthService,
  private logger: LoggerService,
  private router: Router
) {}

Services für Kommunikation zwischen Komponenten

Services sind perfekt um Daten zwischen Komponenten zu teilen!

Beispiel: Counter Service

counter.service.ts:

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

@Injectable({
  providedIn: 'root'
})
export class CounterService {

  // ⭐ BehaviorSubject hält aktuellen Wert
  private countSubject = new BehaviorSubject<number>(0);

  // ⭐ Observable für Komponenten (read-only)
  count$: Observable<number> = this.countSubject.asObservable();

  getCurrentValue(): number {
    return this.countSubject.value;
  }

  increment(): void {
    this.countSubject.next(this.countSubject.value + 1);
  }

  decrement(): void {
    this.countSubject.next(this.countSubject.value - 1);
  }

  reset(): void {
    this.countSubject.next(0);
  }
}

Komponente A: Counter Controls

counter-controls.component.ts:

import { Component } from '@angular/core';
import { CounterService } from '../services/counter.service';

@Component({
  selector: 'app-counter-controls',
  template: `
    <div class="controls">
      <button (click)="increment()">+</button>
      <button (click)="decrement()">-</button>
      <button (click)="reset()">Reset</button>
    </div>
  `
})
export class CounterControlsComponent {

  constructor(private counterService: CounterService) {}

  increment() {
    this.counterService.increment();
  }

  decrement() {
    this.counterService.decrement();
  }

  reset() {
    this.counterService.reset();
  }
}

Komponente B: Counter Display

counter-display.component.ts:

import { Component } from '@angular/core';
import { CounterService } from '../services/counter.service';
import { Observable } from 'rxjs';

@Component({
  selector: 'app-counter-display',
  template: `
    <div class="display">
      <h2>Aktueller Zähler:</h2>
      <p class="counter">{{ count$ | async }}</p>
    </div>
  `
})
export class CounterDisplayComponent {
  count$: Observable<number>;

  constructor(private counterService: CounterService) {
    // ✅ Observable subscriben (mit async pipe)
    this.count$ = this.counterService.count$;
  }
}

Parent Component

app.component.html:

<div class="app">
  <h1>Counter App mit Services</h1>

  <!-- Beide Komponenten teilen sich den Counter-State! -->
  <app-counter-display></app-counter-display>
  <app-counter-controls></app-counter-controls>
</div>

✅ Wenn du in Controls auf + klickst, updated sich Display automatisch!

Warum? Beide nutzen denselben Service (Singleton)!

RxJS & Observables Basics

Angular nutzt RxJS (Reactive Extensions) für asynchrone Operationen.

Was ist ein Observable?

Ein Observable ist ein Stream von Daten über Zeit.

Analogie:

  • Promise: "Ich gebe dir EINEN Wert, irgendwann"
  • Observable: "Ich gebe dir VIELE Werte, über Zeit"

BehaviorSubject vs Subject

Subject:

private subject = new Subject<number>();

// Neue Subscriber bekommen NUR neue Werte

BehaviorSubject:

private subject = new BehaviorSubject<number>(0);  // Initial Value!

// Neue Subscriber bekommen sofort den aktuellen Wert
// + alle neuen Werte

Nutze BehaviorSubject für State!

Observable Pattern

Standard-Pattern für Services:

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

@Injectable({
  providedIn: 'root'
})
export class DataService {

  // 1. Private Subject (write)
  private dataSubject = new BehaviorSubject<Data[]>([]);

  // 2. Public Observable (read-only)
  data$: Observable<Data[]> = this.dataSubject.asObservable();

  // 3. Getter für aktuellen Wert
  getData(): Data[] {
    return this.dataSubject.value;
  }

  // 4. Setter für neuen Wert
  setData(data: Data[]): void {
    this.dataSubject.next(data);
  }

  // 5. Business Logic
  addItem(item: Data): void {
    const current = this.dataSubject.value;
    this.dataSubject.next([...current, item]);
  }
}

Subscribe vs Async Pipe

Subscribe (manuell):

export class MyComponent implements OnInit, OnDestroy {
  data: Data[] = [];
  private subscription!: Subscription;

  ngOnInit() {
    // ⚠️ Subscribe
    this.subscription = this.dataService.data$.subscribe(data => {
      this.data = data;
    });
  }

  ngOnDestroy() {
    // ⚠️ WICHTIG: Unsubscribe!
    this.subscription.unsubscribe();
  }
}

Async Pipe (automatisch):

export class MyComponent {
  // ✅ Kein Subscribe nötig!
  data$ = this.dataService.data$;
}
<!-- ✅ Async Pipe im Template -->
<div *ngFor="let item of data$ | async">
  {{ item.name }}
</div>

Async Pipe macht automatisch unsubscribe!

Häufige Fehler

Fehler 1: Service nicht injiziert

Problem:

export class MyComponent {
  users: User[];

  ngOnInit() {
    // ❌ userService existiert nicht!
    this.users = this.userService.getAllUsers();
  }
}

Lösung:

constructor(private userService: UserService) {}  // Injizieren!

Fehler 2: providedIn vergessen

Problem:

@Injectable()  // ❌ Kein providedIn!
export class UserService { }
Error: No provider for UserService!

Lösung:

@Injectable({
  providedIn: 'root'  // ✅ Root Scope
})
export class UserService { }

Fehler 3: Subscribe Leaks

Problem:

ngOnInit() {
  // ❌ Subscribe ohne Unsubscribe = Memory Leak!
  this.dataService.data$.subscribe(data => {
    this.data = data;
  });
}

Lösung 1: Async Pipe

data$ = this.dataService.data$;  // ✅ Kein Subscribe
<div *ngFor="let item of data$ | async">

Lösung 2: Unsubscribe

private subscription!: Subscription;

ngOnInit() {
  this.subscription = this.dataService.data$.subscribe(...);
}

ngOnDestroy() {
  this.subscription.unsubscribe();  // ✅ Cleanup
}

Fehler 4: Service im Component Scope

Problem:

@Component({
  selector: 'app-user-list',
  providers: [UserService]  // ❌ Jede Komponente neue Instanz!
})
export class UserListComponent { }

Resultat: Komponenten teilen sich KEINE Daten!

Lösung:

// Service mit providedIn: 'root'
@Injectable({
  providedIn: 'root'
})
export class UserService { }

// Component OHNE providers
@Component({
  selector: 'app-user-list',
  // providers: []  ← Weg damit!
})
🔍

Debugging & Troubleshooting

Service wird nicht injiziert

Problem: "No provider for MyService"

Check:

  1. Hat Service @Injectable() Decorator?
  2. Hat Service providedIn: 'root'?
  3. Ist Service im Constructor injiziert?
// ✅ Complete Service
@Injectable({
  providedIn: 'root'
})
export class MyService { }

// ✅ In Komponente
constructor(private myService: MyService) {}

Observable updated nicht

Problem: UI zeigt alte Daten

Check:

  1. Nutzt du .next() um Wert zu updaten?
  2. Nutzt du async pipe im Template?
  3. Hast du subscribed?
// ✅ Service
private subject = new BehaviorSubject<Data[]>([]);
data$ = this.subject.asObservable();

updateData(newData: Data[]) {
  this.subject.next(newData);  // ✅ next() aufrufen!
}

// ✅ Template
<div *ngFor="let item of data$ | async">

Memory Leaks finden

Chrome DevTools:

  1. Performance Tab → Memory
  2. Heap Snapshot nehmen
  3. Komponente mehrmals öffnen/schließen
  4. Neuen Snapshot nehmen
  5. Vergleichen → Sollte keine Komponenten im Memory bleiben!

Lösung:

ngOnDestroy() {
  this.subscription?.unsubscribe();  // ✅ Cleanup
}

Service-Methoden debuggen

@Injectable({
  providedIn: 'root'
})
export class UserService {

  getAllUsers(): User[] {
    console.log('getAllUsers() called');  // ✅ Debug
    const users = this.users;
    console.log('Returning users:', users);  // ✅ Debug
    return users;
  }
}

Oder nutze Breakpoints in VS Code/Chrome DevTools!

Praktisches Beispiel: Todo Service

Komplettes Beispiel mit CRUD-Operationen:

todo.service.ts:

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

export interface Todo {
  id: number;
  title: string;
  completed: boolean;
  createdAt: Date;
}

@Injectable({
  providedIn: 'root'
})
export class TodoService {

  private todosSubject = new BehaviorSubject<Todo[]>([
    { id: 1, title: 'Angular lernen', completed: false, createdAt: new Date() },
    { id: 2, title: 'Services verstehen', completed: true, createdAt: new Date() }
  ]);

  todos$: Observable<Todo[]> = this.todosSubject.asObservable();

  private nextId = 3;

  // Alle Todos holen
  getTodos(): Todo[] {
    return this.todosSubject.value;
  }

  // Einzelnes Todo finden
  getTodoById(id: number): Todo | undefined {
    return this.todosSubject.value.find(todo => todo.id === id);
  }

  // Todo hinzufügen
  addTodo(title: string): void {
    const newTodo: Todo = {
      id: this.nextId++,
      title,
      completed: false,
      createdAt: new Date()
    };

    const current = this.todosSubject.value;
    this.todosSubject.next([...current, newTodo]);
  }

  // Todo togglen
  toggleTodo(id: number): void {
    const current = this.todosSubject.value;
    const updated = current.map(todo =>
      todo.id === id ? { ...todo, completed: !todo.completed } : todo
    );
    this.todosSubject.next(updated);
  }

  // Todo löschen
  deleteTodo(id: number): void {
    const current = this.todosSubject.value;
    const filtered = current.filter(todo => todo.id !== id);
    this.todosSubject.next(filtered);
  }

  // Alle completed löschen
  clearCompleted(): void {
    const current = this.todosSubject.value;
    const filtered = current.filter(todo => !todo.completed);
    this.todosSubject.next(filtered);
  }

  // Stats
  getTodoStats(): { total: number; completed: number; active: number } {
    const todos = this.todosSubject.value;
    return {
      total: todos.length,
      completed: todos.filter(t => t.completed).length,
      active: todos.filter(t => !t.completed).length
    };
  }
}

todo-list.component.ts:

import { Component } from '@angular/core';
import { TodoService, Todo } from '../services/todo.service';
import { Observable } from 'rxjs';

@Component({
  selector: 'app-todo-list',
  template: `
    <div class="todo-app">
      <h1>Todo App mit Services</h1>

      <!-- Add Form -->
      <div class="add-form">
        <input
          type="text"
          [(ngModel)]="newTodoTitle"
          (keyup.enter)="addTodo()"
          placeholder="Neues Todo..."
        >
        <button (click)="addTodo()">Hinzufügen</button>
      </div>

      <!-- Stats -->
      <div class="stats">
        <p>Total: {{ stats.total }}</p>
        <p>Aktiv: {{ stats.active }}</p>
        <p>Erledigt: {{ stats.completed }}</p>
      </div>

      <!-- Todo List -->
      <div class="todo-list">
        <div
          *ngFor="let todo of todos$ | async"
          class="todo-item"
          [class.completed]="todo.completed"
        >
          <input
            type="checkbox"
            [checked]="todo.completed"
            (change)="toggleTodo(todo.id)"
          >
          <span class="title">{{ todo.title }}</span>
          <button (click)="deleteTodo(todo.id)" class="delete">
            Löschen
          </button>
        </div>
      </div>

      <!-- Actions -->
      <div class="actions">
        <button (click)="clearCompleted()">
          Erledigte löschen
        </button>
      </div>
    </div>
  `,
  styles: [`
    .todo-app {
      max-width: 600px;
      margin: 2rem auto;
      padding: 2rem;
    }
    .add-form {
      display: flex;
      gap: 0.5rem;
      margin-bottom: 1rem;
    }
    .add-form input {
      flex: 1;
      padding: 0.5rem;
      border: 1px solid #ddd;
      border-radius: 4px;
    }
    .stats {
      display: flex;
      gap: 1rem;
      margin-bottom: 1rem;
      padding: 1rem;
      background: #f3f4f6;
      border-radius: 8px;
    }
    .todo-item {
      display: flex;
      align-items: center;
      gap: 0.5rem;
      padding: 1rem;
      border: 1px solid #ddd;
      border-radius: 4px;
      margin-bottom: 0.5rem;
    }
    .todo-item.completed .title {
      text-decoration: line-through;
      opacity: 0.6;
    }
    .title {
      flex: 1;
    }
    .delete {
      background: #ef4444;
      color: white;
      border: none;
      padding: 0.25rem 0.75rem;
      border-radius: 4px;
      cursor: pointer;
    }
    .actions {
      margin-top: 1rem;
    }
  `]
})
export class TodoListComponent {
  todos$: Observable<Todo[]>;
  newTodoTitle = '';

  constructor(private todoService: TodoService) {
    this.todos$ = this.todoService.todos$;
  }

  get stats() {
    return this.todoService.getTodoStats();
  }

  addTodo() {
    if (this.newTodoTitle.trim()) {
      this.todoService.addTodo(this.newTodoTitle.trim());
      this.newTodoTitle = '';
    }
  }

  toggleTodo(id: number) {
    this.todoService.toggleTodo(id);
  }

  deleteTodo(id: number) {
    this.todoService.deleteTodo(id);
  }

  clearCompleted() {
    this.todoService.clearCompleted();
  }
}

Nicht vergessen FormsModule zu importieren!

app.module.ts:

import { FormsModule } from '@angular/forms';

@NgModule({
  imports: [
    BrowserModule,
    FormsModule  // ✅ Für ngModel
  ],
  // ...
})
🎯

Zusammenfassung

Du hast gelernt:

  • Services lagern Logik aus Komponenten aus
  • Dependency Injection injiziert Services automatisch
  • @Injectable({ providedIn: 'root' }) macht Services singleton
  • ✅ Services mit ng generate service erstellen
  • ✅ Services im constructor() injizieren
  • BehaviorSubject für reaktiven State
  • Observable mit async pipe nutzen
  • ✅ Services zur Kommunikation zwischen Komponenten nutzen
  • ✅ Immer unsubscribe oder async pipe nutzen

Key Takeaways:

  • Services = Wiederverwendbare Logik
  • DI = Loose Coupling + Testability
  • providedIn: 'root' = Singleton (eine Instanz)
  • BehaviorSubject + Observable = Reactive State Management
  • Async Pipe = Automatisches Subscribe/Unsubscribe

Service Pattern:

@Injectable({ providedIn: 'root' })
export class DataService {
  private dataSubject = new BehaviorSubject<Data[]>([]);
  data$ = this.dataSubject.asObservable();

  getData() { return this.dataSubject.value; }
  setData(data: Data[]) { this.dataSubject.next(data); }
}

Nutze Services für alles außer UI! 🎯

Quiz

📝 Quiz

Was macht der @Injectable() Decorator?

📝 Quiz

Was bedeutet providedIn: 'root'?

📝 Quiz

Was ist der Vorteil von Dependency Injection?

Übungen

Übung 1: Calculator Service

Erstelle einen CalculatorService mit Methoden:

  • add(a, b)
  • subtract(a, b)
  • multiply(a, b)
  • divide(a, b)
  • getHistory() - Array der letzten 10 Berechnungen

Nutze den Service in einer Komponente mit einem Calculator-UI.

Übung 2: Theme Service

Erstelle einen ThemeService der:

  • Dark/Light Mode speichert
  • Theme ändern kann (toggleTheme())
  • Theme als Observable bereitstellt (theme$)
  • Theme in localStorage speichert

Komponente soll Theme ändern können und aktuelles Theme anzeigen.

Übung 3: Shopping Cart Service

Erstelle einen CartService mit:

  • addItem(product) - Produkt hinzufügen
  • removeItem(productId) - Produkt entfernen
  • updateQuantity(productId, quantity) - Menge ändern
  • getTotal() - Gesamtpreis berechnen
  • clearCart() - Warenkorb leeren
  • items$ - Observable mit allen Items

Erstelle 2 Komponenten:

  1. Product List (Produkte mit "Add to Cart" Button)
  2. Shopping Cart (Zeigt alle Items im Warenkorb)

Nächste Schritte

Im nächsten Artikel:

  • Routing & Navigation
  • Router Setup
  • Routes definieren
  • Navigieren zwischen Seiten
  • Route Parameters
  • Guards & Resolver

➡️ Weiter zu: Angular Routing & Navigation

Services verstanden! Zeit für Multi-Page Apps! 🚀

AngularLektion 6 von 10
60% abgeschlossen
Lektion abgeschlossen!

Gut gemacht! 🎉

Du hast "Angular Services & Dependency Injection - Logik auslagern" abgeschlossen

Artikel bewerten

0.0 (0 Bewertungen)

Bitte einloggen um zu bewerten