In Angular stellt der "any"-Provider-Scope eine neue Möglichkeit dar, Abhängigkeiten als Singleton bereitzustellen, jedoch nicht auf globaler Ebene wie beim providedIn: 'root', sondern auf Ebene der jeweiligen Modul-Injektoren. Das bedeutet konkret: Jeder Angular-Modul-Injektor, der eine lazy-loaded Feature-Module-Struktur einführt, erhält eine eigene Instanz der Abhängigkeit, die mit providedIn: 'any' deklariert wurde. Gleichzeitig teilen sich das Root-Modul und alle statisch geladenen Module, die es importiert, eine einzige gemeinsame Instanz dieser Abhängigkeit.

Diese Eigenschaft unterscheidet den "any"-Scope maßgeblich von anderen verbreiteten Provider-Strategien wie providedIn: 'root', der klassischen forRoot-forChild-Methode oder dedizierten Service-Modulen. Insbesondere ist er tree-shakable – das heißt, nicht verwendete Abhängigkeiten werden vom finalen Bundle ausgeschlossen, da es keine zwingenden statischen Referenzen zwischen Modulen und Services gibt. Dies sorgt für optimierte Ladezeiten und kleinere Bundle-Größen, ein entscheidender Vorteil bei größeren Angular-Anwendungen mit komplexer Modulstruktur.

Ein typisches Einsatzszenario für den "any"-Provider-Scope ist der Umgang mit zustandsbehafteten Abhängigkeiten, deren Konfiguration sich zwischen verschiedenen lazy-loaded Modulen unterscheiden kann. Gerade bei Anwendungsteilen, die unterschiedliche Backend-Konfigurationen benötigen – etwa verschiedene Endpunkte für Banking- und Aktienmodule – lässt sich über diesen Mechanismus eine saubere Trennung realisieren, ohne die Wiederverwendbarkeit und Modularität der Anwendung zu gefährden.

Ein praktisches Beispiel veranschaulicht die Architektur: Das Hauptmodul AppModule stellt eine Standardkonfiguration für einen BackendService bereit – bestehend aus Basis-URL, Anzahl der Wiederholungsversuche bei Fehlern und Intervall zwischen den Versuchen. Diese Konfiguration wird über einen InjectionToken verfügbar gemacht und gemeinsam mit dem Service selbst im Root-Modul bereitgestellt. Module, die keine eigene Konfiguration anbieten – wie etwa das Bankkonto-Modul – übernehmen automatisch diese Standardinstanz.

Anders verhält es sich bei Modulen wie dem Aktien-Modul. Dieses bietet eine eigene Backend-Konfiguration, etwa mit verkürztem Retry-Intervall und abweichender API-URL, passend zum höheren Änderungsintervall der Aktieninformationen. Dank des "any"-Scopes wird für dieses Modul eine neue Instanz des BackendService erzeugt, die automatisch auf die lokal konfigurierte Backend-Konfiguration zurückgreift.

Technisch gesehen wird dies durch das Deklarieren der Klasse mit @Injectable({ providedIn: 'any' }) realisiert. Dadurch entscheidet Angular zur Laufzeit, welchen Modul-Injektor es für die Instanziierung verwenden soll. Innerhalb des SharesModuls wird der BackendService somit unabhängig von der Instanz aus dem AppModule erzeugt. Diese Modularisierung erlaubt es nicht nur, Funktionalität zu kapseln, sondern auch spezifische Konfigurationen, etwa für Logging, Analytics oder Feature-Toggles, auf Modulebene zu isolieren.

Ein zusätzlicher Vorteil dieser Methode: Die BackendService-Instanz ist lokal, nicht global. Damit wird ein ungewolltes Überschreiben von Konfigurationen in unterschiedlichen Teilen der Anwendung verhindert, was besonders bei Anwendungen mit hohem Grad an dynamischer Modularität oder bei Multimandantenarchitekturen von Bedeutung ist.

Wichtig ist zu verstehen, dass der "any"-Scope nicht als generischer Ersatz für providedIn: 'root' gedacht ist. Seine Stärke liegt in der granularen Steuerung von Zuständen und Konfigurationen auf Feature-Modul-Ebene. Gerade in Kombination mit lazy loading entsteht so eine Architektur, die sowohl effizient als auch flexibel ist – ohne dabei den Code mit forRoot-/forChild-Mustern unnötig zu verkomplizieren.

Entscheidend für das Verständnis ist, dass tree-

Wie man benutzerdefinierte CSS-Eigenschaften für dynamische Themen in Angular-Anwendungen verwendet

Die Einführung von benutzerdefinierten CSS-Eigenschaften hat den Umgang mit dynamischen Designs in Webanwendungen erheblich vereinfacht. Angular, das populäre Front-End-Framework, bietet viele Möglichkeiten zur Erstellung anpassbarer Benutzeroberflächen. Durch die Integration von benutzerdefinierten CSS-Eigenschaften in Angular-Anwendungen können Entwickler jetzt flexiblere und interaktive Themen erstellen, die den Nutzern eine nahtlose Anpassung der Darstellung ermöglichen. Dieser Abschnitt befasst sich mit der praktischen Implementierung von benutzerdefinierten CSS-Eigenschaften in einer Angular-Anwendung und bietet wertvolle Einblicke in deren Anwendung.

Die Implementierung von benutzerdefinierten CSS-Eigenschaften für dynamische Themen in Angular beginnt mit der Einrichtung eines grundlegenden Beispiels. In der Angular Academy-Anwendung, die im Beispiel verwendet wird, haben die Benutzer die Möglichkeit, das Erscheinungsbild der Benutzeroberfläche durch das Auswählen von Farben für verschiedene Bereiche wie den Hintergrund des Headers oder die Kachelhintergründe anzupassen. Dies wird durch die Verwendung von benutzerdefinierten CSS-Eigenschaften wie --headerbackground und --tilebackground erreicht.

Um mit der Anwendung zu beginnen, müssen die Entwickler die Quelle des Demo-Projekts von GitHub herunterladen. Der Code wird dann in das Verzeichnis projects/demo auf dem lokalen Entwicklungsrechner geklont. Durch die Installation der benötigten Abhängigkeiten und das Starten des Entwicklungsservers über ng serve demo können Entwickler die Anwendung in ihrem lokalen Browser unter http://localhost:4200 ansehen.

Ein zentrales Element der Anwendung ist der Theme Picker, der es Nutzern ermöglicht, das Erscheinungsbild der Anwendung interaktiv zu ändern. Dieser Theme Picker verwendet benutzerdefinierte CSS-Eigenschaften, die es ermöglichen, die Farbwahl für den Header-Hintergrund, den Hintergrund der Kacheln und den allgemeinen Hintergrund direkt aus der Benutzeroberfläche heraus zu ändern. Die gewählten Farben werden dann in der Anwendung angewendet, ohne dass der Entwickler die zugrunde liegenden CSS-Dateien manuell anpassen muss.

Die Konfiguration der benutzerdefinierten CSS-Eigenschaften erfolgt durch die Verwendung von @HostBinding, einer Direktive, die es ermöglicht, CSS-Eigenschaften an die Komponenten des Angular-Frameworks zu binden. Auf diese Weise können Entwickler Werte für diese Eigenschaften dynamisch festlegen und sie direkt auf die Anwendung anwenden. In einem Beispiel für die AppComponent werden verschiedene Eigenschaften wie background, headerBackground und tileBackground über den ThemeService abgerufen, der die Werte für die CSS-Eigenschaften speichert und bei Bedarf aktualisiert.

Die ThemeService-Klasse ist eine einfache Implementierung, die es ermöglicht, die benutzerdefinierten CSS-Eigenschaften zu speichern und später abzurufen. In diesem Fall wird localStorage verwendet, um die Benutzereinstellungen für die Themen zu speichern. So wird beispielsweise der Wert für headerBackground aus dem lokalen Speicher abgerufen, wenn der Benutzer die Anwendung das nächste Mal öffnet. Falls noch keine Einstellung vorhanden ist, wird ein Standardwert wie #00aa00 verwendet.

Ein weiterer wichtiger Aspekt der Verwendung von benutzerdefinierten CSS-Eigenschaften in Angular ist die Möglichkeit, diese in verschiedenen Teilen der Anwendung zu verwenden. Über den ThemeService können Entwickler auch entscheiden, ob sie eine andere Methode zur Speicherung der Themenpräferenzen verwenden möchten, beispielsweise ein externes System zur Verwaltung von Design-Tokens, das in größeren Unternehmen zum Einsatz kommt.

Neben der Anpassung von Farben bietet Angular auch die Möglichkeit, Layouts dynamisch zu verändern. Die Verwendung von CSS Grids ermöglicht es, das Layout der Anwendung flexibel an die Bildschirmgröße und Benutzerpräferenzen anzupassen. In Verbindung mit benutzerdefinierten CSS-Eigenschaften können Entwickler das Layout nach Bedarf dynamisch ändern, um beispielsweise den Platz für Text zu vergrößern und den Platz für Videos zu verringern. Dies ist besonders nützlich, wenn Inhalte auf verschiedenen Geräten, einschließlich Mobiltelefonen, optimal angezeigt werden sollen.

Die Integration von benutzerdefinierten CSS-Eigenschaften in Angular ermöglicht es Entwicklern, benutzerdefinierte Designs zu erstellen, die auf die Wünsche der Nutzer zugeschnitten sind, ohne dass der Code unübersichtlich wird. Der Vorteil dieser Methode liegt in der Flexibilität, die sie bietet: Änderungen an den CSS-Eigenschaften können direkt im JavaScript-Code vorgenommen werden, und das Layout der Anwendung kann dynamisch angepasst werden, ohne dass die gesamte Anwendung neu geladen werden muss.

Diese Technik hat auch den Vorteil, dass die Benutzererfahrung optimiert wird, da Benutzer in der Lage sind, die Anwendung nach ihren eigenen Vorlieben zu gestalten. Ein zentraler Aspekt ist hierbei die Speicherung der Einstellungen zwischen den Sitzungen, sodass die Benutzeroberfläche auch nach einem erneuten Laden der Seite in der bevorzugten Konfiguration erscheint. Dies trägt dazu bei, eine konsistente und benutzerfreundliche Anwendung zu schaffen.

Die Implementierung eines solchen Systems erfordert jedoch einige Überlegungen, wie die Verwaltung von Benutzeranpassungen, insbesondere in Bezug auf die Speicherung von Einstellungen und das Laden von Daten. Es ist wichtig, dass Entwickler bei der Verwendung von localStorage oder anderen Speichermethoden sicherstellen, dass die gespeicherten Daten zuverlässig und sicher sind. Darüber hinaus sollten sie berücksichtigen, dass die Performance der Anwendung nicht durch zu viele Änderungen an den CSS-Eigenschaften beeinträchtigt wird, insbesondere wenn diese Änderungen in Echtzeit vorgenommen werden.

Wie man die regionale Unterstützung mit verbesserten Globalisierungs-APIs optimiert

Multilinguale Anwendungen nutzen die Globalisierung, um Benutzern aus verschiedenen Ländern und Kulturen ein regionales Erlebnis zu bieten. Angular stellt eingebaute APIs zur Verwaltung sowohl der Internationalisierung als auch der Lokalisierung bereit. In diesem Abschnitt werden wir Konfigurations- und Implementierungsbeispiele durchgehen, um einige der neuen Globalisierungsfunktionen zu erläutern, die Ivy mit sich bringt.

Angular verwendet Lokalisierungsdaten für regionale Variationen bei der Formatierung von Daten, Währungen, Dezimalzahlen und Prozentsätzen. In Angular Ivy können wir eine einzelne Locale als Teil des Builds bündeln, indem wir die Option localize im Build-Prozess nutzen. Wenn wir beispielsweise möchten, dass unsere Anwendung das französische Locale verwendet, können wir dies folgendermaßen konfigurieren:

json
{ "projects": { "my-app": { "projectType": "application", "i18n": { "locales": { "fr": "src/locales/translations.fr.xlf" } }, "architect": { "build": { "builder": "@angular-devkit/build-angular:browser", "options": { "localize": ["fr"] } } } } } }

Die localize-Option identifiziert, dass das französische Locale ("fr") zusammen mit der Anwendung gebündelt und geladen wird. Die locales.fr-Option gibt den Pfad zur französischen Übersetzungsdatei an. Wenn wir auf Ivy migrieren, müssen wir zudem ng add @angular/localize ausführen, um die eingebauten Globalisierungs-APIs von Angular zu aktivieren. Wenn wir diese nicht verwenden möchten, können wir dieses Paket weglassen, um die Bundle-Größe nicht unnötig zu vergrößern. Eine weitere Option besteht darin, für jedes Locale eine eigene Build-Konfiguration zu erstellen, aber dies lassen wir hier als Übung.

Die Geschwindigkeit des Lokalisierungs-Builds hat sich mit Ivy erheblich verbessert. Dies betrifft insbesondere die Möglichkeit, Locale-Daten lazy zu laden.

Vor Angular Ivy mussten wir alle Daten für die unterstützten Locales laden und registrieren, mit Ausnahme des "en-US"-Locales, das immer eingebunden war. Dies geschah durch die Verwendung der registerLocaleData-Funktion. Wenn wir jedoch eine Anwendung bauen, die ein gebündeltes Locale und Übersetzungen enthält, bleibt die Verwendung von registerLocaleData weiterhin notwendig. Möchten wir jedoch die eingebauten Internationalisierungs-APIs von Angular nicht nutzen, so haben wir die Möglichkeit, Locale-Daten lazy zu laden. Dies passt gut zu dynamischen Übersetzungsbibliotheken wie ngx-translate und Transloco.

Um Locale-Daten lazy zu laden, können wir das französische Locale-Datenpaket dynamisch importieren, wie im folgenden Beispiel zu sehen:

typescript
import { Injectable } from '@angular/core';
import { EMPTY, from, Observable } from 'rxjs'; import { catchError, mapTo } from 'rxjs/operators'; import { LanguageTag } from '../../shared/ui/language-tag'; @Injectable({ providedIn: 'root', }) export class LocaleLoader { load(locale: LanguageTag): Observable<void> { return from(import(`@angular/common/locales/global/${locale}`)).pipe( catchError(() => { console.error(`Fehler beim Laden des Locales "${locale}"`); return EMPTY; }), mapTo(undefined) ); } }

Der LanguageTag-Typ repräsentiert einen Textstring im BCP47-Format. Der LocaleLoader-Service verwendet die dynamische Importfunktion, um Locale-Daten lazy zu laden und etwaige Fehler an die Konsole des Browsers zu melden. Das Ganze wird in einem Observable gekapselt.

Die Template für den Locale-Picker könnte folgendermaßen aussehen:

html
<label for="locale">Wählen Sie ein Locale</label>
<input id="locale" type="text" [formControl]="selectedLocale" />

Dieser Picker nutzt das native <input>-Element, um Autovervollständigung in seinem Textfeld zu ermöglichen. Das selectedLocale FormControl wird an das Eingabefeld gebunden, wobei zwei Validatoren verwendet werden, die sicherstellen, dass der Benutzer einen gültigen und bekannten Locale-Wert auswählt. Der Code zur Validierung des Locales lautet wie folgt:

typescript
import { AbstractControl, ValidationErrors, ValidatorFn, Validators } from '@angular/forms';
import { languageTagPattern } from '../../shared/ui/language-tag'; import { allLocales } from './all-locales'; const knownLocaleValidator: ValidatorFn = (control: AbstractControl): ValidationErrors | null => { const isValid = allLocales.includes(control.value); return isValid ? null : { locale: true }; }; export const localeValidator = Validators.compose([ Validators.pattern(languageTagPattern), knownLocaleValidator, ]) as ValidatorFn;

Dieser Validator prüft, ob der eingegebene Textstring dem Format des Sprach-Tags entspricht, das im BCP47-Standard definiert ist, indem eine reguläre Ausdrucksüberprüfung durchgeführt wird. Danach wird das Sprach-Tag in einer Liste der bekannten Locales nachgeschlagen.

Die eigentliche Funktionalität des LocalePickerComponent ist es, das ausgewählte Locale an eine übergeordnete Komponente zu übergeben. Dies erfolgt über das localeChange-Event, das den ausgewählten Wert als Observable bereitstellt.

Ivy führt auch die getLocaleDirection-Funktion in @angular/common ein, die es ermöglicht, die Schreibrichtung dynamisch basierend auf dem gewählten Locale anzupassen. Diese Funktion ist besonders wichtig, wenn man Sprachen mit einer von links nach rechts und von rechts nach links verlaufenden Schreibrichtung unterstützt.

Neben diesen grundlegenden Konzepten und Implementierungen sollte man stets berücksichtigen, dass bei der internationalen und regionalen Unterstützung von Anwendungen neben der reinen Lokalisierung und Internationalisierung auch kulturelle Unterschiede und Anforderungen an die Benutzeroberfläche berücksichtigt werden müssen. Dazu gehören Dinge wie die Handhabung von verschiedenen Währungsformaten, die Gestaltung der Benutzeroberfläche in verschiedenen Sprachen und die Einhaltung regionaler Datenschutzbestimmungen. Eine gut durchdachte Lokalisierungsstrategie trägt nicht nur zur Benutzerzufriedenheit bei, sondern kann auch die Marktakzeptanz und den Erfolg einer Anwendung erheblich steigern.