Erfahre in diesem Artikel, wie du Content Projection in Angular effektiv nutzt. Von den Grundlagen bis zu fortgeschrittenen Techniken – praktische Beispiele, um deine Komponenten flexibel und wartbar zu gestalten.
Hast du dich schon oft gefragt, welche Struktur für Angular-Komponenten in deinem Projekt am sinnvollsten ist?
Oder warum der Datenfluss mit der Zeit immer unübersichtlicher wird?
Vielleicht fragst du dich, ob ein @Input()
für eine bestimmte Komponente wirklich notwendig ist – oder welche Alternativen es gibt?
Dann solltest du einen genaueren Blick auf ein nützliches Werkzeug im Angular-Werkzeugkasten werfen: die Content Projection mit <ng-content/>
.
Im Angularkontext beschreibt Content Projection oder auf Deutsch Inhaltsprojektion eine Technik für das Einfügen von Inhalten, ausgehend von einer übergeordneten Elternomponente, in eine untergeordnete Kindkomponente 1.
Im Vergleich zum @Input
-Dekorator bietet Content Projection den Vorteil, dass eine größere Vielfalt an Elementen in die Kind-Komponente übergeben werden kann.
Mögliche Inhalte:
Um sicherzustellen, dass wir alles verstehen, schauen wir uns ein praktisches Beispiel an.
In der folgenden app.component.ts
-Datei haben wir eine Elternkomponente, die die HeadlineComponent (Kindkomponente) verwendet.
@Component({
selector: 'app-root',
standalone: true,
imports: [HeadlineComponent],
template: '<app-headline>Example Headline</app-headline>',
styleUrl: './app.component.scss'
})
export class AppComponent {}
Der Inhalt Example Headline
wird mithilfe von Content Projection in die <app-headline>
-Komponente projiziert.
@Component({
selector: 'app-headline',
standalone: true,
imports: [],
template: '<h1><ng-content></ng-content></h1>',
styleUrl: './headline.component.scss'
})
export class HeadlineComponent {}
Das Resultat im DOM ist ein <h1>
-Tag, mit Example Headline
als Inhalt.
<h1>Content Projection</h1>
Falls du dir den Code selbst anschauen möchtest, findest du ihn in diesem Repository, auf dem examples/basic Branch.
Manchmal ist es notwendig, komplexere Layouts zu erstellen, die unterschiedliche Teile von Inhalten an verschiedenen Stellen rendern. Angular bietet hierfür die Möglichkeit, mehrere Inhalte in eine Komponente zu injizieren, indem man mehrere <ng-content>
-Platzhalter verwendet.
Die unten stehenden Beispiele sind ebenfalls bei GitHub zu finden.
Komponente mit mehreren <ng-content>
-Elementen:
<div class="card">
<h1>
<ng-content select="[card-title]"></ng-content>
</h1>
<ng-content select="card-body"></ng-content>
<ng-content select=".card-footer"></ng-content>
</div>
Wir erkennen das uns bekannte <ng-content>
.
Hier wird das select
-Attribut verwendet, um spezifische Inhalte in die entsprechenden Platzhalter zu projizieren. Beispiel für die Verwendung:
<app-custom-card>
<card-title>Hello</card-title>
<card-body>Welcome to the example</card-body>
</app-custom-card>
Es gibt mehrere Möglichkeiten zu bestimmen, welches <ng-content>
-Element verwendet wird.
Zum einen können wir, wie im oberen Beispiel, mit <card-title>
und <card-body>
die select
-Attribute des jeweiligen <ng-content>
referenzieren.
Es ist wichtig zu beachten, dass die Elternkomponente CUSTOM_ELEMENTS_SCHEMA
als schemas
definiert. Da Angular sonst nach einem Element mit dem Selektor <card-title>
suchen würde. Wir teilen Angular also mit, dass es sich um valide Elemente handelt.
@Component({
...
schemas: [CUSTOM_ELEMENTS_SCHEMA]
})
<ng-content select="[card-title]"></ng-content>
<div card-title>
Card Title
</div>
<ng-content select=".card-title"></ng-content>
<div class="card-title">
Card Title
</div>
Bis hierhin haben wir die Grundlagen der Content Projection in Angular behandelt und anhand von Beispielen gezeigt, wie Inhalte von einer Elternkomponente in eine Kindkomponente projiziert werden können.
Wir haben die einfache Verwendung von <ng-content>
erläutert und gesehen, wie man mehrere Platzhalter in einer Komponente definiert, um verschiedene Inhalte gezielt einzufügen.
Im Folgenden legen wir den Fokus darauf, wann es Sinn macht, diese Technik zu nutzen.
Denn nur weil wir mit <ng-content>
sprichwörtlich einen Hammer in der Werkezugkiste von Angular gefunden haben, bedeutet dies nicht, dass nun alles ein Nagel ist.
<ng-content>
kann die erstellte Komponente verschiedenste Inhalte aufnehmen (Siehe Card-Beispiel).@Input()
und @Output()
. Dies macht deinen Code übersichtlicher, hält Datenströme kurz und ist wartbarer für dich und dein Team.<ng-content>
keinen Vorteil.Neben den grundlegenden Anwendungsfällen gibt es noch einige zusätzliche Aspekte von <ng-content>
, die du kennen solltest.
<ng-content>
sollte nicht bedingt mit @If
etc. eingebunden werden, da Angular die DOM-Elemente auch für die nicht sichtbaren Platzhalter erstellt 1.
Es ist möglich, einen Default-Value für <ng-content>
zu setzen. Dieser wird, wie in dem Beispiel weiter unten, zwischen das öffnende und schließende ng-content-tag gesetzt.
Der Default wird angezeigt, wenn es keinen Inhalt gibt, der dem angegebenen select
eines <ng-content>
entspricht.
<div class="card">
<h1>
<ng-content select="[card-title]">DEFAULT-Value</ng-content>
</h1>
<ng-content select="card-body"></ng-content>
<ng-content select=".card-footer"></ng-content>
</div>
Weiterhin ist es möglich ein fallback <ng-content>
zu nutzen. Wenn wir wieder auf das Card-Beispiel schauen, sehen wir, dass es mehrere ng-content-Blöcke mit einem select
gibt. Versucht die Elternkomponente Inhalt zu projizieren, der nicht einem der select
entspricht, wird dieser nicht angezeigt.
Dieses Problem lässt sich durch ein einfaches Hinzufügen von <ng-content/>
beheben. Der projizierte Inhalt der keinem select
zugeordnet werden kann, wird in diesen fallback <ng-content></ng-content>
proijiziert.
Mithilfe von ngProjectAs
können wir einen CSS-Selektor angeben. Immer wenn ein Element mit ngProjectAs
gegen einen <ng-content>
-Platzhalter geprüft wird, vergleicht Angular mit dem ngProjectAs
-Wert anstelle der Identität des Elements. In diesem Fall wird der card-body
als ein h3
HTML-Element gerendert werden 1.
<app-custom-card>
<div card-title>
Title
</div>
<h3 ngProjectAs="card-body">Hello</h3>
<div class="card-footer">Footer</div>
</app-custom-card>
<ng-content>
-Element handelt es sich weder um eine Komponente, noch um ein DOM-Element. Es ist ein spezieller Platzhalter, der Angular aufzeigt, wo Inhalte gerendert werden sollen.