© deomis/Shutterstock.com
Wiederverwendung in Blazor mit fortgeschrittenen Razor Components

Komponieren mit Blazor


Razor Components sind sowohl das Grundkonzept von Blazor WebAssembly und Blazor Server als auch der Schlüssel zur effizienten Programmierung durch Kapselung und Wiederverwendung.

Eine Razor Component kann in der @page-Direktive eine Route besitzen und damit per URL aufrufbar sein. Jede Razor Component kann darüber hinaus in andere eingebettet werden. In diesem Beitrag geht es zunächst um die Basistechnik der Einbettung und den Datenaustausch mit dem Host über Parameter und Ereignisse. Danach behandelt der Beitrag fortgeschrittene Mechanismen wie kaskadierende Parameter und Razor Components mit Templates.

Einbetten von Razor Components

Eine Razor Component in eine andere einzubetten geht ganz leicht: Man verwendet ein Tag im Razor-Template-Code, das dem Namen der Komponente (also dem Dateinamen ohne .razor) entspricht. Für eine Razor Component in der Datei CounterPanel.razor schreibt man also das Komponenten-Tag <CounterPanel>. Sofern die Razor Component in einem anderen Namensraum liegt, schreibt man den Namensraumnamen davor:

<ITVisions.Blazor.CounterPanel>

oder verwendet ein @using:

@using ITVisions.Blazor; ... <CounterPanel>

Es ist in Visual Studio (Stand: Visual Studio 2019 v16.7) bisher nicht möglich, die Komponenteneinbettung per Drag and Drop vorzunehmen. Beim Drag and Drop entsteht statt des passenden Komponententags ein <a href="…">-Tag.

Eine Komponente kann mehrfach in eine andere eingebettet sein:

Counter 1: <CounterPanel> Counter 2: <CounterPanel> Counter 3: <CounterPanel>

Eine Komponente kann auch in eine andere eingebettet sein, wenn sie eine eigene Route besitzt (@page "/xy"). In diesem speziellen Fall ist diese Komponente sowohl direkt aufrufbar als auch einbettbar. Eingebettete Komponenten können ebenso wieder andere Komponenten enthalten. Ein Limit der Verschachtelungstiefe ist nicht dokumentiert und auch in der Praxis nicht auf den ersten Blick erkennbar. Sofern der Softwareentwickler aber bei der Verschachtelung eine Rekursion erschafft (z. B. Component1.razor enthält <Component2> und Component2.razor enthält <Component1>), hängt sich die Blazor-Anwendung beim Aufruf einer der beiden Komponenten entsprechend in einer Endlosschleife auf.

Parameter für eingebettete Razor Components

Parameter deklariert eine Razor Component mit der Annotation [Parameter]. Parameter müssen Properties sein (Fields sind nicht erlaubt) und die Sichtbarkeit public besitzen:

[Parameter] public int StartValue { get; set; } = 0;

Die Hostkomponente setzt dann den Wert für die Parameter über ein Attribut im Komponententag:

<CounterPanel StartValue="10">

Dabei kann der Wert auch dynamisch per C#-Code ermittelt werden:

<CounterPanel StartValue="@(Startwert+1)">

Die Parameter können genau wie andere Properties im Razor-Code zur Datenbindung mit @Name bzw. @ bind="Name" verwendet werden.

Razor Components können auch Ereignisse auslösen, um die Nutzer z. B. über Wertänderungen zu informieren. Zur Implementierung eines Komponentenereignisses schreibt ein Blazor-Entwickler einen Parameter vom Typ EventCallback<T>, wobei T der Typ der beim Auslösen des Ereignisses übermittelten Nutzdaten ist. T kann ein primitiver Typ (z. B. int, string, bool) oder ein komplexer Typ (Klasse oder Struktur) sein:

[Parameter] public EventCallback<int> ValueHasChanged { get; set; }

Solch ein Ereignis wird dann bei Bedarf innerhalb der Komponente mit dem Aufruf von InvokeAsync() ausgelöst:

await ValueHasChanged.InvokeAsync(parameter);

Da die Klasse EventCallback ein Wrapper um ein normales .NET-Ereignis ist, ist eine Prüfung auf Null nicht notwendig. Es kommt also auch dann nicht zum Laufzeitfehler, wenn die Host-Komponente keine Ereignisbehandlungsroutine an das Ereignis bindet. Listing 1 zeigt eine Razor Component CounterPanel.Razor (hier aus Gründen der Übersichtlichkeit im Ein-Datei-Modell) mit einem Parameter StartValue und einem Ereignis ValueHasChanged(int), das in der Methode Increment() mit await ValueHasChanged.InvokeAsync(currentValue) ausgelöst wird. Listing 2 zeigt die Verwendung der Razor Component inklusive Ereignisbehandlung in NewValueArrived(). Abbildung 1 visualisiert den Zusammenhang der beiden Komponenten und den Datenfluss zwischen den Komponenten. Abbildung 2 zeigt die entstehende Webseite. In diesem Fall ist die Razor Component durch den Einsatz eines Bootstrap Panels auch optisch klar von ihrem Host abgegrenzt; sie könnte sich aber auch für den Benutzer unsichtbar in den Host integrieren.

schwichtenberg_razorcomponents_1.tif_fmt1.jpgAbb. 1: Datenfluss zwischen Komponentenhost.razor und CounterPanel.razor
schwichtenberg_razorcomponents_2.tif_fmt1.jpgAbb. 2: Komponenteneinbettung: CounterPanel.razor in Komponentenhost.razor

Listing 1: CounterPanel.razor mit einem Parameter und einem Ereignis

<h4>Counter</h4> <div class="my-component"> Diese Razor Component kommt aus der DLL: <strong>@System.Reflection.Assembly.GetExecutingAssembly().GetName().Name</strong> DLL. </div> <p>Current count: @currentValue</p> <button class="btn btn-primary" @onclick="Increment">Klick mich!</button> @code { [Parameter] // Parameter muss Property und public sein! public int StartValue { get; set; } = 0; private int currentValue { get; set; } [Parameter] public EventCallback<int> ValueHasChanged { get; set; } protected override void OnInitialized() { this.currentValue = this.StartValue; } async void Increment() { currentValue++; await ValueHasChanged.InvokeAsync(currentValue); } }

Listing 2: Nutzung der Razor Component aus Listing 1

@page "/Demos/Komponentenhost_EigenerTyp" @using ITVisions.Blazor @inject BlazorUtil Util <h3>Komponentenhost</h3> <CounterPanel StartValue="@(Startwert+1)" ValueHasChanged="NewValueArrived" /> <p> Diese Razor Component <b>@this.GetType().FullName</b> kommt aus der DLL: <b><text>@System.Reflection.Assembly.GetExecutingAssembly().GetName().Name</text>.dll</b>! </p> <p> Die eingebettete Komponente liefert den Wert: @Counter (@Details) </p> @code { public int Startwert { get; set; } = 10; public int Counter { get; set; } public string Details { get; set; } public void NewValueArrived(CounterPanel.ValueHasChangedDetails value) { Counter = value.Counter; Details = value.Text; Util.Log("Zaehler ist nun: " + Counter + " (" + Details + ")"); } }

Kaskadierende Parameter

In der Praxis ergibt sich oft die Situation, dass man Blazor-Komponenten über mehrere Ebenen verschachtelt und Werte über mehrere Ebenen weitergeben möchte. Grundsätzlich kann m...

Neugierig geworden? Wir haben diese Angebote für dich:

Angebote für Gewinner-Teams

Wir bieten Lizenz-Lösungen für Teams jeder Größe: Finden Sie heraus, welche Lösung am besten zu Ihnen passt.

Das Library-Modell:
IP-Zugang

Das Company-Modell:
Domain-Zugang