zurück zum Artikel

Tipps und Tricks mit Angular, Teil 10: Mehrdimensionale Navigationsstrukturen

Know-how
Mehrdimensionale Navigationsstrukturen in Angular

Routing an sich ist in Single-Page-Anwendungen nicht kompliziert. Benötigen Navigationsstrukturen Hierarchieebenen, gibt es dafür in Angular zwei Ansätze für unterschiedliche Bedürfnisse: Child Routes und Aux Routes.

Tipps und Tricks für AngularJS

Teil 1: Internationalisierung [1]
Teil 2: ES2015 [2]
Teil 3: OAuth 2.0 [3]
Teil 4: Animationen [4]
Teil 5: Transformationen und Interceptors [5]
Teil 6: Backend-lose Entwicklung mit ngMockE2E [6]
Teil 7: GUIs mit @ngrx/store (I) [7]
Teil 8: GUIs mit @ngrx/store (II) [8]
Teil 9: Performance-Tuning [9]
Teil 10: Mehrdimensionale Navigationsstrukturen

Außerdem:

Das Prinzip des Routing in Single Page Applications ist einfach: Die Anwendung erhält einen Platzhalter, in den ein Router je nach gewünschtem Navigationspunkt eine Komponente lädt. Etwas komplexer wird es, wenn mehrere Platzhalter benötigt werden. Der vorliegende Artikel stellt dazu zwei Konzepte des Angular-Routers vor: Child Routes und Aux Routes. Die dazugehörige Beispielanwendung findet sich im GitHub-Repository [13] des Autors.

Hierarchisches Routing mit Child Routes

Um hierarchische Navigationsstrukturen zu schaffen, können Komponenten, die der Router aktiviert, einen weiteren Platzhalter aufweisen. Die Routen, die für so einen Platzhalter definiert werden, sind auch als Child Routes bekannt.

Ein Beispiel dafür stellt die FlightBookingComponent in Abbildung 1 dar. Sie enthält neben dem links dargestellten Untermenü einen Platzhalter, durch den sich weitere Routen aktivieren lassen.

Komponente mit Child Routes (Abb. 1)
Komponente mit Child Routes (Abb. 1)


Danach hängt es von der gewählten URL ab, welche Child Route der Router im Platzhalter der betrachteten Komponente aktiviert. In Abbildung 2 gibt die verwendete URL beispielsweise an, dass die FlightSearchComponent innerhalb der FlightBookingComponent zu aktivieren ist.

Hierarchisches Routing (Abb. 2)
Hierarchisches Routing (Abb. 2)

Child Routes implementieren

Um die Implementierung von Child Routes zu veranschaulichen, ist im Beispiel ein Hauptmenüpunkt Flug buchen mit zwei untergeordneten Menüpunkten Flug suchen und Passagier suchen vorgesehen. Der Hauptmenüpunkt verweist auf eine FlightBookingComponent, die links ein Menü mit Flug auswählen und Passagier auswählen präsentiert und rechts davon einen Platzhalter aufweist (siehe Abb. 3).

Beispielanwendung, erweitert um hierarchisches Routing (Abb. 3)
Beispielanwendung, erweitert um hierarchisches Routing (Abb. 3)


Abhängig vom Befehl, den der Benutzer auf der linken Seite auswählt, lädt die Anwendung die FlightSearchComponent oder die PassengerSearchComponent (aus Platzgründen hier nicht abgebildet) in den Platzhalter auf der rechten Seite.

Die Implementierung der FlightBookingComponent gestaltet sich zunächst einfach:

@Component({
selector: 'flight-booking',
templateUrl: './flight-booking.component.html'
})
export class FlightBookingComponent {
}

Auch das Template der FlightBookingComponent definiert einen Platzhalter mit router-outlet. Wie in dem der AppComponent richtet das aktuelle Template ein paar Links mittels routerLink ein:

<div class="col-sm-3">
<ul class="nav nav-pills nav-stacked" style="margin-top:20px;">
<li routerLinkActive="active"><a [routerLink]="['./flight-search']">
Flug auswählen</a></li>
<li routerLinkActive="active"><a [routerLink]=
"['./passenger-search']">Passagier auswählen</a></li>
</ul>
</div>

<div class="col-md-9">
<router-outlet></router-outlet>
</div>

Hinweis: Mit dem Präfix ./ zeigt routerLink an, dass die definierte Route an die aktuelle Route anzuhängen ist. Da es sich dabei um das Standardverhalten handelt, könnte es die Information auch weglassen. Interessant wird es jedoch, wenn der Link sich irgendwo anders im Verzeichnisbaum befindet. Um beispielsweise einen Schwesterknoten zu adressieren, ist ../ voranzustellen. Mit ../passenger-search lässt sich somit von /flight-search zu /passenger-search navigieren.

Routenkonfiguration

Dem Router teilt das FlightBookingModule die gewünschte Hierarchie mit ihrer Routenkonfiguration mit. Dazu richtet es eine Route für die FlightBookingComponent ein. Diese erhält über die Auflistung children Child Routes, die auf die FlightSearchComponent und PassengerSearchComponent verweisen:

const FLIGHT_BOOKING_ROUTES: Routes = [
{
path: 'flight-booking',
component: FlightBookingComponent,
children: [
{
path: '',
redirectTo: 'flight-search',
pathMatch: 'full'
},
{
path: 'flight-search',
component: FlightSearchComponent
},
{
path: 'passenger-search',
component: PassengerSearchComponent
}
]
}
];

export let FlightBookingRouterModule =
RouterModule.forChild(FLIGHT_BOOKING_ROUTES);

Die Pfade der Child Routes gelten als Erweiterung der Pfade der übergeordneten Parent-Route. Ein Aufruf der URL flight-booking/flight-search führt beispielsweise dazu, dass der Router die FlightBookingComponent im Platzhalter der Anwendung und die FlightSearchComponent im Platzhalter der FlightBookingComponent aktiviert. Um auch dann eine sinnvolle Antwort liefern zu können, wenn nur die Parent-Route flight-booking aufgerufen wird, definiert die betrachtete Routenkonfiguration eine Standardroute mit leerem Pfad. Diese leitet auf die Route flight-search weiter. Nach dieser Änderung ist noch das Hauptmenü in der AppComponent anzupassen:

<nav class="navbar navbar-default">
<ul class="nav navbar-nav">
<li routerLinkActive="active"><a [routerLink]="['/home']">Home</a></li>
<li routerLinkActive="active"><a [routerLink]="['/flight-booking']">
Flug buchen</a></li>
</ul>
</nav>

<div class="container">
<router-outlet></router-outlet>
</div>

Während der Menüpunkt Home direkt auf die HomeComponent verweist, leitet Flug buchen den Benutzer auf den ersten Unterpunkt der FlugBuchenComponent, nämlich Flug suchen.

Aux Routes

Mit den als Aux Routes bezeichneten Auxiliary Routes (Hilfsrouten) ermöglicht der Router das Einführen mehrerer Platzhalter. Dabei ist jedem von ihnen ein Name zu spendieren. Durch dessen Angabe kann die Anwendung eine Komponente dafür festlegen. Genau genommen hat der bis dato verwendete Platzhalter bereits einen Namen gehabt, zumal Angular standardmäßig die Bezeichnung primary vergibt. Das Konzept hinter Aux Routes bietet zudem die Option, alle Platzhalter unabhängig voneinander mit Inhalten zu füllen:

Schematische Darstellung von Aux Routes (Abb. 4)
Schematische Darstellung von Aux Routes (Abb. 4)


Die Tatsache, dass der Platzhalter mit dem Namen aux unter dem Standardplatzhalter positioniert ist, bedeutet übrigens nicht, dass sein Inhalt immer dort eingeblendet wird. CSS ermöglicht die Platzierung überall auf der Seite und sogar das Überblenden des Standardplatzhalters. Auf die Weise lassen sich mit Aux Routes modale Dialoge entwickeln, deren Zustand die URL widerspiegelt. Weitere Anwendungsfälle sind das Einblenden zusätzlicher Informationen sowie das Umsetzen einer Anwendung, die – wie eine Entwicklungsumgebung – aus unterschiedlichen, bis zu einem bestimmten Grad sogar unabhängigen Bereichen besteht. Ein weiteres Beispiel hierfür ist ein Chat, den Entwickler neben dem Arbeitsbereich der Anwendung für Support-Fälle anlegen, oder das Einrichten mehrerer Arbeitsbereiche im Stil weit verbreiteter Dateimanager.

Im Folgenden soll zur Veranschaulichung von Aux Routes eine FlightHistoryComponent zum Einsatz kommen, die Informationen über vergangene Suchanfragen präsentiert. Bei Bedarf wird sie über eine Aux Route aktiviert:

Nutzung einer Aux Route in der Demo-Anwendung (Abb. 5)
Nutzung einer Aux Route in der Demo-Anwendung (Abb. 5)


Ein Vergleich mit Abb. 6 zeigt, dass der Router Aux Routes unabhängig von der Standardroute aktivieren kann, zumal hier neben der HistoryComponent im Platzhalter aux die HomeComponent im Hauptplatzhalter erscheint.

Aux Routes lassen sich unabhängig von der Standardroute aktivieren (Abb. 6).
Aux Routes lassen sich unabhängig von der Standardroute aktivieren (Abb. 6).


Analog zum Standardplatzhalter erhält die AppComponent einen zweiten Platzhalter mit dem Namen aux:

<div class="container">

<div class="row">
<router-outlet></router-outlet>
</div>

<div class="row">
<router-outlet name="aux"></router-outlet>
</div>

</div>

Zusätzlich erhält die Routenkonfiguration einen Eintrag, der sich auf den Platzhalter aux bezieht. Dazu kommt die Eigenschaft outlet zum Einsatz:

let APP_ROUTES: Routes = [
{
path: '',
redirectTo: 'home',
pathMatch: 'full'
},
{
path: 'home',
component: HomeComponent
},
[...]
{
path: 'history',
component: FlightHistoryComponent,
outlet: 'aux'
}
];

Dabei ist zu beachten, dass jede Route einen bestimmten Platzhalter adressiert. Benennt eine Route ihren Platzhalter nicht direkt über die gezeigte Eigenschaft outlet, weist der Router sie dem Standardplatzhalter primary zu.

Den Inhalt für den Standardplatzhalter primary gibt die Anwendung, wie gehabt, über die URL an. Die Komponenten für alle weiteren benannten Platzhalter sind innerhalb runder Klammern in der URL anzuführen. http://localhost:8080/bookings(aux:history;showDetails=true) weist beispielsweise darauf hin, dass die Route bookings im Hauptplatzhalter zu aktivieren ist, und dass die Route history dem Platzhalter mit dem Namen aux eine Komponente verpassen soll. Letztere erhält zur Demonstration einen Parameter showDetails. Zum Generieren solcher URLs bietet sich die Direktive routerLink an:

<li><a [routerLink]="[{outlets: { aux: 'history' }}]">
History einblenden</a></li>
<li><a [routerLink]="[{outlets: { aux: null }}]">History ausblenden</a></li>

Während sie in den vorangegangenen Abschnitten lediglich auf die Route für den Standardplatzhalter verwiesen hat, definiert die Direktive nun ein Objekt, das mit der Eigenschaft outlet auf ein weiteres Objekt verweist. Letzteres bildet die Namen einzelner Platzhalter auf die dafür zu aktivierenden Routen ab. Auf die Weise lassen sich Komponenten für mehrere Platzhalter gleichzeitig aktivieren. Das betrachtete Beispiel zeigt zudem, dass eine Aux Route nicht zwingend aktiv sein muss. Um einen Platzhalter zu leeren, ist lediglich der Wert null zuzuweisen.

Der Vollständigkeit halber zeigt der unten aufgeführte Codeauszug, wie die Anwendung einer Aux Route Parameter übergeben kann. Dazu kommt die gewohnte Schreibweise zum Einsatz, die den Namen der gewünschten Route zusammen mit Parametern in einem Array verstaut.

<li><a [routerLink]="[{outlets: { aux: ['history', 
{showDetail: true}] }}]">History einblenden</a></li>
<li><a [routerLink]="[{outlets: { aux: null }}]">History ausblenden</a></li>

Fazit

Mit Child Routes und Aux Routes erlaubt der Angular-Router das Verwalten mehrerer Platzhalter. Während bei Child Routes dazu aktivierte Komponenten einen weiteren Platzhalter einführen, können dank Aux Routes auf jeder Hierarchieebene mehrere Platzhalter nebeneinander existieren. Damit lassen sich hierarchische Navigationsstrukturen, teilautonome Seitenbereiche oder auch solche Pop-ups umsetzen, die eine eigene URL bekommen. (jul [14])

Manfred Steyer [15]
ist Trainer und Berater mit Fokus auf Angular sowie Google Developer Expert (GDE). Er schreibt für O'Reilly, das Java-Magazin sowie heise Developer und spricht regelmäßig auf Konferenzen. In seinem aktuellen Buch zu Angular behandelt er die vielen Seiten des populären JavaScript-Frameworks aus der Feder von Google.


URL dieses Artikels:
http://www.heise.de/-3808493

Links in diesem Artikel:
[1] https://www.heise.de/developer/artikel/Tipps-und-Tricks-fuer-AngularJS-Teil-1-Internationalisierung-2516976.html
[2] https://www.heise.de/developer/artikel/Tipps-und-Tricks-fuer-AngularJS-Teil-2-ES2015-2555723.html
[3] https://www.heise.de/developer/artikel/Tipps-und-Tricks-fuer-AngularJS-Teil-3-OAuth-2-0-2620374.html
[4] https://www.heise.de/developer/artikel/Tipps-und-Tricks-fuer-AngularJS-Teil-4-Animationen-in-AngularJS-1-4-2774580.html
[5] https://www.heise.de/developer/artikel/Tipps-und-Tricks-fuer-AngularJS-Teil-5-Transformationen-und-Interceptors-2821088.html
[6] https://www.heise.de/developer/artikel/Tipps-und-Tricks-fuer-AngularJS-Teil-6-Backend-lose-Entwicklung-mit-ngMockE2E-3086974.html
[7] https://www.heise.de/developer/artikel/Tipps-und-Tricks-mit-AngularJS-Teil-7-GUIs-mit-Angular-2-und-Redux-Implementierung-ngrx-store-I-3192046.html
[8] https://www.heise.de/developer/artikel/Tipps-und-Tricks-mit-AngularJS-Teil-8-GUIs-mit-Angular-2-und-ngrx-store-II-3194264.html
[9] https://www.heise.de/developer/artikel/Tipps-und-Tricks-mit-AngularJS-Teil-9-Performance-Tuning-3271483.html
[10] https://www.heise.de/developer/artikel/AngularJS-1-x-und-2-0-mit-dem-Component-Router-parallel-betreiben-2679282.html
[11] https://www.heise.de/developer/artikel/Internationalisierung-fuer-Angular-Teil-1-Bordmittel-3703621.html
[12] https://www.heise.de/developer/artikel/Internationalisierung-fuer-Angular-Teil-2-ngx-translate-3722374.html
[13] https://github.com/manfredsteyer/angular_advanced_enterjs_2017
[14] mailto:jul@heise.de
[15] http://www.softwarearchitekt.at