Webprogrammierung mit Angular, Teil 2
Formulargedöns
Der zweite Teil des Angular-Tutorials zeigt das Senden von Daten an REST-Dienste, das Erstellen untergeordneter Angular-Komponenten, das Routing zwischen Komponenten und den Bau von Eingabemasken.
Im ersten Teil des Tutorials wurde die Struktur des Projekts „MiracleList“ (siehe „Alle Links“, [a]) angelegt und ein Proxy für die REST-Dienste des vorhandenen Backends [b] erstellt. Außerdem entstanden die Anzeige der Kategorien samt Aufgabenliste und eine Detailansicht zu den Aufgaben. Der Benutzer kann jedoch bislang in der Webanwendung keine Daten ändern.
Den Status von Aufgaben ändern
Jede Aufgabe in der mittleren Liste (siehe Abbildung 1) besitzt ein großes Kontrollkästchen (die CSS-Klasse WLcheckbox ist in app.component.css deklariert) zum Ändern des Erledigungsstatus:
<input type="checkbox" [(ngModel)]="t.done" name="done{{t.taskID}}" id="done{{t.taskID}}" (change)="changeDone(t)" class="form-control WLcheckbox">
Wichtig in dieser Zeile sind die Zwei-Wege-Datenbindung von [(ngModel)] an die Eigenschaft done des Task-Objekts und der eindeutige Name für jedes Kontrollkästchen mit name="done{{t.taskID}}". Die Schreibweise [(ngModel)] heißt auch „Banana in a Box“. Listing 1 zeigt die mit dem Kontrollkästchen verbundene Methode changeDone() in der Klasse AppComponent. Sie ruft die REST-Operation /ChangeTask auf und übergibt ihr das geänderte Task-Objekt.
Neue Aufgaben erstellen
Ähnlich sieht es bei Teilaufgaben aus. Hier gewährleistet zudem das bedingte Anwenden eines Style-Attributes, dass – wie im Wunderlist-Original – eine erledigte Unteraufgabe durchgestrichen wird:
<span [style.text-decoration]="st.done ? 'line-through': 'none'"> {{st.title }}</span>
Als Nächstes soll ein Eingabefeld für neue Aufgaben oben in Spalte 2 erscheinen:
<input type="text" class="form-control" name="newTaskTitle" [(ngModel)]="newTaskTitle" (change)="createTask()" placeholder="neue Aufgabe...">
Dazu korrespondieren in der Komponentenklasse (Listing 1) die Eigenschaft newTaskTitle und die Methode createTask(). Letztere erzeugt ein Task-Objekt mit dem neuen Titel und einigen Standardvorgaben. Die von der Datenbank auf dem Server vergebene taskID muss der Entwickler mit 0 initialisieren. Sonst besäße sie den Wert undefined und der Browser serialisierte sie in JSON als null. Das aber mag das Backend nicht, in dem das Attribut Primärschlüssel ist und daher nicht null sein darf. Nach dem Aufruf der REST-Operation /CreateTask lädt der Webclient die Liste der Aufgaben mit this.showTaskSet(this.category) erneut.
Ähnlich verläuft das Hinzufügen einer Teilaufgabe zu einer Aufgabe, also per Eingabefeld für die Eigenschaft newSubTaskTitle der Komponentenklasse:
<input type="text" class="form-control" name="newSubTaskTitle" [(ngModel)]="newSubTaskTitle" (change)="createSubTask()" placeholder="neue Teilaufgabe...">
Die Methode createSubTask() muss dann ein SubTask-Objekt initialisieren, bevor sie /ChangeTask auf dem Server aufruft.
Bei der Initialisierung ist neben der 0 für den Primärschlüssel die doppelte Beziehung zwischen Task- und SubTask-Objekt zu beachten, die this.task.subTaskSet.push(st) und st.taskID = this.task.taskID herstellen (siehe Listing 2). Der Aufruf von push() gewährleistet, dass Angular neue Teilaufgaben zum Server sendet. Die Initialisierung von st.taskID ist notwendig, da der Server auch für Subtasks keinen null-Wert erhalten mag. .NET-Entwickler sind vom Entity Framework gewohnt, dass eine Beziehung über eine Navigationseigenschaft automatisch den Fremdschlüssel per Relationship Fixup verändert und umgekehrt. Eine solche Funktion gibt es im Browser aber nicht ohne eigene Programmierung. Initialisiert man st.taskID mit 0, erledigt der mit dem Entity Framework erstellte Server das Relationship Fixup.
Neben der im ersten Teil des Tutorials erstellten einfachen Aufgabenliste bietet MiracleList eine weitere Liste an (siehe Abbildung 1), deren zweite Spalte wiederum eine Liste enthält, und zwar mit Kategorien und ausgewählten Aufgaben. Sie erscheint, wenn der Benutzer per Eingabefeld sucht oder auf „Fällige Aufgaben“ klickt. Für die Suche nach Aufgaben erhält die erste Spalte unten ein Eingabefeld: