Mit Angular 18 steht Entwickler:innen eine Vielzahl neuer Features und Verbesserungen zur Verfügung, die die Entwicklung moderner Webanwendungen weiter optimieren. Angular 18 baut auf die Fortschritte der Vorgängerversionen auf und führt zahlreiche Performanceoptimierungen sowie neue APIs ein, die die Arbeit mit dem Framework noch effizienter gestalten.
Um neue und bestehende Entwickler optimal zu unterstützen, wurde auch die mit Angular 17 neu releaste Entwicklerdoku unter [1] aus der Betaphase gehoben. Im gleichen Schritt leitet die alte Dokuseite angular.io automatisch auf die neue Seite um.
Nachdem mit Angular 17 die TypeScript-Versionen ab 5.2 verwendet werden können, muss mit Angular 18 nun mindestens TypeScript in Version 5.4 verwendet werden. Die Versionen 5.3 und 5.4 enthalten jeweils viele Verbesserungen in Bezug auf Performance und verbesserte Type Inference bzw. Typerkennung. Außerdem wurden mit den beiden Versionen viele neue Features eingeführt. Beispielhaft sollen einige der Features an dieser Stelle erklärt werden.
Um genauere Entscheidungen über verwendete Typen treffen zu können, beherrscht TypeScript das Type Narrowing. Type Narrowing kann genutzt werden, um aus einem allgemeineren Typ einen spezielleren abzuleiten. Umgesetzt wird Type Narrowing in TypeScript typischerweise durch sogenannte Type Guards. In Listing 1 bekommt beispielsweise die Funktion getColors() den Parameter status übergeben, dessen Typ entweder ein Array verschiedener Statuselemente sein kann, ein einzelner Status oder null. Soll nun auf ein Property des Status zugegriffen werden, etwa status.color, so ist das nicht direkt möglich, da nicht direkt definiert ist, ob ein Statusobjekt vorliegt, mehrere Objekte in einem Array oder gar null. Zur Fallunterscheidung werden in Listing 1 if-Bedingungen verwendet, die als Type Guards dienen. Innerhalb der if-Blöcke ist die Variable jetzt auf den entsprechenden Typen eingeengt, daher der Begriff Type Narrowing.
Listing 1: Type-Narrowing mit if-Bedingungen
function getColors(status: Status[] | Status | null): string[] {
if(!status) {
return []; // `status` ist `null`
}
if(Array.isArray(status)) {
return status.map(stat => stat.color) // `status` ist ein `Array<Status>`
}
return [status.color] // `status` ist ein `Status`-Objekt
}
Mit TypeScript 5.3 wurde nun die Möglichkeit geschaffen, Type Narrowing in Verbindung mit switch(true)-Ausdrücken zu verwenden. Bei switch(true)-Ausdrücken wird jeweils immer nur der Pfad ausgeführt, der momentan true ergibt, daher sollte in einem solchen Fall auch Type Narrowing möglich sein, das wurde mit TypeScript 4.3 umgesetzt. Ein Beispiel dazu wird in Listing 2 gezeigt. Jeder Branch der Switch-Anweisung ist durch einen eigenen Type Guard abgesichert, daher ist ein sicherer Zugriff auf die jeweiligen Eigenschaften möglich. Ein solches Statement kann nun also grundsätzlich wie eine if-elseif-else-Verkettung verwendet werden, je nach Entwicklerpräferenz.
Listing 2: Type-Narrowing mit switch(true)
function getColors(status: Status[] | Status | null): string[] {
switch (true) {
case !status: {
return []; // `status` ist `null`
}
case Array.isArray(status): {
return status.map(stat => stat.color) // `status` ist ein `Array<Status>`
}
default: {
return [status.color] // `status` ist ein `Status`-Objekt
}
}
}
Mit dem neuesten ECMAScript-Standard ES2024 wird zu den bestehenden Klassen Object und Map jeweils die neue Methode groupBy() hinzugefügt. Diese kann verwendet werden, um die Einträge eines Iterable, also z. B. eines Arrays, anhand eines Kriteriums zu gruppieren und in ein Objekt zu überführen. In Listing 3 wird gezeigt, wie groupBy genutzt werden kann, um ein Array mit Zahlen in ein Array für gerade und eines für ungerade Zahlen aufzuspalten. Dazu bekommt die groupBy-Funktion das Array numberArray mit Zahlen übergeben und eine Iterator-Funktion, die jedem Wert im Array eine Gruppe zuweist. In diesem Fall wird mit Hilfe des Modulo-Operators geprüft, ob der Wert gerade oder ungerade ist und entsprechend die Gruppe even oder odd vergeben. Das Resultat ist ein Objekt, das die Eigenschaften even und odd hat, wobei sich in einem Property das gerade, im anderen Property das ungerade Array befindet. Beide Properties müssen mit dem Safe-Navigation-Operator (auch Elvis-Operator: ?) aufgerufen werden, da ein Property nur dann angelegt wird, wenn die entsprechende Gruppe auch Einträge hat.
Von der Syntax her genau wie Object.groupBy() funktioniert auch Map.groupBy(), jedoch wird im Falle von Map.groupBy() eine Map zurückgegeben, deren Keys in diesem Fall even und odd wären. Anders als bei Objekten können bei Maps nicht nur Strings, sondern im Prinzip beliebige Objekte als Key dienen.
Listing 3: Verwendung von Object.groupBy()
const numberArray = [0, 1, 2, 3, 4, 5];
const evenOddObj = Object.groupBy(numberArray, (num) => {
return num % 2 === 0 ? "even": "odd";
});
// Resultat ist Objekt mit Properties "even" und "odd"
console.log(evenOddObj?.even); // ->[0, 2, 4]
console.log(evenOddObj?.odd); // ->[1, 3, 5]
Bisher war es möglich, in TypeScript Enums die vorbelegten TypeScript Keywords Infinity, -Infinity und NaN als Enum-Key zu verwenden, siehe Listing 4. Das ist mit TypeScript 5.4 nicht mehr möglich.
Listing 4: Mit TypeScript 5.4 nicht mehr erlaubte Enum-Keys
//Errors mit diesen Keys
enum NotAllowsEnumKeys {
Infinity,
"-Infinity",
NaN
}
Bereits mit den Angular-CLI-Versionen 17.1, 17.2 und 17.3 sind einige erwähnenswerte Neuerungen in Angular eingeflossen. Diese sollen hier auf dem Weg von Angular 17 zu Angular 18 ebenfalls kurze Erwähnung finden.
Der Karma-Testrunner, den Angular CLI für das Ausführen der Unit-Tests nutzt, wird mittlerweile nicht mehr weiterentwickelt. Daher ist das Angular-Team dabei, als Ersatz für Karma entweder das Jest-Testframework anzubieten oder den @web/test-runner. Mit Jest laufen die Tests dabei rein in der Node.js-Umgebung, während der Web-Test-Runner die Tests wie von Karma gewohnt in der Browserlaufzeitumgebung ausführt. Der Jest-Support kam als Preview bereits mit Angular 17, mit 17.1 kam dann der erste (experimentelle) Support für den Web-Test-Runner, nämlich in Form eines eigenen Test-Builders (@angular-devkit/build-angular:web-test-runner). Das Angular-Team ist also weiter fleißig dabei, das Unit-Testing mit Angular in eine neue Zeit zu bringen.
Eine weitere Anpassung an moderne Webentwicklungsumgebungen ist die neue Möglichkeit, Angular-Projekte nicht nur mit den npm- oder Yarn-Paketmanagern erzeugen und nutzen zu können, sondern auch mit Bun. Bun ist ein neuer Paketmanager im npm-Umfeld, der deutlich schneller als npm und Yarn sein soll.
Für eine bessere Übersicht beim Entwickeln ist außerdem ermöglicht worden, den Terminalinhalt zwischen Rebuilds zu leeren. Dazu muss lediglich die neue Option clearScreen in der angular.json angegeben werden, wie in Listing 5 dargestellt wird.
Listing 5: clearScreen-Option in der angular.json
{
"projects": {
"clear-demo": {
"projectType": "application",
"root": "",
"sourceRoot": "src",
"architect": {
"build": {
"builder": "@angular-devkit/build-angular:application",
"options": {
"clearScreen": true
}
}
}
}
}
}
Allem voran wird ab Angular CLI 18 Version 22 der Node.js-Laufzeitumgebung unterstützt. Damit können Entwickler auch auf die neueste Version der Node.js-Laufzeit im Rahmen der Angular-Entwicklung zurückgreifen. Daneben gibt es einige Erweiterungen, die das Entwicklerleben einfacher machen sollen. Zum Beispiel hat das Kommando ng serve einen neuen Alias bekommen. Bisher gab es beispielsweise den Alias ng s, mit dem der Dev-Server gestartet werden konnte, neu dazugekommen ist nun ng dev.
Gerade bei neuen Entwicklern konnte es laut Angular-Team unter Umständen zu Verwirrungen um den assets-Ordner in Angular-Projekten kommen. Dieser Ordner ist explizit für statische (Laufzeit-)Assets gedacht, während die Build-Zeit-Assets unterhalb des src-Ordners abgelegt sein sollten. Um diesen Unterschied klarer zu machen, heißt der Ordner bei neu generierten Projekten in Zukunft deshalb nicht mehr assets, sondern public.
Weiterhin wird mit Angular CLI 18 der Befehl ng doc entfernt, da dieser ohnehin nur selten genutzt wurde. Die offizielle Empfehlung ist, stattdessen in der Dokumentation unter [1] nachzuschauen.
Neben der neu unterstützten Node.js-Version 22 wurde auch die minimal notwendige Node.js-Version angepasst. Die minimal notwendige LTS-Node.js-Version ist nun 18.19.1, Node.js 19 wird gar nicht mehr unterstützt, und Node.js 20 erst ab Version 20.11.1.
Weiterhin wurde die Anbindung des alten, nur noch im webpack-Build unterstützten Sass API (NG_BUILD_LEGACY_SASS) entfernt. Das Legacy API ermöglichte beispielsweise...