Alles, was Sie über ng-template, ng-content, ng-container und *ngTemplateOutlet in Angular wissen müssen
Es war einer dieser Tage, an denen ich mit der Arbeit an neuen Funktionen für mein Büroprojekt beschäftigt war. Plötzlich erregte etwas meine Aufmerksamkeit:
Bei der Inspektion des DOM sah ich das ngcontent
, das von Angular auf Elemente angewendet wird. Hmm… wenn sie die Elemente im endgültigen DOM enthalten, was ist dann der Nutzen von <ng-container>
? Damals kam ich zwischen <ng-container>
und <ng-content>
durcheinander.
Auf der Suche nach den Antworten auf meine Fragen entdeckte ich das Konzept von <ng-template>
. Zu meiner Überraschung gab es auch *ngTemplateOutlet
. Zu Beginn meiner Reise suchte ich Klarheit über zwei Konzepte, aber jetzt hatte ich vier davon, die fast gleich klangen!
Haben Sie sich jemals in dieser Situation befunden? Wenn ja, dann sind Sie hier richtig. Also gehen wir sie kurzerhand einzeln durch.
<ng-template>
Wie der Name schon sagt, ist das <ng-template>
ein Template-Element, das Angular mit strukturellen Direktiven (*ngIf
, *ngFor
, und benutzerdefinierten Direktiven) verwendet.
Diese Template-Elemente funktionieren nur in Gegenwart von strukturellen Direktiven. Angular wickelt das Host-Element (auf das die Direktive angewandt wird) innerhalb von <ng-template>
ein und konsumiert das <ng-template>
im fertigen DOM, indem es durch diagnostische Kommentare ersetzt wird.
Betrachten wir ein einfaches Beispiel für *ngIf
:
Die obige Abbildung zeigt die Angular-Interpretation von *ngIf
. Angular fügt das Host-Element, auf das die Direktive angewendet wird, in <ng-template>
ein und behält das Host-Element unverändert bei. Das endgültige DOM ähnelt dem, das wir zu Beginn dieses Artikels gesehen haben:
Verwendung:
Wir haben gesehen, wie Angular <ng-template>
verwendet, aber was, wenn wir es verwenden wollen? Da diese Elemente nur mit einer strukturellen Direktive funktionieren, können wir schreiben:
Hier ist home
eine boolean
Eigenschaft der Komponente, die auf true
Wert gesetzt ist. Die Ausgabe des obigen Codes im DOM:
Nichts wurde gerendert! 🙁
Aber warum können wir unsere Nachricht nicht sehen, obwohl wir <ng-template>
korrekt mit einer strukturellen Direktive verwenden?
Das war das erwartete Ergebnis. Wie wir bereits besprochen haben, ersetzt Angular die <ng-template>
durch diagnostische Kommentare. Zweifelsohne würde der obige Code keinen Fehler erzeugen, da Angular mit Ihrem Anwendungsfall bestens zurechtkommt. Sie würden nie erfahren, was genau hinter den Kulissen passiert ist.
Lassen Sie uns die beiden obigen DOMs vergleichen, die von Angular gerendert wurden:
Wenn Sie genau hinsehen, gibt es ein zusätzliches Kommentar-Tag im endgültigen DOM von Beispiel 2. Der Code, den Angular interpretiert hat, war:
Angular hat Ihr Host-<ng-template>
innerhalb eines anderen <ng-template>
verpackt und nicht nur das äußere <ng-template>
in diagnostische Kommentare umgewandelt, sondern auch das innere! Das ist der Grund, warum Sie Ihre Nachricht nicht sehen konnten.
Um dies zu vermeiden, gibt es zwei Möglichkeiten, das gewünschte Ergebnis zu erhalten:
Methode 1:
Bei dieser Methode stellen Sie Angular das entzuckerte Format zur Verfügung, das keine weitere Verarbeitung benötigt. Dieses Mal würde Angular nur <ng-template>
in Kommentare umwandeln, aber den Inhalt darin unberührt lassen (sie befinden sich nicht mehr innerhalb eines <ng-template>
wie im vorherigen Fall). So wird der Inhalt korrekt gerendert.
Um mehr darüber zu erfahren, wie man dieses Format mit anderen strukturellen Direktiven verwendet, lesen Sie diesen Artikel.
Methode 2:
Dies ist ein ziemlich ungesehenes Format und wird selten verwendet (mit zwei Geschwistern <ng-template>
). Hier geben wir einer Vorlage einen Verweis auf die *ngIf
in ihrer then
, um ihr mitzuteilen, welche Vorlage verwendet werden soll, wenn die Bedingung wahr ist.
Die Verwendung mehrerer <ng-template>
auf diese Weise ist nicht ratsam (Sie können stattdessen <ng-container>
verwenden), da dies nicht ihr Zweck ist. Sie werden als Container für Vorlagen verwendet, die an mehreren Stellen wiederverwendet werden können. Wir werden mehr darüber in einem späteren Abschnitt dieses Artikels berichten.
<ng-container>
Haben Sie jemals Code geschrieben oder gesehen, der diesem ähnelt:
Der Grund, warum viele von uns diesen Code schreiben, ist die Unmöglichkeit, mehrere strukturelle Direktiven auf einem einzigen Host-Element in Angular zu verwenden. Dieser Code funktioniert gut, aber er führt mehrere zusätzliche leere <div>
im DOM ein, wenn item.id
ein Falsy-Wert ist, der möglicherweise nicht benötigt wird.
Für ein einfaches Beispiel wie dieses mag man sich keine Sorgen machen, aber für eine große Anwendung, die ein komplexes DOM hat (um Zehntausende von Daten anzuzeigen), könnte dies problematisch werden, da die Elemente möglicherweise Listener haben, die immer noch im DOM vorhanden sind, um auf Ereignisse zu hören.
Was noch schlimmer ist, ist die Verschachtelung, die man machen muss, um sein Styling (CSS) anzuwenden!
Keine Sorge, wir haben <ng-container>
zur Rettung!
Das Angular <ng-container>
ist ein Gruppierungselement, das nicht mit Stilen oder Layout interferiert, weil Angular es nicht in das DOM einfügt.
Wenn wir also unser Beispiel 1 mit <ng-container>
schreiben:
Wir erhalten das endgültige DOM als:
Siehe, wir sind die leeren <div>
s los. Wir sollten <ng-container>
verwenden, wenn wir mehrere strukturelle Direktiven anwenden wollen, ohne ein zusätzliches Element in unser DOM einzuführen.
Weitere Informationen finden Sie in den Dokumenten. Es gibt einen weiteren Anwendungsfall, bei dem eine Vorlage dynamisch in eine Seite eingefügt wird. Ich werde diesen Anwendungsfall im letzten Abschnitt dieses Artikels behandeln.
<ng-content>
Sie werden verwendet, um konfigurierbare Komponenten zu erstellen. Das bedeutet, dass die Komponenten je nach den Bedürfnissen des Benutzers konfiguriert werden können. Dies ist auch als Content Projection bekannt. Komponenten, die in veröffentlichten Bibliotheken verwendet werden, machen von <ng-content>
Gebrauch, um sich selbst konfigurierbar zu machen.
Betrachten wir eine einfache <project-content>
-Komponente:
Der HTML-Inhalt, der innerhalb der öffnenden und schließenden Tags der <project-content>
-Komponente übergeben wird, ist der zu projizierende Inhalt. Dies wird als Inhaltsprojektion bezeichnet. Der Inhalt wird innerhalb des <ng-content>
innerhalb der Komponente gerendert. Dadurch kann der Nutzer der <project-content>
-Komponente jede beliebige benutzerdefinierte Fußzeile innerhalb der Komponente übergeben und genau steuern, wie sie gerendert werden soll.
Mehrere Projektionen:
Was wäre, wenn Sie entscheiden könnten, welcher Inhalt wo platziert werden soll? Anstatt dass jeder Inhalt innerhalb eines einzelnen <ng-content>
projiziert wird, können Sie auch mit dem select
-Attribut von <ng-content>
steuern, wie der Inhalt projiziert wird. Es braucht einen Element-Selektor, um zu entscheiden, welcher Inhalt innerhalb eines bestimmten <ng-content>
projiziert werden soll.
So geht’s:
Wir haben die <project-content>
-Definition geändert, um Multi-Content-Projektion durchzuführen. Das select
-Attribut wählt die Art des Inhalts aus, der innerhalb eines bestimmten <ng-content>
gerendert werden soll. Hier haben wir zuerst select
, um das Element h1
als Kopfzeile darzustellen. Wenn der projizierte Inhalt kein h1
-Element enthält, wird nichts wiedergegeben. In ähnlicher Weise sucht das zweite select
nach einem div
. Der Rest des Inhalts wird innerhalb des letzten <ng-content>
ohne select
gerendert.
Der Aufruf der Komponente sieht wie folgt aus:
*ngTemplateOutlet
…Sie werden als Container für Vorlagen verwendet, die an mehreren Stellen wiederverwendet werden können. Mehr dazu in einem späteren Abschnitt dieses Artikels.
…Es gibt einen weiteren Anwendungsfall, bei dem eine Vorlage dynamisch in eine Seite eingefügt wird. Ich werde diesen Anwendungsfall im letzten Abschnitt dieses Artikels behandeln.
In diesem Abschnitt werden wir die beiden oben erwähnten Punkte besprechen. *ngTemplateOutlet
wird für zwei Szenarien verwendet – um eine gemeinsame Vorlage in verschiedenen Abschnitten einer Ansicht unabhängig von Schleifen oder Bedingungen einzufügen und um eine hoch konfigurierte Komponente zu erstellen.
Vorlagenwiederverwendung:
Betrachten Sie eine Ansicht, in der Sie eine Vorlage an mehreren Stellen einfügen müssen. Zum Beispiel ein Firmenlogo, das auf einer Website platziert werden soll. Wir können das erreichen, indem wir die Vorlage für das Logo einmal schreiben und sie überall in der Ansicht wiederverwenden.
Nachfolgend der Codeausschnitt:
Wie Sie sehen, haben wir die Logo-Vorlage nur einmal geschrieben und sie mit einer einzigen Codezeile dreimal auf derselben Seite verwendet!
*ngTemplateOutlet
akzeptiert auch ein Kontextobjekt, das übergeben werden kann, um die gemeinsame Vorlagenausgabe anzupassen. Weitere Informationen über das Kontextobjekt finden Sie in der offiziellen Dokumentation.
Anpassbare Komponenten:
Der zweite Anwendungsfall für *ngTemplateOutlet
sind stark angepasste Komponenten. Betrachten Sie unser vorheriges Beispiel der <project-content>
Komponente mit einigen Modifikationen:
Oben ist die modifizierte Version der <project-content>
Komponente, die drei Eingabeeigenschaften akzeptiert – headerTemplate
, bodyTemplate
, footerTemplate
. Nachfolgend das Snippet für project-content.ts
:
Was wir hier erreichen wollen, ist die Anzeige von Header, Body und Footer, wie sie von der übergeordneten Komponente von <project-content>
empfangen werden. Wenn eines dieser Elemente nicht zur Verfügung gestellt wird, zeigt unsere Komponente die Standardvorlage an seiner Stelle. Auf diese Weise entsteht eine stark angepasste Komponente.
Um unsere kürzlich geänderte Komponente zu verwenden:
So werden wir die Vorlagenreferenzen an unsere Komponente übergeben. Wenn eine von ihnen nicht übergeben wird, dann wird die Komponente die Standardvorlage wiedergeben.
ng-content vs. *ngTemplateOutlet
Beide helfen uns, hochgradig angepasste Komponenten zu erreichen, aber welche sollen wir wählen und wann?
Es ist deutlich zu sehen, dass *ngTemplateOutlet
uns etwas mehr Macht gibt, die Standardvorlage zu zeigen, wenn keine zur Verfügung gestellt wird.
Das ist nicht der Fall mit ng-content
. Hier wird der Inhalt so wiedergegeben, wie er ist. Sie können den Inhalt höchstens aufteilen und mit Hilfe des Attributs select
an verschiedenen Stellen der Ansicht darstellen. Sie können den Inhalt innerhalb von ng-content
nicht bedingt wiedergeben. Sie müssen den Inhalt anzeigen, der vom übergeordneten Element empfangen wird, ohne die Möglichkeit, Entscheidungen auf der Grundlage des Inhalts zu treffen.
Die Wahl zwischen den beiden hängt jedoch vollständig von Ihrem Anwendungsfall ab. Immerhin haben wir jetzt eine neue Waffe *ngTemplateOutlet
in unserem Arsenal, die zusätzlich zu den Funktionen von ng-content
mehr Kontrolle über den Inhalt bietet!