Angular. Ferdinand Malcher
6.3.3Den BookMonkey erweitern
Story – Event Bindings
Als Leser möchte ich Details eines Buchs abrufen können, um zu entscheiden, ob der Inhalt für mich von Interesse ist.
Bei Auswahl eines Listeneintrags soll die Bücherliste ausgeblendet und stattdessen eine Detailansicht mit Büchern angezeigt werden.
Es soll ein Button in der Detailansicht existieren, der dafür sorgt, dass die Detailansicht ausgeblendet und die Liste der Bücher wieder eingeblendet wird.
Die gelernte Theorie der Event Bindings wollen wir jetzt praktisch am BookMonkey anwenden. Bisher hat die Anwendung eine Listenansicht für die Bücher. Zusätzlich soll jetzt eine Detailansicht angelegt werden, in der man ein einzelnes Buch betrachten kann.
Buchliste und Detailseite sind dabei jeweils eigenständige Komponenten, die in die Hauptkomponente AppComponent eingebunden sind. Damit nur immer eine der beiden Komponenten angezeigt wird, wollen wir die jeweils andere mit der Direktive ngIf ausblenden.
Beide Komponenten können ein Event an die Hauptkomponente übermitteln, um die jeweils andere Ansicht anzuzeigen. Damit kann man dann zwischen den beiden Ansichten umschalten. Das einzelne anzuzeigende Buch wird mit einem Property Binding an die Detailansicht übergeben.
Damit sieht die geplante Kommunikation im Komponentenbaum so aus:
Abb. 6–10 Kommunikation zwischen Komponenten im BookMonkey
Komponente für die Detailansicht anlegen
Als Erstes legen wir die Komponente für die Detailansicht an. Für das Grundgerüst verwenden wir wieder die Angular CLI. Die neue Komponente soll BookDetailsComponent heißen.
$ ng g component book-details
Listing 6–43 Komponente BookDetailsComponent mit der Angular CLI anlegen
Es ergibt sich die folgende Ordnerstruktur im Projekt:
Um die Implementierung der Komponente kümmern wir uns später.
Datenfluss in der AppComponent
Zunächst widmen wir uns der Hauptkomponente AppComponent und implementieren den geplanten Datenfluss, wie er in der Abbildung 6–10 dargestellt ist. In der Komponentenklasse führen wir den Status view-State ein. Dieses Property soll entweder den String list oder details enthalten. Um sicherzustellen, dass kein falscher Wert gesetzt werden kann, definieren wir uns einen TypeScript-Typen, der alle möglichen Zustände beinhaltet. Der neue Typ trägt den Namen ViewState und hat die möglichen Werte list und details.11 Die Buchliste soll nur angezeigt werden, wenn die Eigenschaft viewState auf list gesetzt ist, die Detailkomponente nur dann, wenn der Wert details lautet. Da beim Laden der Anwendung noch keine Aktion durchgeführt wurde, setzen wir als Standardwert list ein.
Die Detailkomponente muss später immer über das anzuzeigende Buch verfügen. Wir legen deshalb in der AppComponent zusätzlich das Property book an. Hier wird ein Book abgelegt, das später über ein Property Binding an die Detailkomponente übergeben wird.
Um zwischen den beiden Zuständen list und details zu wechseln, sollen die beiden Kindkomponenten später ein Event werfen. Um auf diese Events zu reagieren, führen wir in der AppComponent die zwei Methoden showList() und showDetails() ein. Sie sollen den Wert im view-State ändern, sodass die passende Komponente angezeigt wird. Die Methode showDetails() empfängt außerdem ein Buch-Objekt als Event-Payload und schreibt es in die Eigenschaft book der Komponentenklasse.
import { Component } from '@angular/core';
import { Book } from './shared/book';
type ViewState = 'list' | 'details';
@Component({
selector: 'bm-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
book: Book;
viewState: ViewState = 'list';
showList() {
this.viewState = 'list';
}
showDetails(book: Book) {
this.book = book;
this.viewState = 'details';
}
}
Listing 6–44 AppComponent (app.component.ts)
Buchliste und Detailansicht in Hauptkomponente einbinden
Im Template der AppComponent binden wir nun die beiden Kindkomponenten ein. Wir setzen beide Komponenten direkt untereinander, denn es wird ohnehin nur jeweils eine der beiden angezeigt. Um das zu erreichen, werden beide Elemente mit der Direktive ngIf ausgestattet. Die Bedingungen knüpfen wir an den jeweiligen Wert aus dem Property viewState. Außerdem abonnieren wir die geplanten Events showDetails-Event und showListEvent aus den Kindkomponenten.
Meldung: Expected call-signature to have a typedef
Womöglich erhalten Sie für die Methoden showList() und showDetails() die folgende Warnmeldung im Editor:
expected call-signature: 'showList' to have a typedef (typedef)
Das Tool TSLint weist uns darauf hin, dass wir eine Regel gebrochen haben: Eine Methode soll immer einen festgelegten Rückgabetyp besitzen. Geben wir hier also den Rückgabetyp void an (wie beim ngOnInit()), so verschwindet die Meldung.
Wir sind allerdings der Meinung, dass eine Methode nicht immer zwangsläufig einen Rückgabetyp angeben muss, denn der Compiler kann auch ohne unsere Hilfe den korrekten Typ ermitteln. Machen wir hier keine expliziten Angaben, ist der Quelltext etwas kürzer. Wir wollen die Regel daher vollständig deaktivieren. Dazu ändern wir in der Datei tslint.json den Eintrag für typedef von true auf false.
"typedef": [
false,
"call-signature"
],
Ob Sie dies für Ihr Projekt auch tun, bleibt Ihnen überlassen. Es gibt für diese Regel keine richtige oder falsche Entscheidung.
Das Event showDetailsEvent übermittelt in seinem Event-Payload das ausgewählte Buch. Wir haben schon dafür gesorgt, dass dieses Buch im Property book gespeichert wird. Von dort aus übergeben wir das Objekt an die BookDetailsComponent.
<bm-book-list
*ngIf="viewState === 'list'"
(showDetailsEvent)="showDetails($event)"></bm-book-list>
<bm-book-details