Die Features im Überblick
Die Features im Überblick
Angular 9 bringt Ivy mit, aber auch darüber hinaus gibt es Neuerungen am Release. Diese fallen zwar kleiner aus, bedeuten darum aber auch weniger Aufwand für die Aktualisierung. Ein Blick auf die Neuerungen jenseits der großen Features lohnt sich.
Entwickler freuen sich oft am meisten über neue Features. Und da Vorfreude die schönste Freude ist, hat das Angular-Team den neuen Ivy-Renderer seit Angular 6 entwickelt, dabei jedoch stets als Preview in den Releases 6, 7 und 8 bereitgestellt. In Version 9 steht Ivy jetzt aber als neuer Defaultrenderer bereit und stellt damit gleichzeitig auch die bedeutendste Änderung in Angular seit mehreren Versionen dar. Natürlich finden sich in diesem Release auch andere Änderungen, doch allgemein ist das Release primär durch Versionspflege von Abhängigkeiten und kleineren Aktualisierungen geprägt. Für typische Projekte ist daher mit einem sehr geringen Aktualisierungsaufwand zu rechnen. Zumindest, wenn man von Angular 8 kommt und seine Projekte regelmäßig gepflegt hat.
Es würde Angular nicht gerecht werden, sich lediglich auf die Kernfeatures zu fokussieren, denn gerade auch das Ökosystem rund um das eigentliche Framework macht Angular zu dem als produktiv und stabil geschätzten Werkzeug, das es heute ist. Daher betrachten wir im Folgenden auch das Angular CLI und weitere Entwicklungen aus dem Angular-Universum seit dem Release der Version 8. Als Einstieg beginnen wir mit dem Core Angular Framework.
Die wichtigste Neuerung in Angular 9 ist natürlich, dass der Ivy-Renderer nun zum Default wird. An dieser Stelle wollen wir uns aber mit den allgemeinen Änderungen im Angular Framework beschäftigen.
Seit dem Release von Angular 8.0.0 haben einige Neuerungen Einzug in das Framework gehalten. Einige davon sind zur Vorbereitung des 9.0.0-Release schon mit Version 8.2.0 aufgenommen worden. Dazu zählt etwa eine automatische Migration, die in Anwendungen die bereits mit Angular 4 abgekündigte Klasse Renderer gegen den seitdem empfohlenen Renderer2 austauscht. In Version 9.0.0 wird die alte Renderer-Klasse dann endgültig entfernt.
Mit Angular 8 wurde dem @ViewChildren-Decorator ein neues Flag static hinzugefügt, mit dem der Zeitpunkt bestimmt werden kann, zu dem die ViewChildren-Query aufgelöst wird. In Angular 9 wird dieses Flag nun optional. Falls es nicht mehr angegeben wird, wird die ViewChildren-Query dynamisch ausgewertet. Das bedeutet, dass das Ergebnis der Query im ngAfterViewInit() Lifecycle Hook zur Verfügung steht. Soll die Query statisch ausgewertet werden, muss das static Flag auf true gesetzt werden. Das ist allerdings nur möglich, wenn sich die aufzulösenden Elemente nicht innerhalb einer dynamischen Sub-View, also zum Beispiel innerhalb eines *ngIf, befinden.
Eine weitere Neuerung betrifft das Angular-Dependency-Injection-System. Es ist nun möglich, einen Service per providedIn: ‚platform‘ auf Plattformebene per Dependency Injection zur Verfügung zu stellen (Listing 1). Die Plattform stellt in Angular für gewöhnlich Services zur Verfügung, die speziell mit der unterliegenden Laufzeitumgebung zusammenhängen. Standardmäßig läuft eine Angular-App etwa auf der Plattform @angular/platform-browser, also auf der Browserplattform. Diese stellt zum Beispiel die BrowserPlatformLocation zur Verfügung, die intern vom Angular-Routing verwendet wird. Es ist nun möglich, mehrere Angular-Anwendungen, die die gleiche unterliegende Angular-Plattform und alle darin enthaltenen Services verwenden, auf einer Seite zu starten. Mit dem neuen providedIn: ‚platform‘ kann ein Service also als Treeshakable-Provider auf Plattformebene zur Verfügung gestellt werden. Weiterhin kann ein Service per providedIn: ‚any‘ in alle NgModule Injectors aufgenommen werden. Dann erfolgt die Bereitstellung des Service immer in dem Modul, in dem er angefragt wurde. Das führt dazu, dass der Service kein App-Singleton mehr ist, sondern verschiedene, unabhängige Instanzen erzeugt werden.
Listing 1: plattformweiter Service
@Injectable({
providedIn: 'platform'
})
export class MyPlatformService { ... }
Um optimale Unterstützung beim Entwickeln von Angular-Anwendungen anbieten zu können, verlassen sich Entwicklungsumgebungen wie WebStorm und VS Code auf den Angular Language Service. Dieser ermöglicht eine gute Code Completion auch in den Angular Decorators und vor allem den Angular-Templates. Mit Angular 9 wurde der Language Service verbessert, sodass beim Hover über eine Direktive nun das Modul angezeigt werden kann, in dem sie deklariert wurde. Bei Komponenten kann der Language Service nun auf fehlerhaft angegebene templateUrl bzw. styleUrls hinweisen. Ist eine der angegebenen Dateien zum Beispiel nicht vorhanden, kann damit nun direkt die IDE einen Hinweis geben, ohne dass vorher ein Build-Prozess gestartet werden muss. In Templates bietet der Language Service nun auch Code-Completion-Unterstützung für die Angular-Pseudoelemente <ng-container>, <ng-content> und <ng-template>.
Im API von @angular/forms wurde eine Inkonsistenz bereinigt: Während es bisher möglich war, eine Zahl als formControlName anzugeben, ist das nun auch für formGroupName und formArrayName möglich.
Angular-Anwendungen können durch Nutzung des Pakets @angular/service-worker offlinefähig gemacht werden, indem zum Beispiel die Anwendungsbundles durch einen Service Worker lokal im Client gecacht werden. Dieser Service Worker kann auch auf dem Server neu deployte Versionen erkennen und ebenfalls clientseitig cachen. Falls eine dieser Versionen fehlerhaft ist, kann der Angular Service Worker in verschiedene Fehlerzustände übergehen. Einer dieser Zustände ist der EXISTING_CLIENTS_ONLY-Modus, der auftritt, falls neu geladene Versionen fehlerhaft sind. In diesem Modus liefert der Service Worker die gecachten Dateien nur noch an bereits verbundene Clients aus, neue Clients müssen sich die Dateien direkt aus dem Netz ziehen. Bisher war es für einen Service Worker nicht möglich, aus dem EXISTING_CLIENTS_ONLY-Modus zurück in den NORMAL-Modus zu wechseln, etwa wenn eine neue, fehlerfreie Version deployt wurde. Mit Angular 9 kann der Angular Service Worker nun in den NORMAL-Modus zurückwechseln, falls er eine fehlerfreie Version laden kann.
Wie jedes Major-Release von Angular bringt auch Version 9 einige Breaking Changes mit. Auch hier ist die Umstellung auf den Ivy-Renderer natürlich die prominenteste Änderung. Als Resultat aus dieser Umstellung ergeben sich jedoch auch viele Änderungen, die nicht unbedingt direkt ersichtlich sind, weshalb wir uns an dieser Stelle damit beschäftigen wollen. Wie oben erwähnt, kann mit Angular 9 der alte Renderer (deprecated in Angular 4) nicht mehr verwendet werden, stattdessen muss Renderer2 verwendet werden. Gleiches gilt für die zugehörigen Klassen RenderComponentType (wird ersetzt durch RendererType2) und RootRenderer (wird ersetzt durch RendererFactory2).
Mit Angular 9 ist es durch Ivy notwendig, dass alle Klassen (inklusive Sub- und Oberklassen), die Angular-Logik beinhalten, einen Angular Decorator bekommen. Bei Services ist dies der @Injectable Decorator, bei Direktiven der @Directive Decorator. Auch Oberklassen von z. B. Direktiven müssen damit nun einen @Directive Decorator bekommen. Um kennzeichnen zu können, dass es sich bei dieser Oberklasse nur um eine abstrakte Direktive handelt, ist es mit Angular 9 nun möglich, diesen @Directive Decorator auch ohne die bisher obligatorische selector Property zu verwenden. Man legt damit quasi eine abstrakte Direktive an.
Damit Code, der für den neuen Ivy-Renderer geschrieben wird, zunächst abwärtskompatibel zu der bisherigen View Engine bleibt, wurden Selektor-lose Direktiven auch für die View Engine ermöglicht. Alle Projekte, die das Angular CLI verwenden, müssen sich in der Regel nicht darum kümmern, die neu notwendigen Decorators bei einem Update zu setzen: Die automatischen Migrations sorgen direkt beim Update dafür, dass Klassen, die einen Decorator benötigen, diesen auch bekommen. Listing 2 zeigt eine BaseDirective und die davon erbende SubDirective vor der Angular 9-Migration. In Listing 3 ist dasselbe Szenario nach der Angular-9-Migration dargestellt.
Listing 2: BaseDirective vor Angular 9
// base.directive.ts
export class BaseDirective {
@Input() test: any;
}
// sub.directive.ts
@Directive({
selector: '[appSub]'
})
export class SubDirective extends BaseDirective { … }
Listing 3: BaseDirective ab Angular 9
// base.directive.ts
@Directive()
export class BaseDirective {
@Input() test: any;
}
// sub.directive.ts
@Directive({
selector: '[appSub]'
})
export class SubDirective extends BaseDirective { … }
Nutzer, die in ihrer Anwendung die Template-driven Forms von Angular verwenden, konnten bisher die <ngForm></ngForm>-Direktive zum Erzeugen eines Formulars und zum Zugriff darauf verwenden. Diese Schreibweise wurde allerdings mit Version 6 als deprecated markiert und wird jetzt, mit Version 9, entfernt. Stattdessen kann nun die Schreibweise <ng-form></ng-form> verwendet werden, die sich stärker an der Schreibweise anderer Framework-Direktiven wie etwa dem <ng-container> orientiert. In Version 6 wurde auch eine NgFromSelectorWarning-Direktive und eine Funktion FormsModule.withConfig() eingeführt, um bei Verwendung des alten <ngForm> eine Warnung auszugeben. Auch diese beiden Bestandteile werden nun entfernt.
Um eine App offlinefähig zu machen, gibt es das Paket @angular/service-worker. Das Caching-Verhalten des Angular Service Workers wird in der Datei ngsw-config.json konfiguriert. Mit Version 9 wird die Konfigurationsoption versionedFiles entfernt, die bereits seit Version 6 deprecated ist. Diese diente zur Konfiguration der gecachten Assets und wird ersetzt durch die Option files, siehe auch Listing 4 und 5 für einen Vorher-Nachher-Vergleich.
Listing 4: Ausschnitt aus ngsw-conf.json vor Version 9
"assetGroups": [
{
"name": "demo",
"resources": {
"versionedFiles": [
"/**/*.jpg"
]
}
}
]
Listing 5: Ausschnitt aus ngsw-conf.json ab Version 9
"assetGroups": [
{
"name": "demo",
"resources": {
"files": [
"/**/*.jpg"
]
}
}
]
Auch die Abhängigkeiten, auf denen Angular aufbaut, wurden aktualisiert. So wird nun vorausgesetzt, dass RxJS in der (momentan) aktuellsten Releaseversion 6.5.3 vorliegt. TypeScript wurde auf die Version 3.6 aktualisiert, ältere TypeScript-Versionen werden explizit nicht mehr unterstützt. Mit den TypeScript-Neuerungen werden wir uns unten näher beschäftigen. Ein weiterer Breaking Change, der in Zusammenhang mit TypeScript steht, ist die npm-Bibliothek tslib. Diese Bibliothek stellt Hilfsfunktionen wie __extends() zur Verfügung, die zur Laufzeit benötigt werden. Ohne diese Bibliothek müsste TypeScript diese Funktionen selbst generieren, wodurch es zu mehrfachen Definitionen der immer gleichen Funktionen kommen kann. Der Breaking Change betrifft nun den Status der tslib: Bisher war diese von Angular als Dependency gelistet, sie wurde aber nun zu einer Peer Dependency reduziert. Damit müssen Projekte, die das Angular CLI nicht verwenden, tslib nun als eigene Abhängigkeit installieren. Für Angular-CLI-Nutzer wird der Change durch eine automatische Migration durchgeführt.
Angular legte schon immer Wert auf eine gute Testbarkeit von Anwendungen. Dafür bringt es unter anderem eigene Testing-Utilities mit, zum Beispiel TestBed, mit dem man ein eigenes Angular-Testmodul für seine Komponenten aufbauen kann. Mit der Funktion TestBed.get() kann man sich zu diesem Modul gehörige Services injecten lassen. Allerdings ist die get-Funktion nicht typisiert, da sie als Rückgabetypen lediglich any spezifiziert. Aus diesem Grund ist diese Funktion nun deprecated, stattdessen sollte das typsichere TestBed.inject() verwendet werden.
Um Modal- und andere Overlay-Komponenten unabhängig vom sonstigen Komponentenbaum dynamisch instanziieren und anzeigen zu können, müssen sie bisher im entryComponents-Array eines NgModule eingetragen sein. Mit Ivy ist das nicht mehr nötig und wird deshalb nun zunächst deprecated. Das Gleiche gilt für das Injection-Token ANALYZE_FOR_ENTRY_COMPONENTS. Dieses Token erlaubt es bisher, die entryComponents per Dependency Injection zu konfigurieren.
Um einem Angular-Modul Konfigurationen übergeben zu können, bieten bestimmte Module Funktionen wie .forRoot() oder .forFeature() an. Das kann zum Beispiel beim RouterModule (@angular/router) oder dem StoreModule (@ngrx/store) beobachtet werden. Im Fall des RouterModule werden die ansteuerbaren Routen übergeben, beim StoreModule werden die zu verwendenden Reducer übergeben. Alle diese Funktionen haben als Rückgabetypen ein ModuleWithProviders, also ein Modul, das um Provider angereichert ist (Listing 6).
Listing 6: Konfiguration ohne generischen Typ (vor Angular 9)
@NgModule({...})
export class MyCustomModule {
static forRoot(config: MyConfig): ModuleWithProviders {
return {
ngModule: MyOtherModule,
providers: [
{provide: CONFIG_TOKEN, useValue: config}
]
};
}
}
Bisher konnte dieser Typ mit einem optionalen generischen Typparameter versehen werden, der das Modul kennzeichnen sollte, das um Provider angereichert werden soll (in Listing 6 wird zum Beispiel das Modul MyOtherModule angereichert). Mit Angular 9 wird der optionale Typparameter deprecated, stattdessen sollte der Typparameter immer explizit mit angegeben werden. Die neue Variante ist in Listing 7 zu sehen. In einer zukünftigen Version von Angular ist damit zu rechnen, dass der optionale Typparameter komplett wegfällt, sodass immer zwingend ein Typparameter mit angegeben werden muss.
Listing 7: Konfiguration mit generischem Typ (ab Angular 9)
@NgModule({...})
export class MyCustomModule {
static forRoot(config: MyConfig): ModuleWithProviders>MyOtherModule> {
return {
ngModule: MyOtherModule,
providers: [
{provide: CONFIG_TOKEN, useValue: config}
]
};
}
}
Eine weitere Deprecation betrifft die Struktur der per npm bzw. Yarn installierten Angular-Pakete. Bisher werden alle Angular-Pakete sowohl als ES5- als auch als ES2015-kompatible Module bereitgestellt. Für beide Varianten wird ferner jeweils ein Standardmodul (esm5 bzw. esm2015) und ein Flat-Modul (fesm5 bzw. fesm2015) angeboten. Seit Angular 8 ist es nun aber so, dass die beiden ES5-Varianten (esm5 und fesm5) vom Angular CLI nicht mehr benötigt werden. Damit machen sie die Angular-Pakete unnötig groß und verlangsamen so auch den Installationsvorgang der Pakete. Um künftig (z. B. mit Version 10) auf die ES5-Varianten verzichten und so an Größe und Installationszeit sparen zu können, werden sie nun mit Angular 9 deprecated.
Das Angular CLI ist das Kommandozeilenwerkzeug (Command Line Interface) für Angular. Eine Art Schweizer Taschenmesser, das viele Aufgaben von der Projekterstellung über die Erweiterung, Build und neuerdings auch Deployment der Anwendung übernimmt. Sicherlich können moderne IDEs Aufgaben wie Codegenerierung erledigen, doch mit dem Angular CLI erhält man diese Unterstützung direkt durch das Angular-Team implementiert und auch supportet. Einige IDEs rufen daher auch lediglich das Angular CLI auf, wenn eine neue Komponente samt Test und zugehöriger Route generiert werden soll.
Diese Codegenerierung wird im Angular CLI durch Schematics umgesetzt. Angular Schematics erlauben neben der Codegenerierung auch das Update von bestehendem Code. Sogar eigene Erweiterungen lassen sich damit erstellen, um zum Beispiel ein eigenes CSS Framework, Build-Server oder auch nur Lizenzinformationen in einheitlichen und zentral gepflegten und automatisierten Prozessen umzusetzen. Dank Schematics lassen sich auch Migrationen programmatisch beschreiben, damit wird unter der Haube auch ng update umgesetzt, um darüber den Aufwand für die Aktualisierung auf eine neuere Framework-Version zu reduzieren. Kurzum: Das Angular CLI ist eine essenzielle Ergänzung des Angular Frameworks, die Angular zu einer guten Wahl für langlebige Anwendungen (auch) im Enterprise-Kontext macht.
Seit Angular 8.0.0 und damit auch dem Angular CLI 8.0.0 hat sich einiges getan. Die interessantesten Neuerungen von 8.0.0. bis 9.0.0. betrachten wir im Folgenden.
Mit dem Angular CLI 8.1 ist die Möglichkeit hinzugekommen, neue Lazy-Loading-Routen zu generieren. Da für Lazy Loading immer auch NgModules notwendig sind, geschieht das Erzeugen von Lazy-Loading-Routen mit Hilfe einer Erweiterung der ng g module Schematic. Dazu muss ein neu zu generierendes Modul angegeben werden, außerdem die Route zu diesem Modul und ein Ausgangsmodul: ng generate module demo –route=demo –module=app-routing.
In diesem Fall wird ein Ordner demo angelegt, in dem das NgModule DemoModule erstellt wird (Listing 8). Das Flag –route=demo sorgt dafür, dass innerhalb des DemoModule ein DemoRoutingModule (Listing 9) und eine leere DemoComponent eingetragen werden. Mit dem Flag –module=app-routing wird das Ausgangsmodul angegeben, von dem aus das Lazy Loading konfiguriert wird. In diesem Fall ist das Ausgangsmodul das AppRoutingModule, zu dem nun eine Route /demo hinzugefügt wurde (Listing 10).
Listing 8: Datei demo/demo.module.ts
@NgModule({
declarations: [DemoComponent],
imports: [
CommonModule,
DemoRoutingModule
]
})
export class DemoModule { }
Listing 9: Datei demo/demo-routing.module.ts
const routes: Routes = [{ path: '', component: DemoComponent }];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule]
})
export class DemoRoutingModule { }
Listing 10: Datei app-routing.module.ts
const routes: Routes = [
{
path: 'demo',
loadChildren: () => import('./demo/demo.module').then(m => m.DemoModule)
}
];
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
})
export class AppRoutingModule { }
Zum Debugging von Unit-Tests ist es hilfreich, die Ausführung der Tests auf einzelne Testfälle beschränken zu können. Mit dem im Angular CLI verwendeten Jasmine Framework ist das möglich, indem ein Testcase fokussiert wird, also indem man ein describe in ein fdescribe umwandelt oder ein it in ein fit. Dieses Fokussieren hat den Nachteil, dass trotzdem alle Testdateien, inklusive Abhängigkeiten, neu gebaut werden müssen. Hier bietet die Option –include für den Befehl ng test Abhilfe: Durch das Include Flag ist es möglich, explizit nur einzelne Verzeichnisse oder sogar einzelne (Test-)Dateien auszuführen. Dadurch wird die Testausführung beschleunigt, außerdem werden nur noch (Kompilations-)Fehler angezeigt, die auch wirklich zu dem momentanen Test gehören. Netter Seiteneffekt: durch diese Art der Testausführung kann kein fdescribe aus Versehen mit in die Versionsverwaltung eingecheckt werden.
Wenn zum Beispiel nur die Tests im Ordner /src/app/demo ausgeführt werden sollen, kann der Befehl ng test –include app/demo verwendet werden. Um hier wiederum nur die Komponententests auszuführen, kann etwa folgendes Kommando verwendet werden: ng test –include app/demo/demo.component.spec.ts.
Jeder, der schon einmal die ng update-Funktionalität des Angular CLI verwendet hat, wurde in der Regel schon einmal aufgefordert, alle lokalen Änderungen zu committen, bevor das Update ausgeführt wird. Mit dem Flag ng update –allow-dirty kann diese Fehlernachricht übergangen werden, sodass das Update auch bei ungesicherten Änderungen ausgeführt werden kann.
Im Angular CLI ist das Budget-Feature eingebaut. Dieses Feature kann Fehlermeldungen werfen, wenn die beim Build erzeugten Anwendungs-Bundles größer werden als eine bestimmte, zu konfigurierende Größe. Neu hinzugekommen ist das Budget anyComponentStyle, bei dem auf die Style-Größe der einzelnen Komponenten geprüft wird. Unbeabsichtigte Imports als Component-Style können damit entdeckt werden. Mit Angular 9 wird automatisch eine entsprechende Konfiguration generiert, wie in Listing 11 zu sehen.
Listing 11: Generierte Budget-Konfiguration
...
"budgets": [
{
"type": "anyComponentStyle",
"maximumWarning": "6kb",
"maximumError": "10kb"
}
...
]
Neu im Angular CLI ist seit Version 8.3.0 die Möglichkeit, direkt aus dem CLI heraus ein Deployment durchzuführen. Dabei werden verschiedene Anbieter wie Firebase, Azure Cloud, GitHub Pages und die npm unterstützt. Damit ist es besonders leicht, eine Anwendung so bereitzustellen, dass sie produktiv nutzbar und global verfügbar ist. Für den Nutzer gestaltet sich der Prozess besonders einfach, da er weder die Details des Builds noch die einzelnen Schritte bei dem Deployment auf einen Remoteserver selbst durchführen muss. Die Implementierung geschieht dabei durch sogenannte Builder und ist somit erweiterbar. Man kann also davon ausgehen, dass sowohl Hosting- und Cloudanbieter als auch Open-Source-Projekte entsprechenden Support für weitere Umgebungen beisteuern werden.
Die Verwendung ist dabei sehr simpel: Führt man das erste Mal ng deploy aus, ohne dass eine sogenannte Deployment Capability installiert wurde, erscheint der Hinweis aus Abbildung 1. Je nachdem, wo die Seite deployt werden soll, kann entweder eine der angegebenen Capabilities hinzugefügt werden, es können andere Third-Party Capabilities verwendet werden, oder man implementiert eine eigene Capability.
Mit Angular 8.2 wurde die TypeScript-Version 3.5 implementiert. Neben anderen kleineren Updates ist unter anderem der Hilfstyp Omit eingeführt worden. Dieser Typ ist eine Art Metatyp, mit dem man aus einem vorhandenen Typ einen neuen Typ erzeugen kann, in dem dann einige Properties aus dem ersten Typ nicht mehr vorhanden sind (Listing 12).
Listing 12: Omit-Hilfstyp
interface User {
name: string;
login: string;
password: string;
};
type DisplayUser = Omit<User, "password">;
// Entspricht
interface DisplayUser {
name: string;
login: string;
};
Angular 9 benötigt TypeScript in Version 3.6, alle niedrigeren Versionen reichen nicht mehr aus. Mit TypeScript 3.6 wurde das Handling von Promises, vor allem in Verbindung mit den async/await Keywords verbessert. Vom Compiler wird nun eine Warnung ausgegeben, wenn innerhalb einer async-Funktion ein Promise zurückgegeben wird, ohne dass auf das Promise await angewandt wurde.
Seit Version 3.6 ermöglicht TypeScript auch Unicode Characters in seinen Identifier-Namen (Abb. 2) Dies ist allerdings nur möglich, wenn das Compilation Target auf es2015 eingestellt ist.
Eigentlich gar kein Bestandteil von Angular, ist Angular Material eine Komponentenbibliothek, die auf Googles Material Design beruht. Damit werden Komponenten bereitgestellt, mit denen sich zügig optisch ansprechende Anwendungen umsetzen lassen. Seit Angular 8 hat sich auch an der Komponentenbibliothek einiges getan: Zunächst ist das Repository in components umbenannt worden. Damit soll zum Ausdruck gebracht werden, dass nicht nur die fundamentalen Material-Design-Konzepte als Style-Komponenten bereitgestellt werden, sondern auch davon unabhängige höherwertige Komponenten angeboten werden sollen.
Das erste Beispiel dafür ist die Google-Maps-Komponente, die als @angular/google-maps-Modul zur Verfügung steht. Das JavaScript-API von Google Maps wird durch einen Angular-Wrapper als vollwertige Angular-Komponente bereitgestellt. Weitere Komponenten, wie z. B. eine YouTube-Integration für den Videoplayer sind ebenfalls in Arbeit. Im zugrunde liegenden Component Development Kit (CDK) wurde die Integration des HTML5-Clipboard API durch cdk/clipboard neu aufgenommen.
Auch die Verwendung der Komponenten hat sich geändert: Statt wie bisher aus @angular/material zu importieren, müssen zukünftig die spezifischen Komponenten angegeben werden, z. B. @angular/material/button. Bei der Verwendung des Angular CLI und der Unterstützung von ng update werden die Imports automatisch transformiert.
Das grundlegende Image, trion/ng-cli bietet Node in der aktuellen LTS-Version, yarn und das Angular CLI an. Mit diesem Image lassen sich z. B. auch auf einem Build-Server Angular-Anwendungen bauen, ohne dass neben Docker weitere Abhängigkeiten installiert werden müssen. Darauf aufbauend bietet das Image trion/ng-cli-karma einen Chrome-Browser als Umgebung für Karma-basierte Unit-Tests. Auch hier funktioniert das Zusammenspiel mit einem Build-Server wieder ohne Probleme und Tests können so inklusive Code-Coverage-Erfassung durchgeführt werden. Inzwischen ist das Image von DockerHub auch schon über eine Million Mal abgerufen worden. Die Beliebtheit spricht hier für sich.
Zu guter Letzt bietet das Image trion/ng-cli-e2e eine Java-Laufzeitumgebung, um eine optimale WebDriver-Unterstützung für Ende-zu-Ende-Tests zu gewährleisten. Wird Protractor für die Ende-zu-Ende-Tests eingesetzt, reicht jedoch das trion/ng-cli-karma Image, da Protractor eine eigene WebDriver-Anbindung mitliefert, die ohne Java auskommt.
Neu hinzugekommen ist bei dem trion/ng-cli-Image die Unterstützung für mehrere Architekturen: Wer auf ARMv7 oder ARM64-Architekturen unter Linux seine Angular-Anwendungen bauen möchte, kann das nun dank des Multi-Arch Image tun, das für das latest-Tag zur Verfügung steht.
Ivy ist der neue Renderer für Angular. Dank Ivy sind noch stärkere Optimierungen möglich, um z. B. auf Basis von Angular Elements Web Components zu erzeugen, die autark lauffähig sind und somit keine Angular-Framework-Abhängigkeit mehr mitbringen. Damit skaliert Angular als Framework nun auch besser „nach unten“, wenn es um die Entwicklung einzelner Komponenten und Funktionalitäten statt umfangreicher und komplexer Anwendungen geht. In Angular 9 ist Ivy der Standard, und der seit Angular 4 als deprecated gekennzeichnete Renderer der ersten Generation wurde entfernt.
Angular-Anwendungen bestehen in der Regel nicht nur aus selbst geschriebenem Code, für viele Aufgaben werden Third-Party-Bibliotheken genutzt. Um ein vorgefertigtes Set an UI- und Layoutkomponenten zu erhalten, können wir beispielsweise Angular Material einbinden.
Damit Bibliotheken, selbst wenn sie noch nicht mit dem Ivy-Renderer erzeugt wurden, trotzdem von ihm verarbeitet werden können, gibt es den Angular-Compatibility-Compiler ngcc. Wird ein Angular-CLI-Projekt mit Angular 9 angelegt oder auf Angular 9 geupdatet, so wird zunächst ein npm-Postinstall-Skript in der package.json angelegt (Listing 13). Dieses wird dann beim Update oder späteren Install-Vorgängen direkt ausgeführt, um alle Abhängigkeiten, die Angular-Logik enthalten, auf Ivy anzupassen.
Wichtig ist dabei im Kontext von Ivy, dass Anbieter von Libraries auf den Einsatz von Ivy vorläufig verzichten, um hier abwärtskompatibel zu bleiben. Denn nicht jede Anwendung wird von heute auf morgen zu Angular 9 migriert werden.
Listing 13: Postinstall-Script in der package.json
"scripts": {
"postinstall": "ngcc --properties es2015 browser module main --first-only --create-ivy-entry-points",
...
}
Ivy enthält auch schon einen Vorgeschmack auf weitere Entwicklungen im Angular-Framework: In Angular 9 ist das Bootstrapping um einen Parameter erweitert worden, mit dem das Verhalten der Change Detection auf ein asynchrones Verfahren umgestellt werden kann. Das hat den Vorteil, dass durch eine Interaktion oder einen Event mehrfach ausgelöstes Change Detection nur noch zu einem einzelnen Change-Detection-Durchlauf während des nächsten animationFrame-Callbacks durchgeführt wird, wodurch sich die Performance der Anwendung verbessert. Typische Ursache für solche redundanten Change-Detection-Aufrufe sind Events, die im DOM bubbles und dabei an mehreren Komponenten einen entsprechenden Handler aufrufen. Die Konfiguration erfolgt durch das Property ngZoneEventCoalescing, das im Default false ist.
Bei Bazel handelt es sich um ein Build-System von Google, das Open Source verfügbar ist. Bazel ist ein öffentliches Pendant zu dem bei Google intern genutzten Blaze-Werkzeug. Seit 2015 entwickelt, hat Bazel im Oktober 2019 die stabile Version 1.0 erreicht.
Im Gegensatz zu den meisten Build-Werkzeugen zeichnet sich Bazel dadurch aus, dass ein kompletter Abhängigkeitsgraph aller Bestandteile des Builds aufgebaut wird. Das ermöglicht es, dass Bazel bei Änderungen genau bestimmen kann, welche Teile neu gebaut werden müssen und welche durch die Änderung nicht beeinflusst werden. Bazel kann dadurch stark optimierte Builds realisieren, da keine unnötigen Verarbeitungen stattfinden. Bazel unterstützt darüber hinaus auch Remote Builder, sodass der Build durch verteilte Agents mit einem Maximum an Parallelität durchgeführt werden kann. Auch das Caching von Build (Teil-)Ergebnissen kann verteilt erfolgen. Für Entwickler bedeutet das eine weitere Option, Builds zu beschleunigen und von der Rechenleistung von Cloudumgebungen zu profitieren.
Zusammen mit der Möglichkeit, sogenannte Build Rules für jeden Ordner eines Projekts zu spezifizieren, ist Bazel damit auch für polyglotte Projekte geeignet. So kann beispielsweise das in Java entwickelte Backend mit Bazel genauso gebaut werden wie das zugehörige Angular-Frontend. Damit eignet sich Bazel auch, um Projekte mit einem Mono-Repo-Ansatz zu bauen: Angular Libraries und -Anwendungen können, gegebenenfalls sogar inklusive Backend, in einem einzelnen Git-Repository verwaltet und daraus releast werden.
Die Integration bei Angular erfolgt über das Angular CLI. Der Entwickler hat somit die Wahl, sowohl den bisherigen webpack-basierten Build als auch Bazel als Build-Werkzeug zu verwenden. Dabei ist auch die nachträgliche Konfiguration, zum Beispiel für bestehende Projekte, möglich.
Um Bazel für bestehende Projekte zu verwenden, reicht es, Bazel per ng add @angular/bazel zum Projekt hinzuzufügen. Um neue Projekte mit Bazel aufsetzen zu können, müssen zunächst die Bazel Schematics installiert werden: npm install -g @angular/bazel. Unter Verwendung dieser Schematics kann dann eine neue Anwendung mit Bazel aufgesetzt werden: ng new –collection=@angular/bazel my-new-app. Mit dem so umgestellten Build-System können die gewohnten Angular-CLI-Kommandos zum Build verwendet werden.
Die behutsame Einführung des Ivy-Renderers zeigt, welchen Wert das Angular-Team der Stabilität des Frameworks beimisst. Gerade für Entwickler in großen Projekten im Enterprise-Kontext sind inkompatible Änderungen und damit einhergehender Aufwand bei Updates ein besonderes Problem. Angular schafft es hier, weitestgehend abwärtskompatibel zu bleiben und damit den Wartungs- und nicht zuletzt auch Schulungsaufwand gering zu halten. Das zeigt sich nicht nur an Angular selbst, sondern auch an den Optimierungen, die Angular CLI unter der Haube mitbringt.
An dieser Stelle kommt nun oft die Frage: Und wie geht es weiter nach Angular 9? Mit Ivy ist sicherlich ein wichtiger und großer Meilenstein erreicht. Das Angular-Team ist damit natürlich weder arbeitslos noch gehen die Ideen aus. Ein wichtiger Trend ist sicherlich die Zunahme an vorgefertigten Komponenten. So gibt es für Google Maps und YouTube beispielsweise Angular-Komponenten, die die Nutzung der Google-Dienste in eigenen Anwendungen erleichtern. Hier darf man sicherlich weitere Komponenten im Material-Design-Stil erwarten. Natürlich wird auch die interne Pflege von Abhängigkeiten zukünftige Angular-Releases beeinflussen. Auch wenn Stand heute die großen Themen der Zukunft noch nicht feststehen, ist das geplante Datum für Angular 10 schon bekannt gegeben: Im Mai 2020 ist es wieder soweit, dass ein Major-Release ansteht. Bis dahin ist es aber erst einmal noch etwas hin, und als Entwickler ist es sicher eine gute Positionierung, bis dahin das Update auf Angular 9 vollzogen zu haben.