Alle Code-Referenzen und Beispiele beziehen sich auf Angular 16.0.0.
In der Vergangenheit waren wir es gewohnt die forRoot
oder forChild
Funktionen zu verwenden, um Module mit bestimmten Providern zu initialisieren, oder darüber hinausgehende Konfigurierbarkeit zu erreichen. Mit der neuen Standalone-API von Angular und dem Wegfall der NgModules
wird das ganze Prozedere nochmal ein ganzes Stückchen einfacher.
Lazy Loaded Components
In unserem Beispiel haben wir ein einfaches Konfigurationsobjekt SomeConfig
, das wir unter einem bestimmten Injectiontoken COMPONENT_CONFIG
bereitstellen wollen. Außerdem exportieren wir auch eine Standardkonfiguartion für unsere Eager-Loaded-Components.
// config.model.ts
export interface SomeConfig {
debug: boolean;
text: string;
}
export const COMPONENT_CONFIG = new InjectionToken('Component Config');
export const DEFAULT_CONFIG: SomeConfig = { debug: false, text: 'Eager Component' };
Damit wir unsere Standardkonfiguration unter dem Token COMPONENT_CONFIG
bereitstellen können, müssen wir es auf Applikationsebene bereitstellen. In der neuen Standalone Projektstruktur sieht das folgendermaßen aus:
//app.config.ts
export const appConfig: ApplicationConfig = {
providers: [provideRouter(routes), {provide: COMPONENT_CONFIG, useValue:DEFAULT_CONFIG}]
};
//main.ts
bootstrapApplication(AppComponent, appConfig)
.catch((err) => console.error(err));
Wir erstellen nun eine HomeComponent
die beim Start der Anwendung eager geladen wird, weisen ihr eine Route zu und starten unsere Anwendung.
Et voilà!
Alles funktioniert erwartungsgemäß und die Komponente zeigt unsere Konfiguration an.
Soweit, so gut. Aber jetzt wird es Zeit, dass wir mal ein bisschen “Lazyness” in die ganze Sache reinbringen, oder?
Gesagt, getan. Wir erstellen uns dazu eine Kopie der HomeComponent
und nennen sie (wie solle es auch anders sein) LazyHomeComponent
. Um in der Vergangenheit ein Modul lazy zu laden, haben wir dessen Route wie folgt konfiguriert.
const routes: Routes = [
{
path: 'lazy',
loadChildren: () =>
import('../app/lazy-loaded-configurable/lazy-loaded-configurable.module').then(
(m) => m.LazyLoadedConfigurableModule),
},
];
Mit Hilfe von loadChildren
importierten wir das Modul erst wenn auf die Route navigiert wurde. In unserer modullosen Gegenwart rufen wir stattdessen einfach loadComponent
auf. Es funktioniert genau so wie loadChildren
, nur mit dem kleinen Unterschied, dass es anstelle eines Moduls eine Komponente zurück liefert. Aber wie stellen wir nun unsere Konfiguration bereit? Keine Angst, Angular hat da was für uns in petto. Mit Angular 14 wurde bereits die Möglichkeit eingeführt Provider auf Route
-Ebene zu definieren. Das bedeutet, dass Angular einen neuen EnvironmentInjector
für diese Route erzeugt. Damit können wir Services (oder Konfigurationen) bereitstellen, die nur auf Route- bzw. Child-Route-Ebene verfügbar sind. Lasst uns einmal das Providers-Array hinzufügen und uns das Endergebnis anschauen.
//app.routes.ts
export const routes: Routes = [
{ path: '', component: HomeComponent },
{
path: 'lazy',
loadComponent: () => import('./lazy-home/lazy-home.component'),
providers: [{ provide: COMPONENT_CONFIG, useValue: LAZY_CONFIG }],
}
];
Tada! Wir haben erfolgreich unsere “Lazy-Konfiguration” der LazyHomeComponent
zu Verfügung gestellt.
Ihr habt vielleicht bemerkt, dass der Komponentenimport oben kein .then(c => c.LazyHomeComponent)
beinhaltet. Ihr könnt euch diesen Teil sparen indem ihr am Ende eures Komponenten-Files einen default export macht. In unserem Fall also export default LazyHomeComponent
. Das gleiche Prinzip könnt ihr auch auf Files anwenden die Routen beinhalten.
Lazy Loaded Routes
Aber es ist in unseren Anwendungen nicht immer der Fall, dass wir alle lazy geladenen Komponenten individuell konfigurieren wollen. Wie mit Modulen, wollen wir bspw. eine Konfiguration für ein ganzes Routensegment bereitstellen können. Kein Problem. Lasst uns einen neuen Ordner mit zwei weiteren Lazy-Komponenten erstellen. Die dazugehörige Routenkonfiguration sieht so aus.
//more-lazy.routes.ts
export const MORE_LAZINESS_CONFIG: SomeConfig = { debug: false, text: 'More Laziness' };
export const MORE_LAZINESS_ROUTES: Routes = [
{
path: '',
pathMatch: 'prefix',
providers: [{ provide: COMPONENT_CONFIG, useValue: MORE_LAZINESS_CONFIG }],
children: [
{ path: 'lazy-1', component: LazyOneComponent },
{ path: 'lazy-2', component: LazyTwoComponent },
],
},
];
Wie früher definieren wir eine leere Route, die zwei Kindrouten beinhaltet. Zum Bereitstellen unserer Konfiguration befolgen wir den selben Ansatz wie zu vor und fügen das Providers-Array mit der Konfiguration hinzu.
Jetzt müssen wir nur noch diese Routen lazy laden.
export const routes: Routes = [
{ path: '', component: HomeComponent },
{
path: 'lazy',
loadComponent: () => import('./lazy-home/lazy-home.component'),
providers: [{ provide: COMPONENT_CONFIG, useValue: LAZY_CONFIG }],
},
{
path: 'more-laziness',
loadChildren: () => import('./more-laziness/more-lazy.routes'),
},
];
Wir sehen, dass wir hierzu unsere altbekannte loadChildren
Funktion nutzen können. Allerdings funktioniert sie ein bisschen anders als früher. Statt eines Moduls gib sie nun ein Routes
Objekt zurück. So müssen wir nur unsere “gebündelten” Routen referenzieren.
Und nach dem Start unserer Applikation sehen wir auch, dass alles erwartungsgemäß funktioniert.
Zusammenfassung
Wir haben uns angeschaut wie wir lazy geladene Komponenten und Routen innerhalb von Standalone Angular Applikationen konfigurieren können und haben gelernt, dass es nun sogar noch einfacher möglich ist. Der Standalone-Ansatz und die Möglichkeit Services / Konfigurationen auf Routenebene bereitzustellen sind eine großartige Ergänzung des Frameworks, und gewährleisten es wiederverwendbare Architekturen in Kombination mit einer angenehmen Developer-Experience kombinieren zu können.