Blazor WebAssembly, Teil 2: Eingabesteuerelemente & JavaScript-Interoperabilität

Seite 3: Modaler Dialog beim Löschen

Inhaltsverzeichnis

Für ein rundes Benutzererlebnis fehlt nun noch das Löschen von Kategorien und Aufgaben. Auf dem Bildschirm war dafür bereits in Teil 1 des Tutorials ein runder Kreis mit einem "x" zu jedem Eintrag gerendert worden:

<span id="Remove" style="float:right;"></span>

Jetzt ergänzt man hier jeweils eine Ereignisbehandlung für @onlick:

<span id="Remove" style="float:right;" @onclick="() => RemoveCategory(c)"></span>

beziehungsweise

<span id="Remove" style="float:right;" @onclick="() => RemoveTask(t)"></span>

Für die notwendige Rückfrage bei den Benutzern wird ein einfacher modaler Browserdialog verwendet, wie man ihn in JavaScript mit der Funktion confirm() erzeugt (s. Abb. 2). In der Grundausstattung von Blazor selbst gibt es noch keinen modalen Dialog (Drittanbieter liefern solche Zusatzkomponenten allerdings schon). Auch ohne Investition in eine Komponentenbibliothek gibt es aber eine einfache Lösung, denn Blazor kann JavaScript aufrufen (bei Bedarf geht es auch umgekehrt).

Modale Nachfrage beim Löschen (Abb. 2)

Zum Aufruf einer JavaScript-Funktion können Blazor-Entwickler zunächst eine Instanz der aktuellen JavaScript-Laufzeitumgebung in die Komponente injizieren:

[Inject]
IJSRuntime js { get; set; }

Danach können sie auf dieser Instanz die Methoden InvokeVoidAsync() und InvokeAsync<Rückgabetyp>() aufrufen, zum Beispiel:

if (!await js.InvokeAsync<bool>("confirm", "Remove Task #" + t.TaskID + ": " + t.Title + "?")) return;

Der erste Parameter ist dabei der Name der JavaScript-Funktion, ab dem zweiten Parameter in der Liste werden die Werte an die JavaScript-Funktion als Parameter durchgereicht. Die übergebenen Parameter müssen in JSON serialisierbar sein. Wichtig ist, dass hier der .NET-Typ mit dem JavaScript-Typ korrespondiert. Wenn es nicht möglich ist, den JavaScript-Typ in den .NET-Typ umzuwandeln, kommt es zum Laufzeitfehler "Microsoft.JSInterop.JSException: An exception occurred executing JS interop: The JSON value could not be converted to (…)."

Man kann in Blazor nicht nur eingebaute JavaScript-, sondern auch JavaScript-Funktionen in eigenen Skriptdateien aufrufen. In Blazor Server 3.1 und Blazor WebAssembly 3.2 musste man JavaScript-Dateien noch global per <script>-Tag in die Startseite (bei Blazor WebAssembly: index.html) einbinden:

Seit Blazor-Version 5.0 lassen sich JavaScript-Dateien auch bei Bedarf laden:

JSObjectReference module = await jsRuntime.InvokeAsync<JSObjectReference>("import", "./MeineEigeneSkriptdatei.js");
string ergebnis = await module.InvokeAsync<string>("Funktion", "Parameter");

Listing 4 zeigt die Implementierung beider Ereignisbehandlungsroutinen, die zu Beginn die confirm()-Funktion aufrufen, dann die entsprechende WebAPI-Operation und danach den Bildschirm aktualisieren durch Neuladen der Aufgabenliste. Alternativ könnte man zur Vermeidung eines weiteren Rundgangs zum Server hier auch die Listen lokal bereinigen.

/// <summary>
  /// Ereignisbehandlung: Benutzer löscht Aufgabe
  /// </summary>
  public async System.Threading.Tasks.Task RemoveTask(BO.Task t)
  {
   // Rückfrage (Browser-Dialog via JS!)
   if (!await js.InvokeAsync<bool>("confirm", "Remove Task #" + t.TaskID + ": " + t.Title + "?")) return;
   // Löschen via WebAPI-Aufruf
   await proxy.DeleteTaskAsync(t.TaskID, am.Token);
   // Liste der Aufgaben neu laden
   await ShowTaskSet(this.category);
   // aktuelle Aufgabe zurücksetzen
   this.task = null;
  }

  /// <summary>
  /// Ereignisbehandlung: Benutzer löscht Kategorie
  /// </summary>
  /// <param name="c">zu löschende Kategorie</param>
  public async System.Threading.Tasks.Task RemoveCategory(BO.Category c)
  {
   // Rückfrage (Browser-Dialog via JS!)
   if (!await js.InvokeAsync<bool>("confirm", "Remove Category #" + c.CategoryID + ": " + c.Name + "?")) return;
   // Löschen via WebAPI-Aufruf
   await proxy.DeleteCategoryAsync(c.CategoryID, am.Token);
   // Liste der Kategorien neu laden
   await ShowCategorySet();
   // aktuelle Category zurücksetzen
   this.category = null;
  }

Listing 4: Erweiterungen von Index.razor.cs