Komponenten in Angular werden idealerweise so gebaut, dass sie universell eingesetzt werden können. Leider werden aber bei der Einbindung dieser Komponenten häufig kontextuelle Anpassungen benötigt. Wir zeigen auf, welche Möglichkeiten bestehen, um solche Anpassungen vorzunehmen.
Komponenten haben in Angular ihre eigenen Stildefinitionen, welche nur innerhalb dieser Komponente gelten. Im Idealfall kann eine Komponente dann an allen gewünschten Stellen eingesetzt werden, ohne dass es eine Anpassung an der Komponente braucht. Häufig aber gibt es Spezialfälle: Das Padding muss etwas verändert werden; die Grösse ist an einer Stelle anders; die Farbe passt sich kontextspezifisch an.
Drei verschiedene Herangehensweisen ermöglichen kontextspezifische Anpassungen:
- Anpassungsbedarf wird der Komponente mit Input-Parametern mitgeteilt
- Komponente beinhaltet Stilregeln für den jeweiligen Kontext
- Eltern-Komponente definiert Stilregeln für Kind-Komponente
Vor- und Nachteile dieser drei Ansätze sowie deren Verwendungsmöglichkeiten werden im Folgenden aufgezeigt.
Darstellung mit Input-Parameter steuern
Eine Angular-Komponente kann mit Input-Parametern von aussen angesprochen werden. So kann einer Komponente mitgeteilt werden, welche Darstellung gewünscht wird. Diese Input-Parameter veranlassen, dass die Komponente mit den benötigten CSS-Klassen versehen wird, so dass die gewünschten Stilregeln zum Einsatz kommen.
Beispiel
Im HTML wird einer Komponente ein Input-Parameter für die Grösse mitgegeben:
<component [isBig]="true"></code>
Die Komponente hat einen entsprechenden Input-Parameter:
@Input() isBig: boolean;
Im HTML der Komponente wird mit [ngClass] (ggf. mit @Hostbinding) eine entsprechende Klasse gesetzt:
<div class="component" [ngClass]= "{ 'component—big': isBig }"/>
Im CSS der Komponente wird eine entsprechende Stildefinition für diese Klasse aufgeführt:
.component—big { font-size: 2rem }
Vorteile
Da die Stil-Regeln in der Komponente definiert werden, sind Abhängigkeiten bei Anpassungen besser ersichtlich (im Vergleich zu Stilregeln, welche in Eltern-Komponente definiert werden; siehe unten).
Nachteile
Es besteht die Gefahr, dass viele Input-Parameter für z.T. kleine Anpassungen (z.B. unterschiedliche Paddings) benötigt werden.
Eine nachvollziehbare Benennung von Input-Parametern gelingt mal besser und mal schlechter (z.B. reducedPaddingRight: «true»).
Anwendung
Die Definition von input-abhängigen Stilregeln eignet sich v.a. für eine limitierte Anzahl an gut benennbaren Varianten (z.B. bestimmte Grössen-Ausprägungen, invertierte Variante).
Wird mit einem Input-Parameter das HTML angepasst, sollten auch die dazugehörigen Stil-Anpassungen über diesen Input-Parameter gesteuert werden.
Darstellung mit kontextspezifischen Stildefinitionen steuern
Stilregeln für eine Angular-Komponente sind normalweise nur innerhalb dieser Komponente wirksam. Angular bietet aber mit «host-context» die Möglichkeit, innerhalb einer Komponente auf deren Einsatzkontext zu hören. Als Einsatzkontext kann eine CSS-Klasse oder ein Komponenten-Selektor angegeben werden.
Beispiel
Eine Komponente ChildComponent wird in einer ParentComponent eingesetzt:
<parent-component>
<child-component></child-component>
</parent-component>
Im CSS der ChildComponent werden Stilregeln definiert, welche nur gelten, wenn die Komponente in der ParentComponent eingesetzt wird:
:host-context(parent-component) {
.child-component {
background: yellow;
}
}
Vorteile
Wie bei der Steuerung mit Input-Parametern werden Stil-Regeln in der Komponente definiert, wodurch Abhängigkeiten bei Anpassungen besser ersichtlich sind (im Vergleich zu Stilregeln, welche in Eltern-Komponente definiert werden; siehe unten).
Nachteile
Für verschachtelte Abhängigkeiten ist «host-context» nicht geeignet, da nur die Verwendung einer Kontext-Ebene möglich ist. Es ist also nicht möglich, z.B. auf eine Browser-Detection-Klasse auf dem html-Tag und eine Eltern-Komponente zu hören (weder :host-context(.msie parent-component) { …} noch :host-context(.msie) { :host-context(parent-component) { … } } ist möglich).
Anwendung
Die «host-context»-Lösung eignet sich für Kontext-Stilregeln, welche sich mit einer Kontext-Ebene behandeln lassen. Ein Beispiel dafür wäre etwa, wenn eine Komponente für verschiedene Themes farblich angepasst wird (basierend auf einer Theme-Klasse auf dem html-Tag).
Die «host-context»-Lösung funktioniert aber nur bei nicht-verschachtelten Regeln. Es können z.B. Ausnahmeregeln basierend auf einer Browser-Detection definiert oder spezielle Stilregeln für den Einsatz der Komponente innerhalb einer einbettenden Komponente (z.B. Elternkomponente) angelegt werden, aber beides zusammen funktioniert nicht.
Sinnvoll kann sein, solche «host-context»-Stilregeln in eine separates CSS-Datei auszulagern (Komponenten können mehrere CSS-Dateien haben), um allgemeine von kontextspezifischen Regeln zu trennen. So kann der kontextspezifische Ballast einer Komponente z.B. für die Wiederverwertung in einem anderen Projekt schnell entfernt werden.
Darstellung der Kind-Komponente in Eltern-Komponente steuern
Eine Kind-Komponente kann auch aus einer Eltern-Komponente heraus mit Stilregeln versehen werden. Zum einen kann der Host einer Kind-Komponente direkt von der Eltern-Komponente gestyled werden, zum anderen können CSS-Variablen eingesetzt werden, um Stil-Anpassungen von der Eltern-Komponente her zu machen.
Beispiel
Eine Komponente ChildComponent wird in einer ParentComponent eingesetzt und mit einer Klasse versehen:
<parent-component>
<child-component class="parent-component__child"></child-component>
</parent-component>
Die Eltern-Komponente kann in ihrem CSS Regeln für den Host der Kind-Komponente aufweisen:
.parent-component__child {
// Styles für Host der Kind-Komponente
display: flex;
...
}
Die Eltern-Komponente kann mit CSS-Variablen Einfluss auf spezifische Styling-Aspekte der Kind-Komponente nehmen, wenn diese CSS-Variablen anbietet:
.child-component__sub-container {
padding-right: var(–child-component-padding-right, 0);
}
...
.parent-component {
–child-component-padding-right: 20px;
}
Vorteile
Vorteilhaft ist an diesem Ansatz, dass Ausnahmeregeln dort gesetzt werden können, wo sie benötigt werden.
Nachteile
Da die Stilregeln nicht in der betroffenen (Kind-)Komponente definiert werden, sind Abhängigkeiten bei einer Anpassung an dieser Komponente schwerer zu eruieren. CSS-Variablen bieten immerhin einen Hinweis darauf, welche Aspekte wohl anderenorts von aussen gesetzt werden.
Anwendung
Sinnvoll ist die Verwendung dieses Ansatzes v.a. dann, wenn die Stilanpassung nur in dieser Eltern-Komponente gilt.
Schlussfolgerung
Es gibt keinen Köngisweg – alle drei Ansätze haben ihren Nutzen.
Mit Input-Parametern werden alle Stilregeln in der Komponente definiert, was im Grundsatz sauber und übersichtlich ist. Mit vielen Input-Parametern aber wächst die Komplexität und Lesbarkeit.
Kontext-Stilregeln («host-context») werden ebenfalls in der Komponente definiert, was den Vorteil hat, dass bei weiteren Anpassungen an der Komponente die Abhängigkeiten und Ausnahmen schnell eruiert sind. Leider lässt dieser Ansatz keine Verschachtelung der Kontexte zu, weshalb er schnell an seine Grenzen stösst.
Die Steuerung einer Kind-Komponente in der Eltern-Komponente bedeutet, dass ein Teil der Stilregeln für die Kind-Komponente in (verschiedenen) Eltern-Komponenten definiert wird. Dadurch leidet die Nachvollziehbarkeit. Der Ansatz kann aber äusserst hilfreich sein, wenn kleine Anpassungen von Nöten sind, welche nicht über Input-Parameter realisiert werden sollen.
Die drei Ansätze können natürlich auch kombiniert werden. Insbesondere kann die Limitation des «host-context»-Ansatzes, welcher nur eine Kontext-Ebene erlaubt, mit der Steuerung durch die Elternkomponente kombiniert werden. So kann z.B. auf eine Klasse auf dem html-Tag mit «host-context» gehört werden und in einer Eltern-Komponente festgelegt werden, welche Stilregeln die Kind-Komponente in diesem Fall bekommt.
Für die Weiterentwicklung von Angular ist aber zu hoffen, dass für den veralteten «ng-deep»- und für den limitieren «host-context»-Ansatz neue und skalierbare Lösung angeboten werden, sei es nun in Angular selbst oder über breit unterstützte W3C-Standards.
Dieser Artikel ist Teil unserer Serie über Stärken und Schwächen von Angular.
Kommentare