# Fortgeschrittene Angular Dependency Injection 2026: Provider, Tokens und Interview-Fragen > Angular bietet mit Providern, InjectionTokens und hierarchischen Injektoren ein mächtiges DI-System. Diese tiefgehende Analyse behandelt Provider-Strategien, die inject()-Funktion und praxisrelevante Interview-Fragen. - Published: 2026-09-13 - Updated: 2026-09-13 - Author: Anthony Fillion-Maillet - Tags: angular, dependency injection, providers, injection token, interview - Reading time: 5 min --- Das Dependency-Injection-System von Angular ermöglicht eine präzise Kontrolle über die Instanziierung von Services durch Provider, Tokens und hierarchische Injektoren. Die Beherrschung dieser Konzepte unterscheidet erfahrene Angular-Entwickler von Anfängern in technischen Interviews und Produktionscode. > **Kernkonzept** > > Angular verwaltet zwei parallele Injektor-Bäume: den ModuleInjector-Baum für Services auf Modulebene und den ElementInjector-Baum für komponentenbezogene Abhängigkeiten. Die Auflösung beginnt auf Elementebene und steigt zur Wurzel auf. ## Provider-Konfigurationsstrategien in Angular DI Angular bietet mehrere Provider-Konfigurationsstrategien, die jeweils für unterschiedliche Anwendungsfälle geeignet sind. Die gängigsten Konfigurationen verwenden `useClass`, `useValue`, `useFactory` und `useExisting`. ```typescript // app.config.ts import { ApplicationConfig, InjectionToken } from '@angular/core'; import { LoggerService } from './services/logger.service'; import { DebugLoggerService } from './services/debug-logger.service'; import { API_CONFIG, ApiConfig } from './config/api.config'; export const appConfig: ApplicationConfig = { providers: [ // useClass: eine andere Implementierung bereitstellen { provide: LoggerService, useClass: DebugLoggerService }, // useValue: ein statisches Konfigurationsobjekt bereitstellen { provide: API_CONFIG, useValue: { baseUrl: 'https://api.example.com', timeout: 5000 } }, // useFactory: Abhängigkeit mit Laufzeitlogik erstellen { provide: 'FEATURE_FLAGS', useFactory: () => { const env = import.meta.env.MODE; return { debugMode: env === 'development', analytics: env === 'production' }; } }, // useExisting: einen Alias zu einem anderen Provider erstellen { provide: 'Logger', useExisting: LoggerService } ] }; ``` Die `useClass`-Strategie tauscht Implementierungen aus, ohne den Consumer-Code zu ändern. Die `useValue`-Strategie stellt statische Objekte wie Konfigurationen bereit. Die `useFactory`-Strategie behandelt Laufzeitentscheidungen, und `useExisting` erstellt Aliase für polymorphen Zugriff. ## InjectionToken für typsichere Nicht-Klassen-Abhängigkeiten Während `@Injectable` für klassenbasierte Services funktioniert, erfordern Nicht-Klassen-Werte wie Konfigurationsobjekte, Primitive oder Funktionen ein `InjectionToken`. Dieses Token dient als eindeutiger Schlüssel in Angulars DI-Registry. ```typescript // tokens/config.tokens.ts import { InjectionToken } from '@angular/core'; export interface ApiConfig { baseUrl: string; timeout: number; retryAttempts: number; } // Generischer Parameter gewährleistet Typsicherheit am Injektionspunkt export const API_CONFIG = new InjectionToken('api.config', { providedIn: 'root', factory: () => ({ baseUrl: 'https://api.sharpskill.dev', timeout: 30000, retryAttempts: 3 }) }); // Token für primitive Werte export const MAX_UPLOAD_SIZE = new InjectionToken('max.upload.size', { providedIn: 'root', factory: () => 10 * 1024 * 1024 // 10MB }); ``` Der generische Typparameter bei `InjectionToken` wird an den `inject()`-Aufruf weitergegeben. TypeScript erkennt, dass der injizierte Wert dem deklarierten Typ entspricht, und fängt Fehlverwendungen zur Kompilierzeit statt zur Laufzeit ab. ## Die inject()-Funktion vs. Konstruktor-Injektion Angular 14 führte die `inject()`-Funktion als Alternative zur konstruktorbasierten Injektion ein. In Angular 20+ ist `inject()` zum bevorzugten Ansatz geworden, insbesondere in Standalone-Komponenten und funktionalen Kontexten. ```typescript // user.service.ts import { Injectable, inject } from '@angular/core'; import { HttpClient } from '@angular/common/http'; import { API_CONFIG } from '../tokens/config.tokens'; @Injectable({ providedIn: 'root' }) export class UserService { // Moderner Ansatz: inject() auf Feldebene private readonly http = inject(HttpClient); private readonly config = inject(API_CONFIG); getUser(id: string) { return this.http.get(`${this.config.baseUrl}/users/${id}`); } } // Alternative: Konstruktor-Injektion (weiterhin gültig) @Injectable({ providedIn: 'root' }) export class UserServiceLegacy { constructor( private readonly http: HttpClient, @Inject(API_CONFIG) private readonly config: ApiConfig ) {} } ``` Die `inject()`-Funktion eliminiert Decorator-Boilerplate für Tokens und ermöglicht Dependency Injection in Nicht-Klassen-Kontexten wie funktionalen Guards, Resolvern und Interceptoren. ```typescript // auth.guard.ts import { inject } from '@angular/core'; import { CanActivateFn, Router } from '@angular/router'; import { AuthService } from '../services/auth.service'; // Funktionaler Guard mit inject() export const authGuard: CanActivateFn = () => { const authService = inject(AuthService); const router = inject(Router); if (authService.isAuthenticated()) { return true; } return router.createUrlTree(['/login']); }; ``` Die `inject()`-Funktion funktioniert nur innerhalb eines Injektionskontexts: während der Klassenkonstruktion, in Factory-Funktionen oder in funktionalen Angular-Konstrukten. Ein Aufruf außerhalb dieser Kontexte löst einen Laufzeitfehler aus. ## Hierarchische Injektoren: ElementInjector vs. EnvironmentInjector Angular verwaltet zwei parallele Injektor-Hierarchien, die den Service-Scope und die Auflösungsreihenfolge bestimmen. Das Verständnis dieser Architektur ist entscheidend für die Kontrolle der Service-Lebensdauer und -Sichtbarkeit. ```typescript // data.service.ts import { Injectable } from '@angular/core'; // Root-Level-Singleton: einzelne Instanz für die gesamte App @Injectable({ providedIn: 'root' }) export class GlobalDataService { private data = new Map(); set(key: string, value: unknown) { this.data.set(key, value); } get(key: string) { return this.data.get(key); } } // Komponentenbezogen: neue Instanz pro Komponente @Injectable() export class ComponentDataService { private data = new Map(); set(key: string, value: unknown) { this.data.set(key, value); } get(key: string) { return this.data.get(key); } } ``` ```typescript // dashboard.component.ts import { Component } from '@angular/core'; import { ComponentDataService } from './services/component-data.service'; @Component({ selector: 'app-dashboard', standalone: true, // Diese Komponente und alle Kinder erhalten dieselbe Instanz providers: [ComponentDataService], template: ` ` }) export class DashboardComponent {} ``` Wenn eine Komponente einen Provider deklariert, erstellt Angular eine neue Instanz, die auf den ElementInjector dieser Komponente beschränkt ist. Kindkomponenten erben den Zugriff auf die Provider der Elternkomponente, es sei denn, sie deklarieren eigene. Der Auflösungsalgorithmus folgt diesem Pfad: 1. ElementInjector der anfordernden Komponente prüfen 2. ElementInjector-Baum zu den Vorfahren hinaufgehen 3. EnvironmentInjector prüfen (Modul- oder Standalone-Provider) 4. Zum Root-EnvironmentInjector aufsteigen 5. NullInjector erreichen und Fehler werfen, wenn `@Optional()` nicht verwendet wurde ## Auflösungsmodifikatoren: @Self, @SkipSelf, @Optional, @Host Auflösungsmodifikatoren ändern die Art, wie Angular die Injektor-Hierarchie durchsucht. Diese Decorators funktionieren sowohl mit Konstruktor-Injektion als auch mit der `inject()`-Funktion. ```typescript // panel.component.ts import { Component, Optional, SkipSelf, Self, inject } from '@angular/core'; import { PanelService } from './panel.service'; @Component({ selector: 'app-panel', standalone: true, providers: [PanelService], template: `` }) export class PanelComponent { // @Self: nur im Injektor dieser Komponente suchen, sonst fehlschlagen private readonly localService = inject(PanelService, { self: true }); // @SkipSelf: diese Komponente überspringen, Suche bei Eltern beginnen private readonly parentService = inject(PanelService, { skipSelf: true, optional: true }); // @Optional: null zurückgeben statt Fehler zu werfen, wenn nicht gefunden private readonly optionalService = inject(PanelService, { optional: true }); constructor() { // localService ist immer die eigene Instanz der Komponente // parentService ist die Instanz der Elternkomponente oder null console.log('Local:', this.localService); console.log('Parent:', this.parentService); } } ``` Der `@Host()`-Modifikator beschränkt die Auflösung auf den Injektor des Host-Elements und stoppt an Komponentengrenzen. Dies ist nützlich, wenn eine Direktive auf einen Service zugreifen muss, der von ihrer Host-Komponente bereitgestellt wird, aber nicht höher im Baum suchen soll. ```typescript // highlight.directive.ts import { Directive, inject, Host, Optional } from '@angular/core'; import { HighlightConfig } from './highlight.config'; @Directive({ selector: '[appHighlight]', standalone: true }) export class HighlightDirective { // Nur die Provider der Host-Komponente prüfen private readonly config = inject(HighlightConfig, { host: true, optional: true }); constructor() { // config ist null, wenn die Host-Komponente HighlightConfig nicht bereitstellt const color = this.config?.color ?? 'yellow'; // Highlighting anwenden... } } ``` ## Multi-Provider für erweiterbare Systeme Multi-Provider ermöglichen die Registrierung mehrerer Werte unter einem einzelnen Token. Angular gibt alle registrierten Werte als Array zurück, was Plugin-Architekturen und Erweiterungsmuster ermöglicht. ```typescript // validators.tokens.ts import { InjectionToken } from '@angular/core'; export interface Validator { validate(value: string): string | null; } export const VALIDATORS = new InjectionToken('validators'); ``` ```typescript // app.config.ts import { ApplicationConfig } from '@angular/core'; import { VALIDATORS } from './validators.tokens'; const requiredValidator = { validate: (value: string) => value ? null : 'Field is required' }; const minLengthValidator = { validate: (value: string) => value.length >= 3 ? null : 'Minimum 3 characters' }; const emailValidator = { validate: (value: string) => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value) ? null : 'Invalid email format' }; export const appConfig: ApplicationConfig = { providers: [ { provide: VALIDATORS, useValue: requiredValidator, multi: true }, { provide: VALIDATORS, useValue: minLengthValidator, multi: true }, { provide: VALIDATORS, useValue: emailValidator, multi: true } ] }; ``` ```typescript // validation.service.ts import { Injectable, inject } from '@angular/core'; import { VALIDATORS, Validator } from './validators.tokens'; @Injectable({ providedIn: 'root' }) export class ValidationService { private readonly validators = inject(VALIDATORS); validate(value: string): string[] { // validators ist ein Array aller registrierten Validatoren return this.validators .map(v => v.validate(value)) .filter((error): error is string => error !== null); } } ``` Das `multi: true`-Flag weist Angular an, alle Provider für dieses Token in einem Array zu sammeln. Ohne dieses Flag würden spätere Provider frühere überschreiben. ## Interview-Fragen zu Angular Dependency Injection Technische Interviews prüfen häufig das Verständnis von Angulars DI-System. Hier sind Fragen, die Kandidaten mit Produktionserfahrung von anderen unterscheiden. **F: Was passiert, wenn derselbe Service sowohl auf Modul- als auch auf Komponentenebene bereitgestellt wird?** Der Provider auf Komponentenebene erstellt eine separate Instanz, die auf den Teilbaum dieser Komponente beschränkt ist. Services, die in diesem Teilbaum injiziert werden, erhalten die Instanz der Komponente, nicht den Singleton auf Modulebene. Dies ermöglicht Zustandsisolierung, beispielsweise wenn jeder Tab seinen eigenen Formularzustand benötigt. **F: Warum sollte man `InjectionToken` statt eines String-Literals verwenden?** String-Tokens riskieren Kollisionen zwischen Bibliotheken oder verschiedenen Teilen einer Anwendung. `InjectionToken` erstellt eine eindeutige Laufzeitreferenz, die nicht kollidieren kann. Der generische Typparameter bietet auch Kompilierzeit-Typsicherheit, die String-Tokens fehlt. **F: Wann wirft `inject()` einen Fehler vs. gibt undefined zurück?** Standardmäßig wirft `inject()` einen Fehler, wenn die Abhängigkeit nicht gefunden wird. Die Übergabe von `{ optional: true }` ändert den Rückgabetyp zu `T | null` und gibt null statt eines Fehlers zurück. Dies entspricht dem Verhalten des `@Optional()`-Decorators. **F: Erklären Sie den Unterschied zwischen `providedIn: 'root'` und der Bereitstellung im providers-Array eines Moduls.** Beide erstellen Singletons, aber `providedIn: 'root'` ermöglicht Tree-Shaking. Der Service wird nur dann in das Bundle aufgenommen, wenn er tatsächlich irgendwo injiziert wird. Provider auf Modulebene werden unabhängig von der Verwendung immer einbezogen. **F: Wie beeinflusst Lazy Loading den Service-Scope?** Lazy geladene Module erhalten ihren eigenen Kind-EnvironmentInjector. Services, die in einem Lazy-Modul bereitgestellt werden, sind auf dieses Modul und seine Kinder beschränkt. Ein Service mit `providedIn: 'root'` bleibt ein echter Singleton über alle Module hinweg, ob lazy oder nicht. Für Übungsfragen zu Angular Services und DI-Mustern siehe [Angular Interview-Fragen zu Services und Dependency Injection](/technologies/angular/interview-questions/services-dependency-injection). ## Praktische Muster: Konfiguration und Feature-Flags Echte Anwendungen kombinieren diese DI-Konzepte für das Konfigurationsmanagement. Dieses Muster verwendet `InjectionToken`, Factory-Provider und Umgebungsbewusstsein. ```typescript // feature-flags.config.ts import { InjectionToken, inject, PLATFORM_ID } from '@angular/core'; import { isPlatformBrowser } from '@angular/common'; export interface FeatureFlags { newCheckout: boolean; darkMode: boolean; betaFeatures: boolean; } export const FEATURE_FLAGS = new InjectionToken('feature.flags', { providedIn: 'root', factory: () => { const platformId = inject(PLATFORM_ID); if (!isPlatformBrowser(platformId)) { // SSR: sichere Standardwerte zurückgeben return { newCheckout: false, darkMode: false, betaFeatures: false }; } // Browser: localStorage oder Remote-Config prüfen const stored = localStorage.getItem('featureFlags'); if (stored) { return JSON.parse(stored); } return { newCheckout: true, darkMode: false, betaFeatures: false }; } }); ``` ```typescript // feature-flag.directive.ts import { Directive, Input, TemplateRef, ViewContainerRef, inject } from '@angular/core'; import { FEATURE_FLAGS } from './feature-flags.config'; @Directive({ selector: '[appFeatureFlag]', standalone: true }) export class FeatureFlagDirective { private readonly flags = inject(FEATURE_FLAGS); private readonly templateRef = inject(TemplateRef); private readonly viewContainer = inject(ViewContainerRef); @Input() set appFeatureFlag(flag: keyof typeof this.flags) { if (this.flags[flag]) { this.viewContainer.createEmbeddedView(this.templateRef); } else { this.viewContainer.clear(); } } } ``` Diese Direktive rendert Inhalt basierend auf Feature-Flags bedingt, wobei die Flag-Konfiguration in einem injizierbaren Token zentralisiert ist. ## Produktionsreife Angular-DI-Praktiken - `providedIn: 'root'` für anwendungsweite Singletons verwenden, die vom Tree-Shaking profitieren - In Angular 20+ `inject()` der Konstruktor-Injektion vorziehen für sauberere Syntax und funktionale Kompatibilität - Zustandsbehaftete Services auf Komponenten beschränken, wenn Isolation benötigt wird, nicht auf Modulebene - `InjectionToken` für Nicht-Klassen-Abhängigkeiten erstellen, um Typsicherheit zu gewährleisten und Kollisionen zu vermeiden - `@Optional()` anwenden, wenn eine Abhängigkeit möglicherweise nicht existiert, besonders für Plugins oder optionale Features - Komponenten mit überschriebenen Providern mittels `TestBed.overrideComponent()` für Isolation testen - Multi-Provider für Erweiterungsmuster wie Validatoren, Interceptoren und Handler verwenden --- Source: SharpSkill (https://sharpskill.dev), tech interview preparation for your real stack. HTML version of this page: https://sharpskill.dev/de/blog/angular/angular-dependency-injection-advanced-providers-tokens-interview