<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Fabian - angular-freelancer.de</title>
    <description>The latest articles on DEV Community by Fabian - angular-freelancer.de (@angstitc).</description>
    <link>https://dev.to/angstitc</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F789765%2Ff5c1a8a8-9bfa-42aa-9b1f-b221744c8b34.png</url>
      <title>DEV Community: Fabian - angular-freelancer.de</title>
      <link>https://dev.to/angstitc</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/angstitc"/>
    <language>en</language>
    <item>
      <title>QDA in der Praxis: Wie Du mit Quality-Driven Architecture robuste Angular-Anwendungen entwickelst</title>
      <dc:creator>Fabian - angular-freelancer.de</dc:creator>
      <pubDate>Tue, 17 Jun 2025 15:14:50 +0000</pubDate>
      <link>https://dev.to/angstitc/qda-in-der-praxis-wie-du-mit-quality-driven-architecture-robuste-angular-anwendungen-entwickelst-21f0</link>
      <guid>https://dev.to/angstitc/qda-in-der-praxis-wie-du-mit-quality-driven-architecture-robuste-angular-anwendungen-entwickelst-21f0</guid>
      <description>&lt;p&gt;Ob in eigenen Projekten, den Medien oder Erfahrungsberichten anderer Software Engineers und -Architect:innen habe ich eines immer wieder gesehen: Ideen scheitern nicht an der Funktionalität, sondern an einer vernachlässigten Architektur. &lt;br&gt;
Eine Anwendung, die heute funktioniert, aber kein solides Fundament aufweist, wird morgen zur Last. Sie wird langsam, fehleranfällig und kaum noch wartbar. Nicht selten endet das in einem Rewrite (der im Zweifel wieder in derselben Sackgasse landet) und dem Verlust der Mitarbeiter, die man nun am ehesten bräuchte, da diese immer wieder darauf hingewiesen haben.&lt;/p&gt;

&lt;p&gt;Deshalb möchte ich heute das Thema &lt;strong&gt;Quality-Driven Architecture&lt;/strong&gt; besprechen.&lt;/p&gt;

&lt;p&gt;Es ist ein relativ leicht anzuwendendes Vorgehen. Wenn man so will, eine Art Kompass um Software und somit auch Angular-Anwendungen zu bauen, die nicht nur heute funktionieren, sondern auch in Zukunft stabil, schnell und erweiterbar bleiben.&lt;/p&gt;

&lt;h2&gt;
  
  
  Was heißt „qualitätsgetrieben“? Eine Analogie
&lt;/h2&gt;

&lt;p&gt;Stell euch vor, ihr baut ein Haus. Die funktionalen Anforderungen sind klar: Es braucht ein Dach, Wände, eine Küche, ein Bad und eine Garage. Das ist das „Was“. Aber jetzt kommen die entscheidenden Fragen, die Qualitätsfragen – das „Wie gut“:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Wie gut soll das Haus gedämmt sein, damit die Heizkosten niedrig bleiben? (&lt;strong&gt;Performance &amp;amp; Effizienz&lt;/strong&gt;)&lt;/li&gt;
&lt;li&gt;Wie sicher sollen die Schlösser sein? (&lt;strong&gt;Sicherheit&lt;/strong&gt;)&lt;/li&gt;
&lt;li&gt;Wie einfach soll es sein, später einen Wintergarten anzubauen? (&lt;strong&gt;Wartbarkeit &amp;amp; Erweiterbarkeit&lt;/strong&gt;)&lt;/li&gt;
&lt;li&gt;etc.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Genau das Gleiche tun wir in der qualitätsgetriebenen Softwarearchitektur. Wir definieren von Anfang an, welche Qualitätsmerkmale für unser Projekt relevant sind. Diese Ziele leiten dann jede unserer Entscheidungen. Das Schöne daran: Diese Ziele sind meist schon deutlich vor den konkreten, funktionalen Anforderungen definierbar und mit den Stakeholdern verhältnismäßig schnell zu ermitteln.&lt;/p&gt;

&lt;p&gt;Essenziell für ein Qualitätsziel ist, dass es Messbar ist. Denn nur was messbar ist, kann man monitoren, erreichen oder verbessern.&lt;/p&gt;




&lt;h2&gt;
  
  
  Ein wichtiger Werkzeugkoffer: Die Qualitätsziele von arc42
&lt;/h2&gt;

&lt;p&gt;Um diese Ziele greifbar zu machen, orientieren wir uns am praxiserprobten Modell von &lt;a href="https://quality.arc42.org/" rel="noopener noreferrer"&gt;&lt;strong&gt;arc42&lt;/strong&gt;&lt;/a&gt;. Es hilft uns, Klarheit zu schaffen und im Team die gleiche Sprache zu sprechen. Außerdem können wir uns an weit über 100 Zielen und Beispielen abarbeiten oder sie durchsuchen und Szenarien hierfür durchdenken. Es beinhaltet auch die Qualitäten aus &lt;a href="https://quality.arc42.org/q42-for-iso-users/" rel="noopener noreferrer"&gt;ISO-25010&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Laufzeitqualität: Was der Benutzer erlebt
&lt;/h3&gt;

&lt;p&gt;Hier geht es um alles, was während der Ausführung der Anwendung spürbar und gleichzeitig messbar ist.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;🚀 &lt;a href="https://quality.arc42.org/qualities/performance-efficiency/" rel="noopener noreferrer"&gt;&lt;strong&gt;Performance &amp;amp; Effizienz&lt;/strong&gt;&lt;/a&gt;: Die Geschwindigkeit ist oft der erste und wichtigste Eindruck und sie kostet im Zweifel Umsatz.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Messbares Ziel-Szenario:&lt;/strong&gt; 
"Die Produktdetailseite muss bei einer simulierten 'Fast 3G'-Netzwerkverbindung einen &lt;strong&gt;Largest Contentful Paint (LCP) von unter 2,5 Sekunden&lt;/strong&gt; erreichen, gemessen mit Lighthouse-Tools im CI/CD-Prozess."&lt;/li&gt;
&lt;li&gt;➡️ &lt;strong&gt;Architektonische Antwort in Angular:&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lazy Loading:&lt;/strong&gt; Wir laden nicht die gesamte App auf einmal, sondern nur die Teile, die der Nutzer gerade braucht. Bei einem E-Book beispielsweise laden wir nicht den gesamten Inhalt, stattdessen laden wir nur die aktuelle Seite oder ggf. ein Kapitel. Dies ist mit den modernen &lt;strong&gt;Standalone Components&lt;/strong&gt; und weiteren Mitteln wie Lazy-Routing und &lt;code&gt;@defer&lt;/code&gt; in Angular ein Kinderspiel.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;NgOptimizedImage&lt;/code&gt;:&lt;/strong&gt; Bilder sind oft die größten Performance-Fresser. Diese spezielle Direktive ist wie ein intelligenter Assistent, der sicherstellt, dass Bilder erst geladen werden, wenn sie sichtbar werden, und das in der optimalen Größe.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;🔐 &lt;a href="https://quality.arc42.org/qualities/security/" rel="noopener noreferrer"&gt;&lt;strong&gt;Sicherheit (Security)&lt;/strong&gt;&lt;/a&gt;: Das Vertrauen unserer Nutzer ist ein hohes Gut.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Messbares Ziel-Szenario:&lt;/strong&gt; "Bei einem automatisierten Security-Scan mit OWASP ZAP als Teil des Nightly-Builds dürfen &lt;strong&gt;keine neuen 'High'- oder 'Medium'-Alerts&lt;/strong&gt; für Cross-Site Scripting (XSS) oder Cross-Site Request Forgery (CSRF) im Frontend-Scope auftreten."&lt;/li&gt;
&lt;li&gt;➡️ &lt;strong&gt;Architektonische Antwort in Angular:&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Angulars eingebauter Schutz:&lt;/strong&gt; Das Framework ist hier Ihr stärkster Verbündeter. Es bereinigt standardmäßig alle Eingaben, bevor sie auf der Seite angezeigt werden und schützt so automatisch vor gängigen Angriffen wie Cross-Site Scripting (XSS).&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;🤝 &lt;a href="https://quality.arc42.org/qualities/reliability/" rel="noopener noreferrer"&gt;&lt;strong&gt;Zuverlässigkeit (Reliability)&lt;/strong&gt;&lt;/a&gt;: Die Anwendung muss auch dann stabil bleiben, wenn mal etwas schiefgeht.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Messbares Ziel-Szenario:&lt;/strong&gt; "Antwortet der API-Endpunkt für Produktempfehlungen mit einem Error HTTP-Status 503 (Service Unavailable), muss die Seite &lt;strong&gt;ohne JavaScript-Fehler&lt;/strong&gt; weiterlaufen und die definierte Fallback-Komponente ('Empfehlungen konnten nicht geladen werden') wird innerhalb von 500ms angezeigt."&lt;/li&gt;
&lt;li&gt;➡️ &lt;strong&gt;Architektonische Antwort in Angular:&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fehlerbehandlung in Services:&lt;/strong&gt; Wir fangen Fehler dort ab, wo sie entstehen – meist bei der Kommunikation mit dem Server (Backend). Mit dem RxJS-Operator &lt;code&gt;catchError&lt;/code&gt; können wir einen Fehler beispielsweise elegant abfangen und einen stabilen Zustand (z.B. eine leere Liste) an die Komponente weitergeben, damit die UI nicht in einen Error-State gelangt.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h3&gt;
  
  
  2. Entwicklungsqualität: Unser Alltag als Entwickler
&lt;/h3&gt;

&lt;p&gt;Diese Ziele sorgen dafür, dass die Arbeit an der Codebasis effizient und möglichst frustfrei bleibt.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;⚙️ &lt;a href="https://quality.arc42.org/qualities/maintainability/" rel="noopener noreferrer"&gt;&lt;strong&gt;Wartbarkeit (Maintainability)&lt;/strong&gt;&lt;/a&gt;: Der Code muss auch in sechs Monaten noch verständlich und einfach zu ändern sein. Das ist vielleicht das wichtigste Ziel für die Langlebigkeit eines Projekts.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Messbares Ziel-Szenario:&lt;/strong&gt; "Die &lt;strong&gt;zyklomatische Komplexität&lt;/strong&gt; jeder Methode innerhalb von Angular-Services und -Komponenten, gemessen durch Tools wie SonarQube, &lt;strong&gt;darf den Wert 10 nicht überschreiten&lt;/strong&gt;. Neue Pull Requests, die diesen Schwellenwert verletzen, werden automatisch blockiert."&lt;/li&gt;
&lt;li&gt;➡️ &lt;strong&gt;Architektonische Antwort in Angular:&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Organisation nach Features:&lt;/strong&gt; Wir gruppieren Code nicht nach Typ (alle Services in einem Ordner), sondern nach fachlicher Zuständigkeit (alles zum Thema „User“ in einem Ordner). So ist alles, was zusammengehört, auch beisammen.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Trennung in „Smart“- und „Dumb“-Komponenten:&lt;/strong&gt; Dieses Muster ist Gold wert.

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Smart:&lt;/strong&gt; Die Komponente, die weiß, &lt;em&gt;was&lt;/em&gt; zu tun ist. Sie spricht mit den Services und holt Daten.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dumb:&lt;/strong&gt; Eine dumme, aber hübsche Komponente, die nur Daten entgegennimmt und anzeigt. Sie ist wiederverwendbar und einfach zu verstehen.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;📋 &lt;a href="https://quality.arc42.org/qualities/testability/" rel="noopener noreferrer"&gt;&lt;strong&gt;Testbarkeit (Testability)&lt;/strong&gt;&lt;/a&gt;: Vertrauen ist gut, testen ist besser.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Messbares Ziel-Szenario:&lt;/strong&gt; "Die &lt;strong&gt;Code-Abdeckung (Code Coverage) durch Unit-Tests&lt;/strong&gt; für alle einer neuen Unit &lt;strong&gt;muss bei mindestens 85 % liegen&lt;/strong&gt; und soll innerhalb von 30 Minuten erreicht werden können. Dieser Wert wird bei jedem Commit geprüft und darf nicht sinken."&lt;/li&gt;
&lt;li&gt;➡️ &lt;strong&gt;Architektonische Antwort in Angular:&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Logik in Services auslagern:&lt;/strong&gt; Eine wichtige Regel für gute Testbarkeit! Komponenten-Code ist schwerer zu testen. Logik, die in einem einfachen TypeScript-Service steckt, kann hingegen leicht mit Unit-Tests überprüft werden. Angulars &lt;strong&gt;Dependency Injection&lt;/strong&gt; hilft uns dabei, diese Services in unseren Tests durch Mocks zu ersetzen.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Test Driven Development (TDD):&lt;/strong&gt; Tests schreiben, bevor die eigentliche Logik implementiert wird. So stellt man sicher, dass jede neue Funktionalität von Anfang an abgedeckt ist und erreicht idR. eine gut testbare Struktur und die gewünschte Coverage fast automatisch.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h3&gt;
  
  
  3. Betriebsqualität: Die Anwendung im Live-Betrieb
&lt;/h3&gt;

&lt;p&gt;Wenn die Anwendung live ist, wollen wir wissen, was passiert.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;📊 &lt;a href="https://quality.arc42.org/qualities/operations/" rel="noopener noreferrer"&gt;&lt;strong&gt;Betrieb &amp;amp; Monitoring (Operations)&lt;/strong&gt;&lt;/a&gt;: Blind im Produktivsystem zu agieren, ist wie Autofahren bei Nacht ohne Licht.

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Messbares Ziel-Szenario:&lt;/strong&gt; "Alle HTTP-Anfragen, die eine &lt;strong&gt;Antwortzeit von über 800 ms&lt;/strong&gt; aufweisen, müssen &lt;strong&gt;automatisch ein Event&lt;/strong&gt; im Monitoring-Tool auslösen, das die genaue URL, die Dauer und die User-ID enthält."&lt;/li&gt;
&lt;li&gt;➡️ &lt;strong&gt;Architektonische Antwort in Angular:&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;HttpInterceptor:&lt;/strong&gt; Dies ist ein mächtiges Werkzeug in Angular. Wir können einen zentralen Interceptor für alle Server-Anfragen bauen. Dieser kann zum Beispiel messen, wie lange jede Anfrage dauert, und diese Information an ein externes Monitoring-Tool (z.B. Sentry, Datadog o.Ä.) senden. So bemerken wir Performance-Probleme, bevor Nutzer sich beschweren.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;




&lt;h2&gt;
  
  
  Fazit
&lt;/h2&gt;

&lt;p&gt;Qualitätsgetriebene Architektur ist kein Hexenwerk. Es ist die bewusste Entscheidung, von Anfang an über das „Wie gut“ nachzudenken. Sprecht mit euren Teams und Auftraggeber:innen über diese Ziele. Priorisiert sie! Ein kleines internes Tool braucht vermutlich nicht die gleiche Ausfallsicherheit wie ein Online-Shop.&lt;/p&gt;

&lt;p&gt;Wenn diese Prinzipien verinnerlicht werden, werdet ihr nicht nur bessere Anwendungen bauen. Ihr werdet auch zu besseren Architekt:innen, die vorausschauend planen und Systeme erschaffen, die Freude machen – bei der Nutzung und bei der Weiterentwicklung. &lt;br&gt;
Fangt klein an, wählt zwei oder drei Qualitätsziele für die nächsten Features und wendet ggf. auch welche der oben genannten Muster an. Ihr werdet den Unterschied spüren und bitte auch messen.&lt;/p&gt;

</description>
      <category>angular</category>
      <category>architecture</category>
      <category>arc42</category>
      <category>softwarequality</category>
    </item>
    <item>
      <title>Angular &amp; ESBuild: Dein Weg zum perfekten Tree Shaking 🌳🚀</title>
      <dc:creator>Fabian - angular-freelancer.de</dc:creator>
      <pubDate>Thu, 05 Jun 2025 16:25:12 +0000</pubDate>
      <link>https://dev.to/angstitc/angular-esbuild-dein-weg-zum-perfekten-tree-shaking-12bg</link>
      <guid>https://dev.to/angstitc/angular-esbuild-dein-weg-zum-perfekten-tree-shaking-12bg</guid>
      <description>&lt;p&gt;Wir kennen es alle. Wenn man täglich mit großen Angular-Anwendungen arbeitet, weiß man einfach, wie entscheidend Performance für eine gute UX (User Experience) ist. Ein wirklich großer Faktor hierfür ist nicht nur die effiziente Implementierung von Algorithmik oder ähnlichem sonder vor Allem auch die Größe der ausgelieferten JavaScript-Bundles, die aus unserem TypeScript-Code entstehen. Zum Verringern dieser gibt es verschiedene Möglichkeiten. Eine dieser Möglichkeiten möchte ich in diesem Beitrag etwas näher betrachten: &lt;strong&gt;Tree Shaking&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Wir betrachten Tree Shaking hier vor allem ab Angular v17/v18 mit Standalone Components und ESBuild anstatt Webpack.&lt;/p&gt;

&lt;p&gt;Es ist im Übrigen kein Problem, wenn Du noch nicht super viel Erfahrung hast - Tree Shaking ist für alle da. Ich versuche Dich schrittweise mitzunehmen.&lt;/p&gt;

&lt;h2&gt;
  
  
  Was ist Tree Shaking überhaupt?
&lt;/h2&gt;

&lt;p&gt;Stell dir vor, dein Code ist ein großer, sehr verzweigter Baum. Jede Funktion, jede Klasse und jede Variable die du importierst ist ein Ast oder Blatt an diesem Baum. &lt;br&gt;
Jetzt ist es jedoch so, dass wir sehr häufig Bibliotheken oder Module importieren, die viel Mehr an Funktionen bieten als wir tatsächlich in unseren Anwendungen nutzen.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tree Shaking&lt;/strong&gt; ist der Prozess, bei dem ein Bundler (wie früher Webpack oder jetzt ESBuild in Angular) erkennt, welche Teile unseres Codes &lt;strong&gt;tatsächlich&lt;/strong&gt; von unserer Applikation genutzt werden. Alle ungenutzten "Äste" und "Blätter" werden vom Bundler aus den Artefakten, die wir später als Anwendung deployen, entfernt. Das Ergebnis ist ein kleineres, schlankeres und schneller ladbares Bundle, das der Browser des Users herunterladen muss.&lt;/p&gt;
&lt;h2&gt;
  
  
  &lt;strong&gt;Das Problem&lt;/strong&gt;: Zu viel Code im Bundle
&lt;/h2&gt;

&lt;p&gt;Bevor wir weiter ins Detail gehen, schauen wir uns ein kleines, aber alltägliches Szenario an: Wir nutzen eine Utility-Bibliothek, für diesen Beitrag die fiktive Bibliothek &lt;code&gt;@angstitc/utils&lt;/code&gt;, welche diverse Funktionen bereitstellt:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// app.ts
import { formatEnergyAmount, calculateTax, validateEmail } from '@angstitc/utils'
@Component({
  selector: 'app-root',
  template: `
    &amp;lt;h1&amp;gt;Meine Anwendung&amp;lt;/h1&amp;gt;
    &amp;lt;p&amp;gt;Preis: {{ formatEnergyAmount(123.45, 'kWh') }}&amp;lt;/p&amp;gt;
    `
})
export class AppComponent {
  // ...
  formatEnergyAmount = formatEnergyAmount;
}

// node_modules/@angstitc/utilsindex.ts (vereinfacht)
export function formatEnergyAmount(amount: number, unit: string): string {
  // ... Logik zur Formatierung einer Energiemenge ...
  console.log('formatEnergyAmount aufgerufen!');
  return `${amount.toFixed(2)} ${unit}`;
}

export function calculateTax(amount: number, rate: number): number {
  // ... Logik zur Steuerberechnung ...
  console.log('calculateTax aufgerufen!');
  return amount * rate;
}

export function validateEmail(email: string): boolean {
  // ... Logik zur E-Mail-Validierung ...
  console.log('validateEmail aufgerufen!');
}  

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In diesem Szenario importieren wir &lt;code&gt;formatEnergyAmount&lt;/code&gt;, &lt;code&gt;calculateTax&lt;/code&gt; und &lt;code&gt;validateEmail&lt;/code&gt; aus &lt;code&gt;@angstitc/utils&lt;/code&gt;. Aber in unserer AppComponent nutzen wir nur &lt;code&gt;formatEnergyAmount&lt;/code&gt;. Ohne effektives Tree Shaking würden calculateTax und validateEmail trotzdem in unserem finalen Bundle landen! Das ist unnötiger Balast.&lt;/p&gt;

&lt;h2&gt;
  
  
  Angular 17 und ESBuild: Die Traumhochzeit für Tree Shaking
&lt;/h2&gt;

&lt;p&gt;Die gute Nachricht ist, dass Angular in Kombination mit ESBuild &lt;strong&gt;herborragend&lt;/strong&gt; im Tree Shaking ist. ESBuild ist hierbei nicht nur schneller als Webpack es war, sondern auch noch effizienter.&lt;/p&gt;

&lt;p&gt;Was macht ESBuild darin aber so gut? Die Antwort ist so unspektakulär wie simpel: Es wurde von Anfang an mit dem Fokus auf hohe Performance und Effizienz entwickelt. Hierzu nutzt es moderne JavaScript-Module (ESM - ECMAScript Modules), welche es sehr gut versteht und welche die Grundlage für effektives Tree Shaking bilden.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wie funktioniert Tree Shaking (unter der Haube)
&lt;/h2&gt;

&lt;p&gt;Damit Tree Shaking sich effektiv auswirkt, müssen einige Voraussetzungen erfüllt sein:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. ESM-Module:&lt;/strong&gt; ESM bilden die Basis. Die statischen &lt;code&gt;import&lt;/code&gt;- und &lt;code&gt;export&lt;/code&gt;-Statements können durch den Bundler vor Ausführung des Codes analysiert werden. Hierdurch wird klar, welche Funktionen und Codeabschnitte wirklich genutzt werden. Mit den dynamischen &lt;code&gt;CommonJS&lt;/code&gt;-Imports (&lt;code&gt;require()&lt;/code&gt;) wäre dies deutlich schwieriger zu erreichen.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Side-Effect-Free Code:&lt;/strong&gt; Ein Modul gilt als &lt;strong&gt;Side-Effect-Free&lt;/strong&gt;, wenn der Import des Moduls keine globalen Auswirkungen hat. So kann beispielsweise eine Bibliothek in ihrer &lt;code&gt;package.json&lt;/code&gt; einen Eintrag in &lt;code&gt;sideEffects&lt;/code&gt; haben, der dem Bundler dies klar kommuniziert. Dieser Eintrag kann neben boolschen-Werten auch ein Array von Dateipfaden sein (z.B. globale Stylesheets), anhand derer der Bundler weiß, welche Dateien er im Tree Shaking ignorieren muss. &lt;br&gt;
In unserem oben gezeigten Beispiel erfüllen die genutzten Funktionen dieses Kriterium.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Minifier:&lt;/strong&gt; Nach dem Tree Shaking kommt ein Minifier zum Einsatz. Dieser entfernt ungenutzten Code, der durch den Bundler markiert wurde und optimiert den gesamten Code durch z.B. Kürzung von Variablen-Namen und Unprettify (entfernen von Zeichen die nur der Formatierung dienen etc.).&lt;/p&gt;
&lt;h2&gt;
  
  
  Unser Beispiel in Aktion
&lt;/h2&gt;

&lt;p&gt;Kehren wir zu unserem Beispiel zurück. Wenn wir unsere App mit der Prod-Configuration (sollte der Default sein) builden...&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ng build
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;...wird ESBuild unsere &lt;code&gt;AppComponent&lt;/code&gt; analysieren. Es wird sehen, dass &lt;code&gt;formatEnergyAmount&lt;/code&gt; verwendet wird, &lt;code&gt;calculateTax&lt;/code&gt; und &lt;code&gt;validateEmail&lt;/code&gt; jedoch nicht. Dadurch, dass alle drei Funktionen über einen separaten Export verfügen und die Bibliothek als &lt;strong&gt;Side-Effect-Free&lt;/strong&gt; markiert ist (achtet bei euren Bibliotheken bitte darauf. Gute Util-Bibliotheken sind so gebaut), wird ESBuild die beiden ungenutzten Funktionen nicht in das auszuliefernde Bundle aufnehmen.&lt;br&gt;
Das lässt sich überprüfen, indem man den &lt;code&gt;dist&lt;/code&gt;-Folder ein wenig analysiert. Das Bundle wird kleiner sein und bei der Analyse der JS-Dateien (hierbei helfen Source Maps, da der Code minified wurde), stellen wir fest, dass &lt;code&gt;calculateTax&lt;/code&gt; und &lt;code&gt;validateEmail&lt;/code&gt; nicht im JavaScript-Artefact enthalten sind.&lt;br&gt;
Das bedeutet: Selbst unused Imports wären nicht am Tree Shaking Türsteher vorbeigekommen.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tipps, um Tree Shaking aktiv zu verbessern
&lt;/h2&gt;

&lt;p&gt;Als Entwickler können wir aktiv dazu beitragen, das Tree Shaking in unseren Angular-Projekten zu verbessern:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Immer spezifisch importieren:&lt;/strong&gt; &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Importiere nur was du brauchst&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Gut:&lt;/strong&gt; &lt;code&gt;import { formatEnergyAmount } from '@angstitc/utils'&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Vermeide wo möglich:&lt;/strong&gt; &lt;code&gt;import * as Utils from '@angstitc/utils'; Utils.formatEnergyAmount(...)&lt;/code&gt; - das erschwert die Statische analyse.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Nicht jede Bibliothek schafft es in dein Projekt:&lt;/strong&gt;&lt;br&gt;
Bevor du eine neue Bibliothek zu deinem Projekt hinzufügst, prüfe, ob sie moderne ESM-Module verwendet und Tree Shaking-freundlich ist. Gute Bibliotheken kennzeichnen dies oft in ihrer Dokumentation oder durch den &lt;code&gt;sideEffects&lt;/code&gt; Eintrag in ihrer package.json.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Side-Effects verstehen:&lt;/strong&gt; Sei dir bewusst, dass Code, der globale Side-Effects hat (z.B. globale CSS-Dateien, die du direkt in TypeScript importierst und die keine Module sind), nicht einfach "weggeschüttelt" werden können. Das ist auch in Ordnung, da sie ja gebraucht werden. Nutze dies jedoch nur wo nötig.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Immer Production-Builds ausliefern:&lt;/strong&gt; Nur so werden Tree Shaking und Minifications angewandt.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Fazit
&lt;/h2&gt;

&lt;p&gt;Tree Shaking ist keine magische Lösung für alle Performance-Probleme, aber es ist ein grundlegender und mächtiger Optimierungsschritt, der dir dabei hilft, die Größe deines JavaScript-Bundles signifikant zu reduzieren. Mit der nativen Integration von ESBuild ist das Tree Shaking in deinen Anwendungen effizienter und schneller als je zuvor.&lt;/p&gt;

&lt;p&gt;Indem du die Prinzipien von Tree Shaking verstehst und bewusste Entscheidungen bei deinen Imports triffst, kannst du sicherstellen, dass deine Angular-Apps schlank, schnell und responsive bleiben. Deine Nutzer*innen werden es dir Danken - und deine Auftraggeber auch!&lt;/p&gt;

</description>
      <category>angular</category>
      <category>performance</category>
      <category>typescript</category>
      <category>javascript</category>
    </item>
    <item>
      <title>🚀 Angular v20 ist da 🚀</title>
      <dc:creator>Fabian - angular-freelancer.de</dc:creator>
      <pubDate>Thu, 29 May 2025 16:11:30 +0000</pubDate>
      <link>https://dev.to/angstitc/angular-v20-ist-da-3m83</link>
      <guid>https://dev.to/angstitc/angular-v20-ist-da-3m83</guid>
      <description>&lt;p&gt;Hallo zusammen! 👋&lt;/p&gt;

&lt;p&gt;Angular v20 ist da und bringt einige spannende Neuerungen mit sich, die unsere Entwicklerherzen höher schlagen lassen! 🚀&lt;/p&gt;

&lt;p&gt;Hier eine kleine Liste der (für mich) wichtigsten Neuerungen:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Mehr Stabilität bei Signals:&lt;/strong&gt; Die Reaktivität in Angular hat in den letzten Releases einen Paradigmenwechsel durchlebt. Dieser hält an und wird konsequent fortgeführt. So sind &lt;code&gt;effect&lt;/code&gt;, &lt;code&gt;linkedSignal&lt;/code&gt; und &lt;code&gt;toSignal&lt;/code&gt; nun stable und können auch in Projekten mit etwas konservativerem Ansatz genutzt werden.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Zoneless Developer Preview:&lt;/strong&gt; Ein weiterer großer Schritt in Richtung Zukunft ist getan: Zoneless ist nun in der Developer Preview. Feinere Change Detection, bessere Performance und einfacherer Integration mit anderen Frameworks könnt ihr jetzt also guten Gewissens ausprobieren.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Template Literals:&lt;/strong&gt; Strings in Templates mit &lt;code&gt;+&lt;/code&gt;zusammenbauen oder gar durch mehrere Interpolations gehen ist nun vorbei. Wie auch schon direkt im TypeScript code, könnte ihr Teile eurer Strings im Template dynamisch verändern:&lt;br&gt;
&lt;code&gt;&amp;lt;span&amp;gt;{{ 'Willkommen ${user().name}! Schön dich zu sehen.'}}&amp;lt;/span&amp;gt;&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Ng-Reflect im DOM fällt weg:&lt;/strong&gt; Bisher waren im Developer-Mode im DOM die NG-Reflect-Attribute für z.B. RouterLinks zu sehen. Diese fallen nun &lt;strong&gt;&lt;strong&gt;ALLE&lt;/strong&gt;&lt;/strong&gt; weg. Wer sich in Unit Tests hierauf verlassen hat, muss nachbessern.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Überarbeiteter Styleguide:&lt;/strong&gt; Bye bye &lt;code&gt;.component.&amp;lt;xyz&amp;gt;&lt;/code&gt;-Suffix. Ab sofort fallen die Suffixe für Components, Services und Co. weg.&lt;br&gt;
Alle Neuerungen des Styleguides findet ihr &lt;a href="https://angular.dev/style-guide" rel="noopener noreferrer"&gt;hier&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Was sind für euch die wichtigsten Änderungen? 👇&lt;/p&gt;

</description>
      <category>angular</category>
      <category>typescript</category>
      <category>signals</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Angular &amp; Facades: So entkoppelst du deine Components und machst deinen Code wartbarer</title>
      <dc:creator>Fabian - angular-freelancer.de</dc:creator>
      <pubDate>Thu, 08 May 2025 18:11:58 +0000</pubDate>
      <link>https://dev.to/angstitc/angular-facades-so-entkoppelst-du-deine-components-und-machst-deinen-code-wartbarer-5e89</link>
      <guid>https://dev.to/angstitc/angular-facades-so-entkoppelst-du-deine-components-und-machst-deinen-code-wartbarer-5e89</guid>
      <description>&lt;p&gt;Sobald eine Angular-Anwendung wächst kann es schnell unübersichtlich werden. Komponenten die beispielsweise direkt auf Backend-APIs zugreifen oder den Application-State manipulieren sind hier aus meiner Erfahrung heraus keine Seltenheit und führen langfristig zu Problemen. Komponenten werden zu groß, die Testbarkeit leidet und das Austauschen von Stores oder APIs wird mühsam und kostspielig.&lt;/p&gt;

&lt;p&gt;Ein Lösungsansatz für diese Problematik könnte die Verwendung von Facades sein. Facades sind eine Art von Design-Pattern, das eine vereinfachte Schnittstelle zu einer komplexen API oder einem System bereitstellt. In Angular-Anwendungen können Facades verwendet werden, um den Zugriff auf verschiedene Teile der Anwendung zu abstrahieren und zu vereinheitlichen. Sie fungieren als Vermittler zwischen der UI und den Services, die die Logik und den Zustand der Anwendung verwalten.&lt;/p&gt;

&lt;h2&gt;
  
  
  Was sind Facades (in Angular)?
&lt;/h2&gt;

&lt;p&gt;Stell dir eine Fernbedienung für ein komplexes Soundsystem vor. Anstatt dich mit den unzähligen Knöpfen und Einstellungen der einzelnen Komponenten (Verstärker, Equalizer, Lautsprecher) auseinandersetzen zu müssen, bietet dir die Fernbedienung eine einfache Möglichkeit, die Lautstärke zu regeln, die Quelle auszuwählen oder das System ein- und auszuschalten. Genau diese Rolle übernimmt eine Facade in Angular.&lt;/p&gt;

&lt;p&gt;Eine Angular Facade ist ein Service, der als &lt;strong&gt;vereinfachte Schnittstelle&lt;/strong&gt; zu einem komplexeren Subsystem dient. Dieses Subsystem kann Backend-APIs, State-Management-Lösungen oder andere komplexe Dienste umfassen. Die Facade kapselt die zugrunde liegende Komplexität und bietet den Komponenten eine intuitive und leicht verständliche API.&lt;/p&gt;

&lt;p&gt;Ein einfaches Beispiel: Angenommen, du möchtest Benutzerdaten abrufen. Ohne Facade könnte deine Komponente direkt den &lt;code&gt;HttpClient&lt;/code&gt; und einen spezifischen API-Service nutzen. Mit einer Facade sieht das Ganze so aus:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;
&lt;span class="c1"&gt;// The actual API service&lt;/span&gt;
&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Injectable&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;providedIn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;root&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UserService&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="nx"&gt;httpClient&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;inject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;HttpClient&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nf"&gt;getUsers&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="nx"&gt;httpClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;get&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;User&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://jsonplaceholder.typicode.com/users&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// The facade to abstract the Access to the API&lt;/span&gt;
&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Injectable&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;providedIn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;root&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UserDataFacade&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="nx"&gt;userService&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;inject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;UserService&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nf"&gt;getUsers&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nx"&gt;Observable&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;User&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="nx"&gt;userService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getUsers&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// The component that uses the facade&lt;/span&gt;
&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;app-user-profile&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;imports&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;AsyncPipe&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;template&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`@for (user of users$ | async; track user.id) {
    &amp;lt;div class="user"&amp;gt;
      &amp;lt;h2&amp;gt;{{ user.name }}&amp;lt;/h2&amp;gt;
      &amp;lt;p&amp;gt;Email: {{ user.email }}&amp;lt;/p&amp;gt;
      &amp;lt;p&amp;gt;Phone: {{ user.phone }}&amp;lt;/p&amp;gt;
      &amp;lt;p&amp;gt;Website: {{ user.website }}&amp;lt;/p&amp;gt;
    &amp;lt;/div&amp;gt;
    }`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;changeDetection&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ChangeDetectionStrategy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;OnPush&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UserListComponent&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="nx"&gt;userDataFacade&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;inject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;UserDataFacade&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;users$&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="nx"&gt;userDataFacade&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getUsers&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In dem hier zu sehenden Beispiel delegiert die &lt;code&gt;UserDataFacade&lt;/code&gt; den Zugriff auf den &lt;code&gt;UserService&lt;/code&gt; und stellt eine vereinfachte API bereit, die von der Komponente verwendet werden kann. Dadurch wird die Komplexität des zugrunde liegenden Systems verborgen und die Testbarkeit sowie Wartbarkeit der Anwendung verbessert. Angenommen die User-Daten würden durch 50 andere Komponenten verwendet, dann würde es genügen die Facade zu ändern und nicht alle 50 Komponenten. Das ist einer der großen Vorteile von Facades.&lt;/p&gt;

&lt;p&gt;Die Tests für die Facade sind ebenfalls einfacher, da du nur die Facade selbst testen musst, anstatt alle Komponenten, die den Service verwenden. Du kannst Mock-Implementierungen der Facade verwenden, um sicherzustellen, dass die Komponenten korrekt mit der Facade interagieren, was folgendes Beispiel zeigt:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;UserListComponent&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="na"&gt;component&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;UserListComponent&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="na"&gt;fixture&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ComponentFixture&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;UserListComponent&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="na"&gt;mockUserFacade&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Partial&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;UserDataFacade&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nf"&gt;beforeEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;mockUserFacade&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;getUsers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;jest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;mockRejectedValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="k"&gt;of&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
          &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Leanne Graham&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;username&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Bret&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Sincere@april.biz&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;address&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
              &lt;span class="na"&gt;street&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Kulas Light&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
              &lt;span class="na"&gt;suite&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Apt. 556&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
              &lt;span class="na"&gt;city&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Gwenborough&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
              &lt;span class="na"&gt;zipcode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;92998-3874&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
              &lt;span class="na"&gt;geo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="na"&gt;lat&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;-37.3159&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="na"&gt;lng&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;81.1496&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
              &lt;span class="p"&gt;},&lt;/span&gt;
            &lt;span class="p"&gt;},&lt;/span&gt;
            &lt;span class="na"&gt;phone&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;1-770-736-8031 x56442&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;website&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;hildegard.org&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;company&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
              &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Romaguera-Crona&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
              &lt;span class="na"&gt;catchPhrase&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Multi-layered client-server neural-net&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
              &lt;span class="na"&gt;bs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;harness real-time e-markets&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;},&lt;/span&gt;
          &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;])&lt;/span&gt;
      &lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;

    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;TestBed&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;configureTestingModule&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;imports&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;UserListComponent&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
      &lt;span class="na"&gt;providers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt; &lt;span class="na"&gt;provide&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;UserDataFacade&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;useValue&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;mockUserFacade&lt;/span&gt; &lt;span class="p"&gt;}],&lt;/span&gt;
    &lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nf"&gt;compileComponents&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="nx"&gt;fixture&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;TestBed&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createComponent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;UserListComponent&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;component&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;fixture&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;componentInstance&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="nx"&gt;fixture&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;detectChanges&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;fixture&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;whenStable&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;should create&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;component&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toBeTruthy&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;should call getUsers on init&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;mockUserFacade&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getUsers&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toHaveBeenCalledTimes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="c1"&gt;// ... more tests&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Best Practices bei der Verwendung von Facades
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Granularität&lt;/strong&gt;: Halte die Facade granular und spezifisch für einen bestimmten Anwendungsfall oder eine bestimmte Funktionalität. Vermeide es, eine große Facade zu erstellen, die viele verschiedene Funktionen abdeckt. Genauso ist es nicht sinnvoll eine Facade für jede Komponente zu erstellen. Eine Facade sollte immer mehrere Komponenten bedienen können. Die Balance zwischen Granularität und Wiederverwendbarkeit ist hier entscheidend.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Verwende Dependency Injection&lt;/strong&gt;: Nutze Angulars Dependency Injection, um Abhängigkeiten in der Facade zu verwalten. Dadurch wird die Testbarkeit und Wartbarkeit verbessert.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Vermeide direkte Abhängigkeiten&lt;/strong&gt;: Vermeide direkte Abhängigkeiten zwischen der Facade und den Komponenten. Verwende stattdessen Observable oder andere Mechanismen, um die Kommunikation zu ermöglichen.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Vermeide zu viel Logik&lt;/strong&gt;: Halte die Logik in der Facade minimal. Die Facade sollte hauptsächlich als Vermittler fungieren und nicht zu viel Geschäftslogik enthalten. Diese sollte in den genutzten Services liegen.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Vorteile von Facades
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Abstraktion&lt;/strong&gt;: Facades abstrahieren die Komplexität des zugrunde liegenden Systems und bieten eine vereinfachte API.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Testbarkeit&lt;/strong&gt;: Facades erleichtern das Testen von Komponenten, da sie eine klare Schnittstelle bieten und die zugrunde liegende Implementierung verbergen.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Wartbarkeit&lt;/strong&gt;: Änderungen am zugrunde liegenden System erfordern keine Änderungen an den Komponenten, die die Facade verwenden.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Konsistenz&lt;/strong&gt;: Facades fördern eine konsistente API für den Zugriff auf verschiedene Teile der Anwendung, was die Lesbarkeit und Wartbarkeit des Codes verbessert.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Flexibilität&lt;/strong&gt;: Facades ermöglichen es, verschiedene Implementierungen oder Strategien auszutauschen, ohne dass die Komponenten davon betroffen sind.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Entkopplung&lt;/strong&gt;: Facades entkoppeln die Komponenten von den spezifischen Implementierungen der Dienste, was die Modularität und Wiederverwendbarkeit des Codes erhöht.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Zentralisierung&lt;/strong&gt;: Facades bieten einen zentralen Ort für die Verwaltung von Abhängigkeiten und Konfigurationen, was die Übersichtlichkeit und Wartbarkeit des Codes verbessert.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Erweiterbarkeit&lt;/strong&gt;: Facades ermöglichen es, neue Funktionen oder Dienste hinzuzufügen, ohne dass die bestehenden Komponenten geändert werden müssen.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fehlerbehandlung&lt;/strong&gt;: Facades können eine zentrale Stelle für die Fehlerbehandlung bieten, was die Konsistenz und Wartbarkeit des Codes verbessert.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Performance&lt;/strong&gt;: Facades können Optimierungen wie Caching oder Lazy Loading implementieren, um die Leistung der Anwendung zu verbessern.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dokumentation&lt;/strong&gt;: Facades bieten eine klare und konsistente API, die die Dokumentation und das Verständnis der Anwendung erleichtert.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Best Practices&lt;/strong&gt;: Facades fördern bewährte Praktiken wie Separation of Concerns und Single Responsibility Principle, was zu einem saubereren und wartbareren Code führt.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Nachteile von Facades
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Zusätzliche Komplexität&lt;/strong&gt;: Facades können zusätzliche Komplexität in die Anwendung einführen, insbesondere wenn sie nicht gut strukturiert sind.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Überabstraktion&lt;/strong&gt;: Facades können zu einer Überabstraktion führen, wenn sie zu viele Funktionen oder Abstraktionen bieten, die nicht benötigt werden.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Leistungsprobleme&lt;/strong&gt;: Facades können in einigen Fällen die Leistung beeinträchtigen, insbesondere wenn sie nicht effizient implementiert sind.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Wartungsaufwand&lt;/strong&gt;: Facades erfordern zusätzlichen Wartungsaufwand, insbesondere wenn sich die zugrunde liegenden Systeme ändern.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Testaufwand&lt;/strong&gt;: Facades erfordern zusätzliche Tests, um sicherzustellen, dass sie korrekt funktionieren und die zugrunde liegenden Systeme korrekt abstrahieren.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Fazit
&lt;/h2&gt;

&lt;p&gt;Angular Facades sind ein mächtiges Werkzeug, um die Architektur deiner Anwendung zu verbessern. Durch die Entkopplung von Komponenten von komplexen Abhängigkeiten wie Backend-APIs und State-Management-Lösungen schaffst du eine sauberere, wartbarere und testbarere Codebasis. Wenn du in größeren Angular-Projekten arbeitest, solltest du den Einsatz von Facades unbedingt in Betracht ziehen, um langfristig die Qualität und Flexibilität deiner Anwendung zu gewährleisten.&lt;/p&gt;

&lt;p&gt;Lass mich gerne wissen, wie du Facades in deinen Projekten einsetzt oder ob du noch weitere Fragen zu diesem Thema hast. Ich freue mich auf den Austausch mit dir!&lt;/p&gt;

</description>
      <category>angular</category>
      <category>architecture</category>
      <category>typescript</category>
      <category>designpatterns</category>
    </item>
    <item>
      <title>Für jeden, der sich in letzter Zeit weniger mit den fundamentalen Änderungen in Angular auseinandergesetzt hat oder neu im Angular-Kosmos unterwegs ist</title>
      <dc:creator>Fabian - angular-freelancer.de</dc:creator>
      <pubDate>Sun, 27 Apr 2025 15:20:50 +0000</pubDate>
      <link>https://dev.to/angstitc/fur-jeden-der-sich-in-letzter-zeit-weniger-mit-den-fundamentalen-anderungen-in-angular-1olj</link>
      <guid>https://dev.to/angstitc/fur-jeden-der-sich-in-letzter-zeit-weniger-mit-den-fundamentalen-anderungen-in-angular-1olj</guid>
      <description>&lt;div class="ltag__link--embedded"&gt;
  &lt;div class="crayons-story "&gt;
  &lt;a href="https://dev.to/angstitc/angular-17-control-flow-bye-bye-ngif-ngfor-hallo-if-for-3cfe" class="crayons-story__hidden-navigation-link"&gt;Angular 17+ Control Flow: Bye Bye `*ngIf`&amp;amp; `*ngFor`- Hallo `@if`&amp;amp; `@for&lt;/a&gt;


  &lt;div class="crayons-story__body crayons-story__body-full_post"&gt;
    &lt;div class="crayons-story__top"&gt;
      &lt;div class="crayons-story__meta"&gt;
        &lt;div class="crayons-story__author-pic"&gt;

          &lt;a href="/angstitc" class="crayons-avatar  crayons-avatar--l  "&gt;
            &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F789765%2Ff5c1a8a8-9bfa-42aa-9b1f-b221744c8b34.png" alt="angstitc profile" class="crayons-avatar__image"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;div&gt;
            &lt;a href="/angstitc" class="crayons-story__secondary fw-medium m:hidden"&gt;
              Fabian - angular-freelancer.de
            &lt;/a&gt;
            &lt;div class="profile-preview-card relative mb-4 s:mb-0 fw-medium hidden m:inline-block"&gt;
              
                Fabian - angular-freelancer.de
                
              
              &lt;div id="story-author-preview-content-2437806" class="profile-preview-card__content crayons-dropdown branded-7 p-4 pt-0"&gt;
                &lt;div class="gap-4 grid"&gt;
                  &lt;div class="-mt-4"&gt;
                    &lt;a href="/angstitc" class="flex"&gt;
                      &lt;span class="crayons-avatar crayons-avatar--xl mr-2 shrink-0"&gt;
                        &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F789765%2Ff5c1a8a8-9bfa-42aa-9b1f-b221744c8b34.png" class="crayons-avatar__image" alt=""&gt;
                      &lt;/span&gt;
                      &lt;span class="crayons-link crayons-subtitle-2 mt-5"&gt;Fabian - angular-freelancer.de&lt;/span&gt;
                    &lt;/a&gt;
                  &lt;/div&gt;
                  &lt;div class="print-hidden"&gt;
                    
                      Follow
                    
                  &lt;/div&gt;
                  &lt;div class="author-preview-metadata-container"&gt;&lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
            &lt;/div&gt;

          &lt;/div&gt;
          &lt;a href="https://dev.to/angstitc/angular-17-control-flow-bye-bye-ngif-ngfor-hallo-if-for-3cfe" class="crayons-story__tertiary fs-xs"&gt;&lt;time&gt;Apr 27 '25&lt;/time&gt;&lt;span class="time-ago-indicator-initial-placeholder"&gt;&lt;/span&gt;&lt;/a&gt;
        &lt;/div&gt;
      &lt;/div&gt;

    &lt;/div&gt;

    &lt;div class="crayons-story__indention"&gt;
      &lt;h2 class="crayons-story__title crayons-story__title-full_post"&gt;
        &lt;a href="https://dev.to/angstitc/angular-17-control-flow-bye-bye-ngif-ngfor-hallo-if-for-3cfe" id="article-link-2437806"&gt;
          Angular 17+ Control Flow: Bye Bye `*ngIf`&amp;amp; `*ngFor`- Hallo `@if`&amp;amp; `@for
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;div class="crayons-story__tags"&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/angular"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;angular&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/webdev"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;webdev&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/typescript"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;typescript&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/controlflow"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;controlflow&lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="crayons-story__bottom"&gt;
        &lt;div class="crayons-story__details"&gt;
          &lt;a href="https://dev.to/angstitc/angular-17-control-flow-bye-bye-ngif-ngfor-hallo-if-for-3cfe" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left"&gt;
            &lt;div class="multiple_reactions_aggregate"&gt;
              &lt;span class="multiple_reactions_icons_container"&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/raised-hands-74b2099fd66a39f2d7eed9305ee0f4553df0eb7b4f11b01b6b1b499973048fe5.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
              &lt;/span&gt;
              &lt;span class="aggregate_reactions_counter"&gt;1&lt;span class="hidden s:inline"&gt; reaction&lt;/span&gt;&lt;/span&gt;
            &lt;/div&gt;
          &lt;/a&gt;
            &lt;a href="https://dev.to/angstitc/angular-17-control-flow-bye-bye-ngif-ngfor-hallo-if-for-3cfe#comments" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left flex items-center"&gt;
              Comments


              &lt;span class="hidden s:inline"&gt;Add Comment&lt;/span&gt;
            &lt;/a&gt;
        &lt;/div&gt;
        &lt;div class="crayons-story__save"&gt;
          &lt;small class="crayons-story__tertiary fs-xs mr-2"&gt;
            3 min read
          &lt;/small&gt;
            
              &lt;span class="bm-initial"&gt;
                

              &lt;/span&gt;
              &lt;span class="bm-success"&gt;
                

              &lt;/span&gt;
            
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;


</description>
      <category>angular</category>
      <category>webdev</category>
      <category>typescript</category>
      <category>controlflow</category>
    </item>
    <item>
      <title>Angular 17+ Control Flow: Bye Bye `*ngIf`&amp; `*ngFor`- Hallo `@if`&amp; `@for</title>
      <dc:creator>Fabian - angular-freelancer.de</dc:creator>
      <pubDate>Sun, 27 Apr 2025 15:19:03 +0000</pubDate>
      <link>https://dev.to/angstitc/angular-17-control-flow-bye-bye-ngif-ngfor-hallo-if-for-3cfe</link>
      <guid>https://dev.to/angstitc/angular-17-control-flow-bye-bye-ngif-ngfor-hallo-if-for-3cfe</guid>
      <description>&lt;p&gt;Seit Angular 17 hat sich die Art, mit der wir Logik in unseren Templates steuern, grundlegend geändert. Die altbekannten strukturellen Direktiven wie &lt;code&gt;*ngIf&lt;/code&gt;, &lt;code&gt;*ngFor&lt;/code&gt; und &lt;code&gt;*ngSwitch&lt;/code&gt; bekommen Konkurrenz durch eine neue, den Standard definierende Control Flow Syntax: &lt;code&gt;@if&lt;/code&gt;, &lt;code&gt;@for&lt;/code&gt; und &lt;code&gt;@switch&lt;/code&gt;. Aber warum ganze? Ist es nur syntactic Sugar oder steckt mehr dahinter? TLDR;: Es ist mehr!&lt;/p&gt;

&lt;p&gt;In diesem Beitrag tauchen wir tief in die neue Syntax ein, vergleichen sie mit den alten Direktiven und beleuchten die Vorteile, die weit über die reine Optik hinausgehen.&lt;/p&gt;

&lt;h2&gt;
  
  
  Warum ein neuer Control Flow?
&lt;/h2&gt;

&lt;p&gt;Das Angular-Team verfolgt mit dieser Änderung mehrere Ziele:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Bessere Performance:&lt;/strong&gt; Die neue Syntax ist direkt in den Compiler integriert und benötigt keine Direktiven-Imports, was zu kleineren Bundles und potenziell schnellerer Ausführung führen kann (insbesondere in Kombination mit zukünftigen Zoneless-Strategien).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Verbesserte Developer Experience (DX):&lt;/strong&gt; Eine intuitivere, klarere Syntax, die weniger fehleranfällig ist und schon beim visuellen Scannen des Codes ersichtlich ist.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Striktere Typisierung:&lt;/strong&gt; Insbesondere &lt;code&gt;@switch&lt;/code&gt; profitiert von besserer Type Narrowing.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Im Detail: &lt;code&gt;@if&lt;/code&gt;, &lt;code&gt;@else if&lt;/code&gt;, &lt;code&gt;@else&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Die neue Bedingungslogik ist sichtlich einfach:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;userLoggedIn&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Willkommen&lt;/span&gt; &lt;span class="nx"&gt;zurück&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{{&lt;/span&gt; &lt;span class="nf"&gt;userName&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;}}&lt;/span&gt;&lt;span class="o"&gt;!&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/p&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nf"&gt;button &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;click&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;logout()&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Logout&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/button&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;isLoading&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Lade&lt;/span&gt; &lt;span class="nx"&gt;Benutzerdaten&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/p&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Bitte&lt;/span&gt; &lt;span class="nx"&gt;einloggen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/p&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nf"&gt;button &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;click&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;login()&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Login&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/button&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Im vergleich zu &lt;code&gt;*ngIf&lt;/code&gt;:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Kein &lt;code&gt;then&lt;/code&gt;oder &lt;code&gt;else&lt;/code&gt; mit Template-Referenzen mehr nötig - deutlich erhöhte Lesbarkeit.&lt;/li&gt;
&lt;li&gt;Klar definierte Blöcke&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Im Detail &lt;code&gt;@for&lt;/code&gt; und &lt;code&gt;@empty&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Das Iterieren über Listen wurde ebenfalls angepasst, wie in folgendem Beispiel zu erkennen ist:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ul&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nf"&gt;users&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="nx"&gt;track&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;li&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kd"&gt;class&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;first&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;$first&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kd"&gt;class&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;last&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;$last&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kd"&gt;class&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;even&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;$event&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kd"&gt;class&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;odd&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;$odd&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;{{&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;}}&lt;/span&gt;&lt;span class="sr"&gt;/{{ $count }}: {{ user.name }} &lt;/span&gt;&lt;span class="se"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;ID: {{ user.id }}&lt;/span&gt;&lt;span class="err"&gt;)
&lt;/span&gt;    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/li&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;empty&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;li&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Keine&lt;/span&gt; &lt;span class="nx"&gt;Benutzer&lt;/span&gt; &lt;span class="nx"&gt;gefunden&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/li&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/ul&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Zu beachten bei &lt;code&gt;@for&lt;/code&gt;:&lt;/strong&gt; &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;track&lt;/code&gt; ist nun Pflicht:&lt;/strong&gt; Das führt dazu, dass eine Funktion bzw. ein Parameter angegeben werden muss, anhand dessen das Tracking der Listeneinträge durch Angular geschieht. Dies verbessert die Performance des Renderings eklatant, da Angular erkennt, welches Element bei Änderungen neu gerendert werden muss, anstatt die ganze Liste neu darzustellen. Dies war vorher optional und wurde entsprechend oft vergessen ohne den Impact zu kennen.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;empty&lt;/code&gt;-Block:&lt;/strong&gt; Das Anzeigen von Empty-States wird hiermit out of the Box durch Angular unterstützt und spart mühsame IF-Abfragen.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Implizite Variablen:&lt;/strong&gt; Variablen, die früher im Schleifen-Kopf expliziert definiert werden mussten, stehen nun direkt zu Verfügung. Diese sind &lt;code&gt;$index&lt;/code&gt;, &lt;code&gt;$first&lt;/code&gt;, &lt;code&gt;$last&lt;/code&gt;, &lt;code&gt;$even&lt;/code&gt;, &lt;code&gt;$odd&lt;/code&gt; und &lt;code&gt;$count&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Im Detail: &lt;code&gt;@switch&lt;/code&gt;, &lt;code&gt;@case&lt;/code&gt; und &lt;code&gt;@default&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Zustandsabhängige Darstellungen sind nun deutlich mehr straight-forward zu lösen.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Im vergleich zu &lt;code&gt;*ngSwitch&lt;/code&gt;:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Bessere Typprüfung innerhalb der &lt;code&gt;@case&lt;/code&gt;-Blöcke (Type Narrowing)&lt;/li&gt;
&lt;li&gt;Klarere Syntax und deutlich getrenntere Blöcke ohne &lt;code&gt;*ngSwitchCase&lt;/code&gt;-Directiven
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;switch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;accessLevel&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;case&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;admin&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;admin&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;dashboard&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;case&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;moderator&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;moderator&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;tools&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;default&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;profile&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Vorteile zusammengefasst:
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Performance:&lt;/strong&gt; Potenziell kleinere Bundles und effizientere Change Detection (insbesondere in Zukunft). &lt;code&gt;track&lt;/code&gt; bei &lt;a class="mentioned-user" href="https://dev.to/for"&gt;@for&lt;/a&gt; ist ein direkter Performance-Gewinn.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Developer Experience:&lt;/strong&gt; Intuitiver, weniger Boilerplate, besser lesbar.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Typsicherheit:&lt;/strong&gt; Vor allem bei &lt;a class="mentioned-user" href="https://dev.to/switch"&gt;@switch&lt;/a&gt; verbessert.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Keine Imports:&lt;/strong&gt; Die neue Syntax ist eingebaut, keine Notwendigkeit, &lt;code&gt;CommonModule&lt;/code&gt; oder &lt;code&gt;NgIf&lt;/code&gt;/&lt;code&gt;NgFor&lt;/code&gt; etc. zu importieren (gerade bei Standalone Components, welche der neue Standard sind, sehr von Vorteil).&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Migration
&lt;/h2&gt;

&lt;p&gt;Die Migration ist, wie man es aus dem Angular-Ecosystem mittlerweile gewohnt ist, sehr einfach und Komfortabel durchzuführen. Das Angular Team bietet hierfür eine Schematic an, mit welcher existierender Code automatisch migriert und auf Wunsch auch formatiert wird:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ng generate @angular/core:control-flow
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Fazit
&lt;/h2&gt;

&lt;p&gt;Der neue Control Flow ab Angular Version 17 ist weit mehr als nur eine kosmetische Änderung. Er verbessert die Performance, die Developer Experience und die Typsicherheit. Die Pflicht zum track-Keyword bei &lt;a class="mentioned-user" href="https://dev.to/for"&gt;@for&lt;/a&gt; allein ist ein riesiger Schritt für effizientere Listen-Updates. Es gibt keinen Grund, mit der alten Syntax zu arbeiten, wenn man mit Angular 17+ arbeitet!&lt;/p&gt;

</description>
      <category>angular</category>
      <category>webdev</category>
      <category>typescript</category>
      <category>controlflow</category>
    </item>
    <item>
      <title>Angular 19: Die neue ResourceAPI</title>
      <dc:creator>Fabian - angular-freelancer.de</dc:creator>
      <pubDate>Sun, 01 Dec 2024 10:55:39 +0000</pubDate>
      <link>https://dev.to/angstitc/angular-19-die-neue-resourceapi-1goo</link>
      <guid>https://dev.to/angstitc/angular-19-die-neue-resourceapi-1goo</guid>
      <description>&lt;p&gt;Mit Angular 19 führt das Framework eine neue und spannende Funktion ein: die ResourceAPI. Diese API ist darauf ausgelegt, die Arbeit mit HTTP-Anfragen einfacher, strukturierter und effizienter zu gestalten. Aber vor allem hebt es diese in die Welt der Signals. Hiermit geht das Angular-Team einen weiteren und essentiellen Schritt, Angular komplett auf Signals umzustellen.&lt;/p&gt;

&lt;p&gt;In diesem Blogbeitrag werfen wir einen Blick darauf, was die Resource API ist, welche Vorteile sie bietet und wie sie verwendet werden kann.&lt;/p&gt;




&lt;h2&gt;
  
  
  Was ist neu?
&lt;/h2&gt;

&lt;p&gt;Mit Angular 19 kommt die ResourceAPI. Diese vereinfacht das asynchrone Laden von Daten erheblich und bietet eine reaktive Schnittstelle für die Arbeit mit Ressourcen.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;Achtung, die ResourceAPI ist noch Experimental. Von einem produktiven Einsatz rate ich dringend ab.&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Was versprechen wir uns davon?
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Vereinfachte Datenabholung&lt;/strong&gt; - Weniger Boilerplate und bessere Lesbarkeit&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reaktivität&lt;/strong&gt; - Mit Signals anstatt RxJS&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Bessere Kontrolle&lt;/strong&gt; - Effizienteres verfolgen des Status von Anfragen und verwalten von Fehlern, Reloads und Responses&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Wie nutzt man die API?
&lt;/h2&gt;

&lt;p&gt;Als beispielhaftes Backend nutze ich in folgenden Beispielen die API von &lt;a href="https://restful-api.dev/" rel="noopener noreferrer"&gt;restful-api.dev&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Folgend erstellen wir eine Component in einem Angular 19 Projekt, mit inline Template und Styles.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ng g c ApiResourceDemoComponent &lt;span class="nt"&gt;--inline-style&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt; &lt;span class="nt"&gt;--inline-template&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt; &lt;span class="nt"&gt;--change-detection&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;OnPush &lt;span class="nt"&gt;--skip-tests&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In dieser Component möchten wir einen Datensatz laden, Fehler- und Loading-State darstellen sowie nach erfolgreichem abholen der Daten diese darstellen.&lt;/p&gt;

&lt;p&gt;Hierzu nutzen wir die neue ResourceAPI.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;JsonPipe&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@angular/common&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;ChangeDetectionStrategy&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;resource&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;signal&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@angular/core&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;


&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;app-api-resource-demo&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;imports&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;JsonPipe&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;changeDetection&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ChangeDetectionStrategy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;OnPush&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;template&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`
    &amp;lt;div&amp;gt;
      @if(dataResource.isLoading()) {
        &amp;lt;span&amp;gt;Loading...&amp;lt;/span&amp;gt;
      }
      @if(dataResource.error()) {
        &amp;lt;span&amp;gt;Error while loading data &amp;lt;/span&amp;gt;
      }
      @if(!dataResource.isLoading() &amp;amp;&amp;amp; dataResource.hasValue()) {
        &amp;lt;pre&amp;gt;{{ dataResource.value() | json }}&amp;lt;/pre&amp;gt;
      }
      &amp;lt;br&amp;gt;
      &amp;lt;hr&amp;gt;
      &amp;lt;button (click)="onReload()" [disabled]="dataResource.isLoading()"&amp;gt;Reload data&amp;lt;/button&amp;gt;
    &amp;lt;/div&amp;gt;
  `&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ApiResourceDemoComponent&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;signal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;dataResource&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;resource&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;request&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;id&lt;/span&gt;&lt;span class="p"&gt;()}),&lt;/span&gt;
      &lt;span class="na"&gt;loader&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;abortSignal&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;await &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`https://api.restful-api.dev/objects/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;signal&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;abortSignal&lt;/span&gt; &lt;span class="p"&gt;})).&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;


    &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="nf"&gt;onReload&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dataResource&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reload&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Das ID Signal steht hier exemplarisch für eine in irgend einer Art von außen gesetzte Variable.&lt;br&gt;
Die &lt;code&gt;dataResource&lt;/code&gt; wird aus dem Request und dem Loader zusammengesetzt. Der Request ist hier eher ein Parameter, das Angular Team ist sich hier im Naming selbst noch nicht 100% sicher. &lt;br&gt;
Der Loader bildet den wirklichen HTTP Request ab, der hier via fetch gelöst wird. Das Abort-Signal ermöglicht es, den Request vorzeitig abzubrechen.&lt;/p&gt;

&lt;p&gt;Die Resource ermöglicht es, abzufragen ob der Request noch in Gange ist (&lt;code&gt;isLoading&lt;/code&gt;), ob ein Fehler aufgetreten ist (&lt;code&gt;error&lt;/code&gt;) und ob der Request mit Daten beantwortet wurde (&lt;code&gt;hasValue&lt;/code&gt; und &lt;code&gt;value&lt;/code&gt;).&lt;br&gt;
Weiterhin ermöglicht sie es auf einfachstem Wege ein Reload durchzuführen.&lt;/p&gt;




&lt;h1&gt;
  
  
  Fazit
&lt;/h1&gt;

&lt;p&gt;Die ResourceAPI ist ein leistungsstarkes Werkzeug, das die Entwicklung von Angular-Anwendungen vereinfacht, wenn man asynchrone Daten in Ihrer Anwendung verwaltet. Kommen Sie bei Fragen gerne via Kommentare oder direkt über meine &lt;a href="https://angular-freelancer.de" rel="noopener noreferrer"&gt;Website&lt;/a&gt; auf mich zu.&lt;/p&gt;




&lt;h1&gt;
  
  
  Weitere Ressourcen
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://angular.dev/guide/signals/resource" rel="noopener noreferrer"&gt;Offizielle Dokumentationen&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Ausführlichere Beispiele von den &lt;a href="https://www.angulararchitects.io/blog/asynchronous-resources-with-angulars-new-resource-api/" rel="noopener noreferrer"&gt;Angular-Architects&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>angular</category>
      <category>typescript</category>
      <category>webdev</category>
      <category>resourceapi</category>
    </item>
    <item>
      <title>Angular Reactive Forms: Automatische Validation-Hints</title>
      <dc:creator>Fabian - angular-freelancer.de</dc:creator>
      <pubDate>Thu, 21 Nov 2024 18:59:21 +0000</pubDate>
      <link>https://dev.to/angstitc/angular-reactive-forms-automatische-validation-hints-178a</link>
      <guid>https://dev.to/angstitc/angular-reactive-forms-automatische-validation-hints-178a</guid>
      <description>&lt;p&gt;Die Validierung von Formularfeldern gehört für jeden Angular Developer zum Alltag. Das Anzeigen von Hinweisen in Formularen kann aber schnell zu viel repetetivem Boilerplate-Code führen - und den mag bekanntlich niemand.&lt;/p&gt;

&lt;p&gt;Deshalb zeige ich in diesem Beitrag, wie man diesen Boilerplate-Code vermeidet und die Anzeige der Validierungshinweise streamlined - und sogar nachträglich einfach auf bestehenden Reactive Forms anwenden kann.&lt;/p&gt;




&lt;h2&gt;
  
  
  Setup
&lt;/h2&gt;

&lt;p&gt;Bevor wir mit der Implementierung beginnen, richten wir ein neues Angular-Projekt ein. Ich nutze in diesem Beispiel Angular 18.&lt;/p&gt;

&lt;p&gt;Initialisieren des Projekts:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ng new form-autovalidation-demo &lt;span class="nt"&gt;--style&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;scss &lt;span class="nt"&gt;--routing&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;false&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ins Projektverzeichnis wechseln:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;form-autovalidation-demo
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nun erstellen wir die Komponenten / Directives die wir für die Demonstration benötigen:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ng generate component components/login-form
ng generate component components/form-validation-hint
ng generate directive directives/form-auto-validation-hint
ng generate directive directives/form-auto-validation-hint-location
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Jetzt haben wir unser Grundgerüst:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Eine LoginFormComponent, die Repräsentativ für alle weiteren Formulare steht.&lt;/li&gt;
&lt;li&gt;Eine FormValidationHinComponent, welche die Darstellung der Validiation-Hints übernimmt.&lt;/li&gt;
&lt;li&gt;Eine FormAutoValidationHintDirective sowie eine FormAutoValidationHintLocationDirective, welche die Automatisierung der Anzeige übernehmen und es ermöglichen, Fehler an einer spezifischen Stelle und nicht nur Direct am FormControl anzuzeigen.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;Ein kleiner Hinweis: Fürs Aussehen habe ich Tailwind mittels dieser Zeile in der index.html inkludiert:&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"https://cdn.tailwindcss.com"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;Für das richtige Einbinden in produktive Applikationen bitte entsprechend das NPM-Package nutzen. &lt;br&gt;
Das Design der Login Form habe ich von &lt;a href="https://tailwindui.com/components/application-ui/forms/sign-in-forms" rel="noopener noreferrer"&gt;TailwindUI&lt;/a&gt; übernommen.&lt;/em&gt;&lt;/p&gt;


&lt;h2&gt;
  
  
  Das Login-Formular
&lt;/h2&gt;

&lt;p&gt;In der LoginFormComponent erstellen wir ein einfaches Formular mit Reactive Forms, welches die Eingabe von E-Mail und Passwort ermöglicht und die Eingaben validiert:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;login-form.component.ts&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;app-login-form&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;standalone&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;imports&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;ReactiveFormsModule&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;templateUrl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./login-form.component.html&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;styleUrl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./login-form.component.scss&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;LoginFormComponent&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="nx"&gt;fb&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;FormBuilder&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;inject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;FormBuilder&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;form&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;FormGroup&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;form&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="nx"&gt;fb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;group&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;Validators&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;required&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Validators&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;]],&lt;/span&gt;
      &lt;span class="na"&gt;password&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;Validators&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;required&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Validators&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;minLength&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nx"&gt;Validators&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;maxLength&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;32&lt;/span&gt;&lt;span class="p"&gt;)]],&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="nf"&gt;onSubmit&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;form&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;valid&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Form Submitted:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;form&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;form&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;markAllAsTouched&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;login-form.component.html&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"flex min-h-full flex-col justify-center px-6 py-12 lg:px-8"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"sm:mx-auto sm:w-full sm:max-w-sm"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;h2&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"mt-10 text-center text-2xl/9 font-bold tracking-tight text-gray-900"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Login&lt;span class="nt"&gt;&amp;lt;/h2&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"mt-10 sm:mx-auto sm:w-full sm:max-w-sm"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;

    &lt;span class="nt"&gt;&amp;lt;form&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"space-y-6"&lt;/span&gt; &lt;span class="na"&gt;[formGroup]=&lt;/span&gt;&lt;span class="s"&gt;"form"&lt;/span&gt; &lt;span class="na"&gt;(ngSubmit)=&lt;/span&gt;&lt;span class="s"&gt;"onSubmit()"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;div&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;label&lt;/span&gt; &lt;span class="na"&gt;for=&lt;/span&gt;&lt;span class="s"&gt;"email"&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"block text-sm/6 font-medium text-gray-900"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Email address&lt;span class="nt"&gt;&amp;lt;/label&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"mt-2"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"email"&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"email"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"email"&lt;/span&gt; &lt;span class="na"&gt;formControlName=&lt;/span&gt;&lt;span class="s"&gt;"email"&lt;/span&gt; &lt;span class="na"&gt;autocomplete=&lt;/span&gt;&lt;span class="s"&gt;"email"&lt;/span&gt; &lt;span class="na"&gt;required&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm/6"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;

      &lt;span class="nt"&gt;&amp;lt;div&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"flex items-center justify-between"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="nt"&gt;&amp;lt;label&lt;/span&gt; &lt;span class="na"&gt;for=&lt;/span&gt;&lt;span class="s"&gt;"password"&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"block text-sm/6 font-medium text-gray-900"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Password&lt;span class="nt"&gt;&amp;lt;/label&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"mt-2"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"password"&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"password"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"password"&lt;/span&gt; &lt;span class="na"&gt;formControlName=&lt;/span&gt;&lt;span class="s"&gt;"password"&lt;/span&gt; &lt;span class="na"&gt;autocomplete=&lt;/span&gt;&lt;span class="s"&gt;"current-password"&lt;/span&gt; &lt;span class="na"&gt;required&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm/6"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;

      &lt;span class="nt"&gt;&amp;lt;div&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"submit"&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"flex w-full justify-center rounded-md bg-indigo-600 px-3 py-1.5 text-sm/6 font-semibold text-white shadow-sm hover:bg-indigo-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600"&lt;/span&gt; &lt;span class="na"&gt;[disabled]=&lt;/span&gt;&lt;span class="s"&gt;"form.invalid"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Sign in&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/form&amp;gt;&lt;/span&gt;

  &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Implementierung der FormValidationHintComponent
&lt;/h2&gt;

&lt;p&gt;Die FormValidationHintComponent zeigt später die Validierungshinweise eines FormControl basierend auf dessen Validierungsstatus an.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;form-validation-hint.component.ts&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;app-form-hint&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;standalone&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;imports&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt;
  &lt;span class="na"&gt;template&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`
    @if (hints().length &amp;gt; 0) {
      &amp;lt;div class="flex flex-column gap-4 my-2"&amp;gt;
        @for (hint of hints(); track $index) {
            &amp;lt;span class="text-red-600 text-sm leading-4"&amp;gt;{{ hint }}&amp;lt;/span&amp;gt;
        }
      &amp;lt;/div&amp;gt;
  }
  `&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;FormHintComponent&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;hints&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;([]);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Implementierung der Directives
&lt;/h2&gt;

&lt;p&gt;Folgend werden die Directives implementiert, die später die automatisierte Anzeige der Validierungsnachrichten ermöglicht.&lt;/p&gt;

&lt;h3&gt;
  
  
  FormAutoValidationHintLocationDirective
&lt;/h3&gt;

&lt;p&gt;Diese Directive dient als Helfer für die noch zu implementiertende  FormAutoValidationHintDirective. Sie markiert ausschließlich den Container, an welchen der Validierungshinweis angehangen werden soll.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;form-auto-validation-hint-location.directive.ts&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Directive&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;[appFormAutoValidationHintLocation]&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;standalone&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;exportAs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;hintLocation&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;FormAutoValidationHintLocationDirective&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;hintLocationContainerRef&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;inject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ViewContainerRef&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Die Directive kann durch das &lt;code&gt;exportAs&lt;/code&gt; in Templates referenziert werden und stellt die ViewContainerRef nach außen zu Verfügung. Diese wird später dann durch die FormAutoValidationHintDirective genutzt.&lt;/p&gt;

&lt;h3&gt;
  
  
  FormAutoValidationHintDirective
&lt;/h3&gt;

&lt;p&gt;Diese Directive übernimmt die hauptsächliche Arbeit und ist dafür zuständig, Validierungsfehler eines FormControls in lesbare Hinweise zu verwandeln und diese mittels der bereits definierten &lt;code&gt;FormValidationHintComponent&lt;/code&gt; anzuzeigen.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;form-auto-validation-hint.directive.ts&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;VALIDATION_HINTS&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Dieses Feld ist erforderlich.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Bitte geben Sie eine gültige E-Mail-Adresse ein.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;minlength&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Das Feld muss mindestens {requiredLength} Zeichen lang sein.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;maxlength&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Das Feld darf maximal {requiredLength} Zeichen lang sein.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;


&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Directive&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;[appFormAutoValidationHint]&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;standalone&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;FormAutoValidationHintDirective&lt;/span&gt; &lt;span class="k"&gt;implements&lt;/span&gt; &lt;span class="nx"&gt;OnInit&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="nx"&gt;control&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;inject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;NgControl&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="nx"&gt;viewContainerRef&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;inject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ViewContainerRef&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="nx"&gt;injector&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;inject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;Injector&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="nx"&gt;valueChanges&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Signal&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="nx"&gt;statusChanges&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Signal&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;FormControlStatus&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="nx"&gt;formControl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;FormControl&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="nx"&gt;hintRef&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ComponentRef&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;FormHintComponent&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;location&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;FormAutoValidationHintLocationDirective&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;


  &lt;span class="nf"&gt;ngOnInit&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="nx"&gt;control&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Es konnte kein FormControl identifiziert werden, für welches Validierungs-Hints angezeigt werden sollen. Bitte sicherstellen, dass die Directive nur an FormControls eingesetzt wird.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// Value Changes und Status Changes sind im Constructor noch nicht verfügbar, deshalb müssen wir im ngOnInit arbeiten&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="nx"&gt;formControl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="nx"&gt;control&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;unknown&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;FormControl&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nf"&gt;runInInjectionContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="nx"&gt;injector&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="nx"&gt;formControl&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="nx"&gt;valueChanges&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;toSignal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="nx"&gt;formControl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;valueChanges&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="nx"&gt;statusChanges&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;toSignal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="nx"&gt;formControl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;statusChanges&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;

      &lt;span class="nf"&gt;effect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Bei Statusänderung und Wertänderung müssen die Hinweise aktualisiert werden&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="nx"&gt;valueChanges&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="nf"&gt;valueChanges&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;valid&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="nx"&gt;statusChanges&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;valid&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="nf"&gt;statusChanges&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;VALID&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;updateHints&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;valid&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nf"&gt;updateHints&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;valid&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="nx"&gt;hintRef&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="nx"&gt;hintRef&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;location&lt;/span&gt;&lt;span class="p"&gt;()?.&lt;/span&gt;&lt;span class="nx"&gt;hintLocationContainerRef&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="nx"&gt;viewContainerRef&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;createComponent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;FormHintComponent&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;valid&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="nx"&gt;hintRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setInput&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;hints&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[]);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;hints&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;evaluateCurrentHints&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="nx"&gt;hintRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setInput&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;hints&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;hints&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; 

  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nf"&gt;evaluateCurrentHints&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;errors&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="nx"&gt;formControl&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;errors&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;errors&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;errors&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ek&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;messageTemplate&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;VALIDATION_HINTS&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;ek&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;params&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;errors&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;ek&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;messageTemplate&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replaceParamPlaceholder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;messageTemplate&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`Bitte überprüfen Sie ihre Eingaben. Unbekannter Fehler: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;ek&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;      
    &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nf"&gt;replaceParamPlaceholder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;template&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="p"&gt;{[&lt;/span&gt;&lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="p"&gt;}):&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;template&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;\{(\w&lt;/span&gt;&lt;span class="sr"&gt;+&lt;/span&gt;&lt;span class="se"&gt;)\}&lt;/span&gt;&lt;span class="sr"&gt;/g&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;params&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Die Directive greift via Injection auf das Form Control zu und reagiert auf Status- und Value-Änderungen. Hierbei greift Sie auf die Errors des Controls zu und nutzt die Namen der Errors um die Fehlertexte (in VALIDATION_HINTS definiert, können später erweitert oder von Außen definiert werden, je nachdem was die individuellen Anforderungen verlangen) zu identifizieren. Sollten die Fehlertexte Platzhalter enthalten, werden diese mit den Werten aus den Error-Details ersetzt, sofern diese Dort vorhanden sind.&lt;/p&gt;

&lt;p&gt;Beispiel:&lt;/p&gt;

&lt;p&gt;Das Passwort muss 6-32 Zeichen lang sein. Die Eingabe "Pa55W" führt zum folgenden Validierungsfehler:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"minlength"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"requiredLength"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"actualLength"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Die Information der requiredLength wird somit aus dem Error-Object in den Text eingesetzt.&lt;/p&gt;




&lt;h2&gt;
  
  
  Nutzung der Directiven
&lt;/h2&gt;

&lt;p&gt;Folgend nutzen wir die implementierten Directives im Login Formular, um die Validierungen der Formulare sichtbar zu machen.&lt;/p&gt;

&lt;h3&gt;
  
  
  Anzeige direkt am FormControl
&lt;/h3&gt;

&lt;p&gt;Um die Validierungshinweise direkt an einem FormControl anzuzeigen, muss diese nur an dem FormControl angebracht werden. Folgend am Beispiel der E-Mail im Login-Formular:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;login-form.component.html&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!-- ... --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;appFormAutoValidationHint&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"email"&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"email"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"email"&lt;/span&gt; &lt;span class="na"&gt;formControlName=&lt;/span&gt;&lt;span class="s"&gt;"email"&lt;/span&gt; &lt;span class="na"&gt;autocomplete=&lt;/span&gt;&lt;span class="s"&gt;"email"&lt;/span&gt; &lt;span class="na"&gt;required&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm/6"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="c"&gt;&amp;lt;!-- ... --&amp;gt;&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Anzeige an beliebiger Stelle
&lt;/h3&gt;

&lt;p&gt;Um die Validierungshinweise an anderer Stelle anzuzeigen, müssen wir der &lt;code&gt;FormAutoValidationHintDirective&lt;/code&gt; sagen, an welcher Stelle der Hinweis angezeigt werden soll. Hierzu nutzen wir die &lt;code&gt;FormAutoValidationHintLocationDirective&lt;/code&gt; und weißen die Referenz auf diese der &lt;code&gt;FormAutoValidationHintDirective&lt;/code&gt; zu. Folgend am Beispiel des Passworts im Login-Formular:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;login-form.component.html&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!-- ... --&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;div&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"flex items-center justify-between"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="nt"&gt;&amp;lt;label&lt;/span&gt; &lt;span class="na"&gt;for=&lt;/span&gt;&lt;span class="s"&gt;"password"&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"block text-sm/6 font-medium text-gray-900"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Password&lt;span class="nt"&gt;&amp;lt;/label&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"mt-2"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;appFormAutoValidationHint&lt;/span&gt; &lt;span class="na"&gt;[location]=&lt;/span&gt;&lt;span class="s"&gt;"loc"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"password"&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"password"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"password"&lt;/span&gt; &lt;span class="na"&gt;formControlName=&lt;/span&gt;&lt;span class="s"&gt;"password"&lt;/span&gt; &lt;span class="na"&gt;autocomplete=&lt;/span&gt;&lt;span class="s"&gt;"current-password"&lt;/span&gt; &lt;span class="na"&gt;required&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm/6"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;

      &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;appFormAutoValidationHintLocation&lt;/span&gt; &lt;span class="na"&gt;#loc&lt;/span&gt;&lt;span class="err"&gt;="&lt;/span&gt;&lt;span class="na"&gt;hintLocation&lt;/span&gt;&lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"submit"&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"flex w-full justify-center rounded-md bg-indigo-600 px-3 py-1.5 text-sm/6 font-semibold text-white shadow-sm enabled:hover:bg-indigo-500 enabled:focus-visible:outline enabled:focus-visible:outline-2 enabled:focus-visible:outline-offset-2 enabled:focus-visible:outline-indigo-600 disabled:opacity-75"&lt;/span&gt; &lt;span class="na"&gt;[disabled]=&lt;/span&gt;&lt;span class="s"&gt;"form.invalid"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Sign in&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;

&lt;span class="c"&gt;&amp;lt;!-- ... --&amp;gt;&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Die Validierungshinweise des Passworts werden nun unterhalb des Buttons angezeigt.&lt;/p&gt;

&lt;h2&gt;
  
  
  Halbautomatisch auf bestehende Formulare applizieren
&lt;/h2&gt;

&lt;p&gt;Wenn Ihre Applikation bereits über Formulare verfügt, die noch keine Hinweise enthält, Sie diese aber konsistent und einfach verteilen möchten, können Sie das relativ einfach erreichen. Hierzu müssen wir die &lt;code&gt;FormAutoValidationHintDirective&lt;/code&gt; um zwei Selector erweitern.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;form-auto-validation-hint.directive.ts&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Directive&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;[appFormAutoValidationHint], [formControl], [formControlName]&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;standalone&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Durch die Selektoren &lt;code&gt;formControl&lt;/code&gt; und &lt;code&gt;formControlName&lt;/code&gt; wird die Directive automatisch überall dort angewandt, wo FormControls via Reactive Forms genutzt werden. Die Directive muss nun nur in den einzelnen Components oder der Globalen Configuration als Provider angegeben werden. Nun werden die Hinweise in jedem Formular angezeigt. Ggf. muss Ihr Styling jedoch noch angepasst werden.&lt;/p&gt;




&lt;h2&gt;
  
  
  Fazit
&lt;/h2&gt;

&lt;p&gt;Mit dieser Anpassung werden Validierungsfehler direkt am jeweiligen Eingabeelement angezeigt, ohne dass zusätzlicher Template-Code notwendig ist. Der Ansatz kombiniert die Stärken von Angular-Direktiven und Reactive Forms, um sowohl Entwicklerfreundlichkeit als auch Wartbarkeit zu maximieren.&lt;/p&gt;

&lt;p&gt;Probieren Sie es aus und passen Sie die Logik bei Bedarf an Ihre spezifischen Anforderungen an! &lt;/p&gt;

&lt;p&gt;Brauchen Sie Unterstützung? &lt;a href="https://angular-freelancer.de/" rel="noopener noreferrer"&gt;Kommen Sie gerne auf mich zu!&lt;/a&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>angular</category>
      <category>productivity</category>
      <category>typescript</category>
    </item>
  </channel>
</rss>
