
Architecting Angular Applications - Teil 3: Directives
Veröffentlicht am 28. Juni 2025 - ⏱️ Ca. (wird berechnet) min. Lesezeit
Im letzten Teil haben wir uns mit Components beschäftigt. Dem wohl verbreitetsten Baustein in Angular. Direktiven aber sind ein ganz anderes Blatt. Sie werden von vielen Entwicklern gerne gemieden. Das ist verständlich, denn sie wirken zunächst unsichtbar und schwer greifbar. Oft entsteht der Eindruck, sie seien irgendwie fehl am Platz. Doch wer sich mit ihnen beschäftigt, findet darin ein erstaunlich präzises Werkzeug, das an ungewohnter Stelle zum Einsatz kommt.
Im Angular-Framework nehmen Direktiven eine besondere Rolle ein. Sie besitzen kein eigenes Template und durchlaufen nicht den typischen Lebenszyklus einer Komponente. Trotzdem sind sie tief mit dem Fundament von Angular verwoben. Gerade weil sie so wenig sichtbar sind, werden sie häufig missverstanden.
Es ist fast tragisch, dass sie so oft übersehen werden. Denn viele spüren den Bedarf an einer Direktive, vergessen nur, dass das Werkzeug dafür da wäre. Wer hat nicht schonmal ein Feature gebaut, welches er immer wieder im HTML braucht. Aber es passt nicht als eigene Komponente, Content Projection fühlt sich falsch an. Ein Service passt sowieso nicht, denn wir sind im HTML. Eine Pipe? Nein, es wird ja nichts transformiert. Also wird doch duplizierter Code in Kauf genommen oder merkwürdige Komponenten erstellt. Denn die Directive wird gern vergessen. Leider birgt sie auch Risiken.
In einem Projekt führte eine Direktive dazu, dass beim Scrollen automatisch API-Requests ausgelöst wurden. Keiner der Entwickler bemerkte das sofort. Die Logik versteckte sich im Markup, ohne dass dies nach außen hin erkennbar gewesen wäre. Die Folge: Es wurden Daten geladen, obwohl niemand diese Anfragen beabsichtigt hatte. Und das sehr gut versteckt. Man kann sich mit Direktiven also leicht ein Bein stellen.
Direktiven erinnern in ihrer Unsichtbarkeit an CSS-Klassen. Sie fallen optisch kaum auf, haben aber einen deutlichen Einfluss auf das Verhalten der Anwendung. Während CSS allerdings nur das Aussehen beeinflusst, greifen Direktiven direkt in das Verhalten ein. Wer das aus dem Blick verliert, riskiert, schwer durchschaubare Seiteneffekte zu erzeugen. Das steht im Widerspruch zu den Prinzipien einer sauberen Architektur.
Die Ambivalenz von Direktiven: Präzision vs. Magie
Direktiven sind ein zweischneidiges Schwert: Sie können das Verhalten von Elementen auf elegante, präzise Weise erweitern oder aber für schwer durchschaubare Magie sorgen.
Ihre größte Stärke ist die Möglichkeit, technisches Verhalten direkt am Element zu deklarieren. Das macht Templates schlanker und sorgt für eine hohe Wiederverwendbarkeit. Doch gerade diese Unsichtbarkeit birgt Risiken: Direktiven können Logik verstecken, Seiteneffekte auslösen oder die Architektur undurchsichtig machen.
Die Kunst besteht darin, Direktiven so einzusetzen, dass sie ihr Potenzial entfalten, ohne dabei zur Blackbox zu werden. Sie sollten immer explizit sein, klar benannt und einfach zu verstehen. Sobald eine Direktive beginnt, Logik zu kapseln, die nicht mehr auf einen Blick erkennbar ist, droht sie zur „magischen“ Lösung zu werden. Das widerspricht dann den Prinzipien einer nachhaltigen Architektur.
Architektur Prinzip: Direktiven sind mächtig, aber gefährlich. Sie verlangen Disziplin und ein klares Ziel, sonst werden sie zur Quelle von Magie und Verwirrung.
Einordnung: Was Direktiven eigentlich sind
Direktiven sind darauf ausgelegt, Verhalten direkt auf DOM-Ebene zu kapseln. Sie übernehmen keine Zustandsverwaltung, enthalten keine Geschäftslogik und binden keine Services ein. Ihr Auftrag ist eindeutig: Sie sollen das HTML um technisches Verhalten ergänzen, jedoch nicht die eigentliche Anwendung erweitern.
Eine gute Direktive bleibt stets klein und konzentriert sich auf genau eine Aufgabe. Sie ist für technische Ergänzungen gedacht und nicht für semantische Erweiterungen. Laut offizieller Angular-Dokumentation verändern Structural Directives die Struktur des DOM, indem sie Elemente hinzufügen oder entfernen.
Soweit zur Theorie. Eine etwas anschaulichere Beschreibung: Denke eine Direktive als eine Art Decorator fürs HTML. Das macht das Potenzial direkt klarer. Decorator sind eine fantastische, geniale Erfindung. Mit Ihnen lassen sich Funktionen super einfach wiederverwendbar erweitern. Wenn man an eine API denkt, dann hat man dort einen Endpunkt: getUsers. Der soll nur von admins genutzt werden können. Die Lösung: Ein Decorator! hasPermission ist ein Klassiker, der sich super wartbar und lesbar auf jeden Endpunkt bei Bedarf annotieren lässt. Und dieses Beispiel lässt sich eins zu eins auf Direktiven übertragen. Mit dieser Analogie im Kopf lässt es sich auch viel einfacher und praxisrelevanter Beispiele für gelungene Direktiven finden:
hasPermission
: Blendet Elemente abhängig von Berechtigungen aus.observeResize
: Meldet Größenänderungen, ohne selbst eine Darstellung zu besitzen.debounceInput
: Reduziert die Anzahl der ausgelösten Events in Eingabefeldern.
In solchen Szenarien sind Direktiven das passende Mittel. Wichtig ist dabei, dass sie keine Seiteneffekte erzeugen, keine Geschäftslogik enthalten und keinen eigenen Zustand verwalten.
Direktiven und Pipes. Eine klare Abgrenzung
Auf den ersten Blick erscheinen Direktiven und Pipes ähnlich, da beide das Erscheinungsbild von Inhalten im Template verändern. Doch ihr Zweck und ihr Wirkungsbereich unterscheiden sich grundlegend.
Pipes dienen dazu, Daten direkt im Ausdruck zu transformieren. Sie wandeln zum Beispiel ein Datum um oder formatieren eine Zahl. Dabei bleiben sie immer deterministisch. Pipes verursachen keine Nebenwirkungen, enthalten keine Logik außerhalb ihres Ausdrucks und greifen niemals auf das DOM zu.
Direktiven hingegen wirken von außen auf das DOM ein. Sie steuern das Verhalten von Elementen, können diese ausblenden, Events binden oder Inhalte dynamisch erzeugen. Ihr Einfluss ist damit strukturell.
Diese Unterscheidung ist nicht nur technischer Natur. Sie sorgt dafür, dass Zuständigkeiten klar getrennt bleiben. Wer Pipes für Logik missbraucht oder Direktiven mit Datenmanipulation beauftragt, riskiert unvorhersehbares Verhalten, schwer testbare Abhängigkeiten und eine schlechtere Wartbarkeit des Codes.
Architektur Prinzip: Pipes gestalten das Was. Direktiven steuern das Wie.
Eine einfache Faustregel hilft bei der Abgrenzung: Pipes bestimmen, wie Daten dargestellt werden. Direktiven steuern, wie sich Elemente im DOM verhalten.
Wo Direktiven fehlen, obwohl sie sinnvoll wären
Falsch eingesetzte Direktiven sind ärgerlich. Mindestens genauso problematisch ist jedoch das Fehlen von Direktiven an Stellen, wo sie technisch sinnvoll wären. Stattdessen werden häufig Komponenten oder Services bemüht, was die Komplexität unnötig erhöht.
Ein klassisches Beispiel sind Eingabefelder, bei denen das Drücken der Enter-Taste ein Formular abschicken soll. Dieses Verhalten wird in vielen Anwendungen immer wieder neu implementiert. Jedes Mal, wenn es benötigt wird, fügt man in der jeweiligen Komponente eigene Listener hinzu. Dabei könnte eine kleine Direktive diese Aufgabe viel eleganter übernehmen:
@Directive({ selector: '[submitOnEnter]' })
export class SubmitOnEnterDirective {
@HostListener('keydown.enter', ['$event.target'])
onEnter(el: HTMLElement) {
el.closest('form')?.dispatchEvent(new Event('submit', { bubbles: true }));
}
}
Auch bei wiederkehrenden UI-Interaktionen wie der Beobachtung von Größenänderungen fehlt oft eine
passende Lösung. Häufig werden Services zweckentfremdet, Utility-Funktionen eingeführt oder sogar
Vererbung bemüht. Dabei wäre eine Direktive wie observeResize
viel passender: Sie übernimmt
ausschließlich das gewünschte Verhalten, ohne Darstellung oder zusätzliche Struktur.
Direktiven bieten sich immer dann an, wenn Verhalten nicht dominiert, sondern lediglich ergänzt werden soll. Besonders relevant ist das in folgenden Fällen:
- DOM-Events, die erweitert oder abgefangen werden sollen
- Verhalten, das unabhängig von Layout und Komponentenhierarchie funktioniert
- Technische Aufgaben wie Scrollverhalten oder Resize-Observer
Wer in diesen Situationen eine Komponente erstellt, erzeugt unnötige Komplexität. Wer stattdessen auf einen Service zurückgreift, koppelt globale Logik an etwas, das eigentlich lokal bleiben sollte. Direktiven hingegen sitzen direkt am Element und erfüllen genau diesen Zweck.
Architektur Prinzip:: Eine Direktive ersetzt nicht, sondern ergänzt. Wer Zustand oder Logik braucht, baut besser eine Komponente.
Gerade solche Funktionalitäten führen häufig zu doppeltem Code in der Anwendung. Entwickler merken schnell, dass dieser Code eigentlich fehl am Platz ist. Es handelt sich weder um klassische Service-Logik noch um reine Präsentationslogik. Oft landet er deshalb in Komponenten, obwohl eine Direktive die bessere Lösung gewesen wäre. Leider wird diese Möglichkeit häufig übersehen.
Viele dieser Schwierigkeiten sind keine Eigenheiten von Angular, sondern verstoßen gegen ein grundlegendes Architekturprinzip: „Explicit is better than implicit.” Dieser Leitsatz stammt aus dem Zen of Python (PEP 20) und gilt unabhängig vom Framework. Wer Verhalten versteckt oder verkapselt, ohne es klar sichtbar zu machen, erhöht das Risiko unbeabsichtigter Seiteneffekte. Eine Direktive, die beispielsweise API-Aufrufe auslöst oder Geschäftslogik übernimmt, untergräbt die Erwartbarkeit des Codes.
Architektur Prinzip: Direktiven sollen Verhalten sichtbar machen und nicht verstecken. Was sich nicht erklären lässt, gehört nicht in ein Attribut.
Beispiele für gelungene Structural Directives
Structural Directives wie *ngIf
oder *ngFor
sind aus dem Angular-Alltag nicht
wegzudenken. Es lohnt sich jedoch, eigene Varianten zu entwickeln, die spezifische Anwendungsfälle
abbilden und dabei genauso lesbar und selbsterklärend bleiben. Besonders überzeugend sind Structural
Directives immer dann, wenn sie ein klar verständliches Signal senden. So wird auf den ersten Blick
deutlich, unter welcher Bedingung ein Element angezeigt oder entfernt wird.
Ein prominentes Beispiel aus dem Angular-Ökosystem demonstriert eindrucksvoll das Potenzial struktureller Direktiven:
Die *cdkVirtualFor
-Direktive sorgt dafür, dass auch sehr große Listen performant gerendert
werden,
indem stets nur die sichtbaren Elemente im DOM gehalten werden. Selbst bei tausenden von Einträgen
bleibt das Template übersichtlich und gut lesbar:
<cdk-virtual-scroll-viewport itemSize="50" class="viewport">
<div *cdkVirtualFor="let item of items">{{item}}</div>
</cdk-virtual-scroll-viewport>
*cdkVirtualFor
funktioniert ähnlich wie *ngFor
, entscheidet aber eigenständig,
welche Elemente im DOM sichtbar sind. Dieses Beispiel zeigt, wie eine Structural Directive das Verhalten
deklarativ erweitert und dabei für das Template vollkommen transparent bleibt.
Architektur Prinzip: Hier kapselt eine Direktive ein verhaltensabhängiges Rendering-Muster und ermöglicht dadurch deklarative Klarheit an der Oberfläche.
Auch abseits von Rendering-Optimierungen bieten Direktiven elegante Lösungen für wiederkehrende UI-Logik.
Ein weiteres gelungenes Beispiel ist die *hasPermission
-Direktive. Sie sorgt dafür, dass
die Berechtigungsprüfung genau dort stattfindet, wo sie hingehört: direkt am betroffenen Element.
Dadurch entfällt es, in jeder einzelnen Komponente neue Bedingungen zu formulieren oder View-Logik mit
Berechtigungswissen zu vermischen.
Ein Element wird so nur dann angezeigt, wenn der Nutzer über die entsprechende Berechtigung verfügt. Die gesamte Logik bleibt in der Direktive gekapselt. Im Template reicht ein einziges Attribut:
<button *hasPermission="'admin.create'">Anlegen</button>
Ohne eine solche Direktive wird das Template schnell unübersichtlich. Entwickler müssen entweder wiederholt prüfen, ob die Berechtigung vorliegt, oder die Logik auslagern und in Services oder Komponenten doppeln. Beides erhöht die Komplexität und erschwert das Verständnis. Die Direktive bleibt dagegen schlank, wiederverwendbar und macht die Absicht sofort deutlich.
Auch hier gilt: Die Direktive löst keinen Seiteneffekt aus, sondern liest lediglich den Zustand aus und blendet das Element gegebenenfalls aus. So bleibt die Trennung zwischen Darstellung und Geschäftslogik gewahrt.
Noch ein Beispiel für eine gelungene Structural Directive ist *ifNative
. Sie sorgt
dafür, dass ein Element nur dann angezeigt wird, wenn die Anwendung in einer nativen Umgebung läuft.
Besonders in hybriden Angular-Anwendungen, etwa bei Ionic-Projekten, kann das sinnvoll sein. Oft muss eine Komponente unterschiedlich reagieren, je nachdem, ob sie in einer nativen App oder im Browser ausgeführt wird. Während Services für die technische Kapselung sorgen, kann eine Structural Directive das Template gezielt aufteilen. So wird beispielsweise ein nativer Button nur dann eingeblendet, wenn er tatsächlich verwendet werden kann.
<native-toolbar *ifNative></native-toolbar>
Die Entscheidung, ob die App in einer nativen Umgebung läuft, bleibt so an einer zentralen, wartbaren
und gut testbaren Stelle. Es ist nicht mehr nötig, überall im Code Bedingungen wie if
(Capacitor.getPlatform() === 'ios')
zu verteilen. Die Direktive kapselt diese Logik an einer
einzigen Stelle.
Auf diese Weise lässt sich das DOM gezielt verschlanken, und die Benutzeroberfläche bleibt robust gegenüber plattformspezifischen Besonderheiten. Wichtig ist dabei: Die Direktive entscheidet darüber, ob ein Element angezeigt wird, aber niemals was dargestellt wird.
Der stille Rückzug der Direktiven
Seit Angular 17 gibt es mit der neuen Control-Flow-Syntax @if () {}
und @for ()
{}
eine sichtbare Veränderung im Aufbau der Templates. Bewährte Direktiven wie *ngIf
oder *ngFor
werden nun durch diese eingebettete Syntax ersetzt. Das Angular-Team verfolgt
damit das Ziel, weniger Magie und mehr Vorhersehbarkeit zu schaffen. Der HTML-Code wird dadurch
linearer, klarer strukturiert und die Trennung zwischen Struktur und Inhalt wieder deutlicher.
Das Angular-Team benennt drei entscheidende Kritikpunkte an der Mikrosyntax (*ngIf
, *ngFor
):
-
Typprüfung und Lesbarkeit leidet
Die zusammengesetzte Syntax (*let
,trackBy
,ng-template
) erzeugt unklare Templates und erschwert das korrekte Typisieren im Compiler. -
Signal-Komponenten brauchen neue Basis
In einer zonenfreien Architektur, etwa mit Signals, ist die alte Struktur nicht tragfähig. Angular entkoppelt sich jetzt sauber davon. -
Bessere Toolchain und Codegenerierung
Die neue@if
/@for
-Syntax ermöglicht klareren, effizienteren Code und bessere Unterstützung durch IDEs und Compiler.
Angular verlagert strukturelle Aufgaben zunehmend in die eingebettete Syntax. Dennoch bleiben Direktiven ein zentrales Werkzeug, wenn Verhalten modularisiert, gekapselt und wiederverwendbar werden soll. Die neue Syntax macht bestimmte Direktiven zwar implizit, aber sobald komplexere Bedingungen, Policies oder plattformabhängiges Verhalten erforderlich sind, kommt man auch in Zukunft nicht ohne eigene Direktiven aus.
Doch in der Anpassung von Angular steckt auch Kritik an Direktiven und diese Kritik ist nicht von der Hand zu weisen. Direktiven verführen sehr leicht und sorgen schnell für Magie. Nicht zuletzt deswegen lassen viele ihre Finger von Direktiven. Es erfordert sehr viel Disziplin und Fokus, damit Direktiven nicht zu undurchsichtig und magisch werden. Daher im Folgenden einige negativ Beispiele.
Typische Negativ-Beispiele
Direktiven können subtil zur Fehlerquelle werden, wenn sie zu viel Verantwortung übernehmen, unklar benannt sind oder unerwartete Effekte auslösen. Hier einige typische, weniger offensichtliche Negativ-Beispiele – jeweils mit einer besseren Alternative:
1. Unklare Namensgebung und Verhalten
Problem: Eine Direktive mit schwammigem Namen und unklarem Effekt macht das Template schwer verständlich.
<div magicEffect></div>
Es ist nicht ersichtlich, was magicEffect
tatsächlich bewirkt – verändert sie das Aussehen,
das Verhalten oder löst sie sogar Seiteneffekte aus?
Besser so: Die Direktive sollte klar benannt sein und ihr Verhalten explizit machen.
<div autoRefresh></div>
2. Versteckte Seiteneffekte im Hintergrund
Problem: Eine Direktive löst im Hintergrund Aktionen aus, die im Template nicht sichtbar sind.
<div autoRefresh></div>
Hier werden eventuell regelmäßig Daten nachgeladen, ohne dass dies für Entwickler oder Nutzer im Markup transparent ist.
Besser so: Seiteneffekte sollten klar signalisiert und steuerbar sein.
<div [autoRefresh]="intervalInMs" (refresh)="reloadData()"></div>
3. Direktiven, die native Features duplizieren
Problem: Eine Direktive ersetzt ein vorhandenes HTML-Feature ohne Mehrwert.
<input customDisabled>
Das ist verwirrend, wenn stattdessen das native disabled
-Attribut genutzt werden kann.
Besser so: Nutze native HTML-Features, wann immer möglich.
<input disabled>
5. Direktiven, die zu viel auf einmal machen
Problem: Eine Direktive übernimmt Styling, Event-Handling und Logik gleichzeitig.
<div appAllInOne></div>
Das erschwert Wartung und Wiederverwendung. Die Verantwortlichkeiten sind nicht sauber getrennt.
Besser so: Zerlege komplexe Aufgaben in mehrere spezialisierte Direktiven oder nutze eine Komponente.
<div [resizeObserver] [style.color]="user.color" (click)="onSelect()"></div>
Fazit: Was verloren geht, wenn man Direktiven meidet
Directives sind kein technisches Relikt, sondern ein architektonisches Werkzeug für präzise, wiederverwendbare Logik, das in der Angular-Praxis viel zu oft ignoriert wird. Wer Komponenten reflexhaft allem vorzieht, entscheidet sich nicht nur für Sichtbarkeit, sondern auch gegen Trennung von Verantwortung.
Gute Architektur verlangt manchmal den unbequemen Weg: bewusst und reduziert. Wer Angular nur über Komponenten denkt, verschenkt Potenzial. Wer sich auf Directives einlässt, gewinnt eine Ausdrucksstärke, die über reines Styling hinausgeht und beginnt, Architektur wirklich zu gestalten.