ASP.NET Core-Razor-Komponenten

In diesem Artikel wird erläutert, wie Sie Razor-Komponenten erstellen und in Blazor-Apps verwenden. Sie finden außerdem Anleitungen zur Razor-Syntax, zu Komponentennamen, Namespaces und Komponentenparametern.

Blazor-Apps werden mit Razor-Komponenten erstellt, die informell als Blazor-Komponenten bezeichnet werden. Eine Komponente ist ein eigenständiger Teil der Benutzeroberfläche mit Verarbeitungslogik, die dynamisches Verhalten ermöglicht. Komponenten können geschachtelt, wiederverwendet, von Projekten gemeinsam genutzt und in MVC- sowie Razor Pages-Apps verwendet werden.

Komponentenklassen

Komponenten werden mithilfe einer Kombination aus C# und HTML-Markup in Razor-Komponentendateien mit der Dateierweiterung .razor implementiert.

Razor-Syntax

Für Komponenten wird die Razor-Syntax verwendet. Von Komponenten werden zwei Razor-Features häufig verwendet: Anweisungen und Anweisungsattribute. Dies sind reservierte Schlüsselwörter, die im Razor-Markup mit dem Präfix @ angezeigt werden:

  • Anweisungen: Diese ändern, wie Komponentenmarkup analysiert oder ausgeführt wird. Beispielsweise gibt die @page-Anweisung eine routingfähige Komponente mit einer Routenvorlage an und kann direkt über die Anforderung eines Benutzers im Browser unter einer bestimmten URL erreicht werden.
  • Anweisungsattribute: Diese ändern, wie ein Komponentenelement analysiert oder ausgeführt wird. Beispielsweise bindet das @bind-Anweisungsattribut für ein <input>-Element Daten an den Wert des Elements.

In Komponenten verwendete Anweisungen und Anweisungsattribute werden in diesem Artikel und in anderen Artikeln der Blazor-Dokumentation näher erläutert. Allgemeine Informationen zur Razor-Syntax finden Sie in der Referenz zur Razor-Syntax für ASP.NET Core.

Namen

Der Name einer Komponente muss mit einem Großbuchstaben beginnen:

  • ProductDetail.razor ist gültig.
  • productDetail.razor ist ungültig.

Allgemeine Blazor-Namenskonventionen, die in der gesamten Blazor-Dokumentation verwendet werden:

  • Für Komponentendateipfade wird die Pascal-Schreibweise† verwendet. Sie werden vor den Komponentencodebeispielen angezeigt. Pfade geben typische Ordnerspeicherorte an. Beispielsweise gibt Pages/ProductDetail.razor an, dass der Dateiname der ProductDetail-Komponente ProductDetail.razor lautet und die Komponente im Ordner Pages der App enthalten ist.
  • In den URLs für Komponentendateipfade routingfähiger Komponenten werden die Wörter in der Routenvorlage einer Komponente durch Bindestriche anstelle von Leerzeichen getrennt. Beispielsweise wird die Komponente ProductDetail mit der Routenvorlage /product-detail (@page "/product-detail") in einem Browser an der relativen URL /product-detail angefordert.

† Bei der Pascal-Schreibweise (Camel-Case-Schreibweise mit großem Anfangsbuchstaben) werden keine Leerzeichen und Interpunktionszeichen verwendet, und der erste Buchstabe jedes Worts, einschließlich des ersten Worts, ist ein Großbuchstabe.

Routing

Das Routing in Blazor erfolgt, indem mit der @page-Anweisung eine Routenvorlage für jede zugängliche Komponente in der App bereitgestellt wird. Wenn eine Razor-Datei mit einer @page-Anweisung kompiliert wird, wird der generierten Klasse RouteAttribute hinzugefügt und so die Routenvorlage angegeben. Zur Laufzeit sucht der Router nach Komponentenklassen mit einem RouteAttribute und rendert die Komponente, deren Routenvorlage mit der angeforderten URL übereinstimmt.

Für die folgende HelloWorld-Komponente wird die Routenvorlage /hello-world verwendet. Die gerenderte Webseite für die Komponente wird über die relative URL /hello-world erreicht. Wenn Sie eine Blazor-App lokal mit dem Standardprotokoll, -host und -port ausführen, wird die HelloWorld-Komponente im Browser unter https://localhost:5001/hello-world angefordert. Komponenten, die Webseiten erzeugen, befinden sich in der Regel im Ordner Pages. Sie können Komponenten jedoch in beliebigen Ordnern speichern, einschließlich geschachtelter Ordner.

Pages/HelloWorld.razor:

@page "/hello-world"

<h1>Hello World!</h1>

Die vorangehende Komponente wird im Browser unter /hello-world geladen, unabhängig davon, ob Sie die Komponente der Benutzeroberflächennavigation der App hinzufügen. Optional können Komponenten zur NavMenu-Komponente hinzugefügt werden, sodass in der Benutzeroberflächennavigation der App ein Link zur Komponente angezeigt wird.

Bei der vorherigen HelloWorld-Komponente können Sie eine NavLink-Komponente zur NavMenu-Komponente im Shared-Ordner hinzufügen. Weitere Informationen, einschließlich Beschreibungen der Komponenten NavLink und NavMenu, finden Sie unter Routing und Navigation in Blazor in ASP.NET Core.

Markup

Die Benutzeroberfläche einer Komponente wird mithilfe der Razor-Syntax definiert, die aus Razor-Markup, C# und HTML besteht. Wenn eine App kompiliert wird, werden das HTML-Markup und die C#-Renderinglogik in eine Komponentenklasse konvertiert. Der Name der generierten Klasse entspricht dem Namen der Datei.

Member der Komponentenklasse werden in einem oder mehreren @code-Blöcken definiert. In @code-Blöcken wird der Komponentenzustand angegeben und mit C# verarbeitet:

  • Eigenschaften- und Feldinitialisierer
  • Parameterwerte aus Argumenten, die von übergeordneten Komponenten und Routenparametern übergeben werden
  • Methoden für die Behandlung von Benutzerereignissen, Lebenszyklusereignissen und benutzerdefinierter Komponentenlogik

Komponentenmember werden in der Renderinglogik mithilfe von C#-Ausdrücken verwendet, die mit dem Symbol @ beginnen. Beispielsweise wird ein C#-Feld gerendert, indem @ dem Feldnamen vorangestellt wird. Durch die unten gezeigte Markup-Komponente wird Folgendes ausgewertet und gerendert:

  • headingFontStyle für den CSS-Eigenschaftswert font-style des Überschriftenelements.
  • headingText für den Inhalt des Überschriftenelements.

Pages/Markup.razor:

@page "/markup"

<h1 style="font-style:@headingFontStyle">@headingText</h1>

@code {
    private string headingFontStyle = "italic";
    private string headingText = "Put on your new Blazor!";
}

Hinweis

In den Beispielen in der gesamten Blazor-Dokumentation wird für private Member der private-Zugriffsmodifizierer angegeben. Der Bereich privater Member ist auf die Klasse einer Komponente beschränkt. In C# wird jedoch der private-Zugriffsmodifizierer angenommen, wenn kein Zugriffsmodifizierer vorhanden ist. Daher ist das explizite Markieren von Membern als private im eigenen Code optional. Weitere Informationen zu Zugriffsmodifizierern finden Sie unter Zugriffsmodifizierer (C#-Programmierhandbuch).

Das Blazor-Framework verarbeitet eine Komponente intern als Renderstruktur. Dies ist die Kombination aus dem Dokumentobjektmodell (DOM) einer Komponente und dem Cascading Style Sheet Object Model (CSSOM). Nachdem die Komponente zum ersten Mal gerendert wurde, wird ihre Renderstruktur als Reaktion auf Ereignisse erneut generiert. Blazor vergleicht die neue Renderstruktur mit der vorherigen und wendet alle Änderungen auf das DOM des Browsers für die Anzeige an. Weitere Informationen finden Sie unter Rendering von Razor-Komponenten in ASP.NET Core.

Komponenten sind normale C#-Klassen und können an beliebiger Stelle innerhalb eines Projekts eingefügt werden. Komponenten, die Webseiten erzeugen, befinden sich in der Regel im Ordner Pages. Nicht für Seiten verwendete Komponenten werden häufig im Ordner Shared oder einem benutzerdefinierten Ordner gespeichert, der dem Projekt hinzugefügt wurde.

Laut Razor-Syntax werden für C#-Steuerelementstrukturen, Anweisungen und Anweisungsattribute Kleinbuchstaben verwendet (z. B. @if, @code, @bind). Eigenschaftennamen stehen in Großbuchstaben (z. B. @Body für LayoutComponentBase.Body).

Asynchrone Methoden (async) unterstützen nicht die Rückgabe von void.

Das Blazor-Framework verfolgt keine asynchronen Methoden (async) nach, die void zurückgeben. Folglich werden keine Ausnahmen abgefangen, wenn void zurückgegeben wird. Lassen Sie asynchrone Methoden stets Task zurückgeben.

Geschachtelte Komponenten

Komponenten können andere Komponenten enthalten, sofern Sie sie mithilfe der HTML-Syntax deklarieren. Das Markup für die Verwendung einer Komponente sieht wie ein HTML-Tag aus, wobei der Name des Tags der Typ der Komponente ist.

Betrachten Sie die folgende Heading-Komponente, die von anderen Komponenten verwendet werden kann, um eine Überschrift anzuzeigen.

Shared/Heading.razor:

<h1 style="font-style:@headingFontStyle">Heading Example</h1>

@code {
    private string headingFontStyle = "italic";
}

Das folgende Markup in der HeadingExample-Komponente rendert die vorherige Heading-Komponente an der Position, an der das <Heading />-Tag angezeigt wird.

Pages/HeadingExample.razor:

@page "/heading-example"

<Heading />

Wenn eine Komponente ein HTML-Element mit einem großgeschriebenen ersten Buchstaben enthält, der nicht mit einem Komponentennamen in demselben Namespace identisch ist, wird eine Warnung ausgegeben, dass das Element einen unerwarteten Namen aufweist. Durch das Hinzufügen einer @using-Anweisung für den Namespace der Komponente wird die Komponente zur Verfügung gestellt, wodurch die Warnung nicht mehr auftritt. Weitere Informationen finden Sie im Abschnitt Namespaces.

Das in diesem Abschnitt gezeigte Beispiel für die Heading-Komponente weist keine @page-Anweisung auf. Daher können Benutzer nicht über eine Anforderung im Browser direkt auf die Heading-Komponente zugreifen. Jede Komponente mit einer @page-Anweisung kann jedoch in einer anderen Komponente geschachtelt werden. Wenn auf die Heading-Komponente direkt zugegriffen werden kann, indem am Anfang der Razor-Datei @page "/heading" eingeschlossen wird, wird die Komponente für Browseranforderungen sowohl unter /heading als auch unter /heading-example gerendert.

Namespaces

In der Regel wird der Namespace einer Komponente aus dem Stammnamespace der App und dem Speicherort (Ordner) der Komponente in der App abgeleitet. Wenn der Stammnamespace der App BlazorSample lautet und sich die Counter Komponente im Ordner Pages befindet, gilt Folgendes:

  • der Namespace der Counter-Komponente BlazorSample.Pages, und
  • der vollqualifizierten Typname der Komponente ist BlazorSample.Pages.Counter.

Fügen Sie für benutzerdefinierte Ordner, die Komponenten enthalten, der übergeordneten Komponente oder der _Imports.razor-Datei der App eine @using-Anweisung hinzu. Das folgende Beispiel stellt Komponenten im Ordner Components zur Verfügung:

@using BlazorSample.Components

Hinweis

@using-Direktive in der _Imports.razor-Datei werden nur auf Razor-Dateien (.razor) angewendet und nicht auf C#-Dateien (.cs).

Auf Komponenten kann auch mit ihrem vollqualifizierten Namen verwiesen werden. Hierfür ist keine @using-Anweisung erforderlich. Im folgenden Beispiel wird direkt auf die ProductDetail-Komponente im Ordner Components der App verwiesen:

<BlazorSample.Components.ProductDetail />

Der Namespace einer mit Razor erstellten Komponente basiert auf Folgendem (nach Priorität):

  • Die @namespace-Anweisung im Markup der Razor-Datei (z. B. @namespace BlazorSample.CustomNamespace).
  • Der RootNamespace des Projekts in der Projektdatei (z. B. <RootNamespace>BlazorSample</RootNamespace>).
  • Der Projektname, der aus dem Namen der Projektdatei (.csproj) und dem Pfad vom Projektstamm zur Komponente resultiert. Das Framework löst beispielsweise für die Index-Komponente den Pfad {PROJECT ROOT}/Pages/Index.razor mit dem Projektnamespace BlazorSample (BlazorSample.csproj) in den Namespace BlazorSample.Pages auf. {PROJECT ROOT} ist der Projektstammpfad. Komponenten folgen den Namensbindungsregeln für C#. Für die Index-Komponente in diesem Beispiel werden folgende Komponenten berücksichtigt:
    • Im gleichen Ordner: Pages.
    • Komponenten im Stammverzeichnis des Projekts, die nicht explizit einen anderen Namespace angeben

Folgendes wird nicht unterstützt:

  • Die global::-Qualifizierung.
  • Das Importieren von Komponenten mit using-Aliasanweisungen. @using Foo = Bar wird beispielsweise nicht unterstützt.
  • Unvollständig qualifizierte Namen. Beispielsweise können Sie einer Komponente nicht @using BlazorSample hinzufügen und dann mit <Shared.NavMenu></Shared.NavMenu> auf die NavMenu-Komponente im Ordner Shared (Shared/NavMenu.razor) der App verweisen.

Unterstützung für partielle Klassen

Komponenten werden als partielle C#-Klassen generiert und mit einer der folgenden Vorgehensweisen erstellt:

  • Eine einzelne Datei enthält C#-Code, der in einem oder mehreren @code-Blöcken, HTML-Markup und Razor-Markup definiert ist. In Blazor-Projektvorlagen werden Komponenten mithilfe einer einzelnen Datei definiert.
  • HTML- und Razor-Markup werden in einer Razor-Datei (.razor) gespeichert. C#-Code wird in einer CodeBehind-Datei gespeichert, die als partielle Klasse (.cs) definiert ist.

Hinweis

Ein Komponentenstylesheet, das komponentenspezifische Stile definiert, ist eine eigene Datei (.css). Die CSS-Isolation von Blazor wird weiter unten unter CSS-Isolation von Blazor in ASP.NET Core beschrieben.

Das folgende Beispiel zeigt die Counter-Standardkomponente mit einem @code-Block in einer App, die aus einer Blazor-Projektvorlage generiert wurde. Markup und C#-Code befinden sich in derselben Datei. Dies ist der häufigste Ansatz bei der Komponentenerstellung.

Pages/Counter.razor:

@page "/counter"

<PageTitle>Counter</PageTitle>

<h1>Counter</h1>

<p role="status">Current count: @currentCount</p>

<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>

@code {
    private int currentCount = 0;

    private void IncrementCount()
    {
        currentCount++;
    }
}

In der folgenden Counter-Komponente wird das HTML- und Razor-Markup aus C#-Code unter Verwendung einer CodeBehind-Datei mit einer partiellen Klasse aufgeteilt:

Pages/CounterPartialClass.razor:

@page "/counter-partial-class"

<PageTitle>Counter</PageTitle>

<h1>Counter</h1>

<p role="status">Current count: @currentCount</p>

<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>

Pages/CounterPartialClass.razor.cs:

namespace BlazorSample.Pages
{
    public partial class CounterPartialClass
    {
        private int currentCount = 0;

        void IncrementCount()
        {
            currentCount++;
        }
    }
}

@using-Direktive in der _Imports.razor-Datei werden nur auf Razor-Dateien (.razor) angewendet und nicht auf C#-Dateien (.cs). Fügen Sie einer partiellen Klassendatei ggf. Namespaces hinzu.

Typische Namespaces, die von Komponenten verwendet werden:

using System.Net.Http;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Components.Authorization;
using Microsoft.AspNetCore.Components.Forms;
using Microsoft.AspNetCore.Components.Routing;
using Microsoft.AspNetCore.Components.Web;
using Microsoft.AspNetCore.Components.Web.Virtualization;
using Microsoft.JSInterop;

Typische Namespaces enthalten auch den Namespace der App und den Namespace, der dem Ordner Shared der App entspricht:

using BlazorSample;
using BlazorSample.Shared;

Angeben einer Basisklasse

Die @inherits-Anweisung wird verwendet, um eine Basisklasse für eine Komponente anzugeben. Das folgende Beispiel zeigt, wie eine Komponente eine Basisklasse erben kann, um die Eigenschaften und Methoden der Komponente bereitzustellen. Die Basisklasse BlazorRocksBase wird von ComponentBase abgeleitet.

Pages/BlazorRocks.razor:

@page "/blazor-rocks"
@inherits BlazorRocksBase

<h1>@BlazorRocksText</h1>

BlazorRocksBase.cs:

using Microsoft.AspNetCore.Components;

namespace BlazorSample
{
    public class BlazorRocksBase : ComponentBase
    {
        public string BlazorRocksText { get; set; } =
            "Blazor rocks the browser!";
    }
}

Komponentenparameter

Komponentenparameter übergeben Daten an Komponenten und werden mit öffentlichen C#-Eigenschaften für die Komponentenklasse mit dem [Parameter]-Attribut definiert. Im folgenden Beispiel werden ein integrierter Verweistyp (System.String) und ein benutzerdefinierter Verweistyp (PanelBody) als Komponentenparameter übergeben.

PanelBody.cs:

public class PanelBody
{
    public string? Text { get; set; }
    public string? Style { get; set; }
}

Shared/ParameterChild.razor:

<div class="card w-25" style="margin-bottom:15px">
    <div class="card-header font-weight-bold">@Title</div>
    <div class="card-body" style="font-style:@Body.Style">
        @Body.Text
    </div>
</div>

@code {
    [Parameter]
    public string Title { get; set; } = "Set By Child";

    [Parameter]
    public PanelBody Body { get; set; } =
        new()
        {
            Text = "Set by child.",
            Style = "normal"
        };
}

Warnung

Die Bereitstellung von Anfangswerten für Komponentenparameter wird unterstützt. Erstellen Sie jedoch keine Komponente, die in die eigenen Parameter schreibt, nachdem die Komponente zum ersten Mal gerendert wurde. Weitere Informationen finden Sie im Abschnitt Überschriebene Parameter dieses Artikels.

Die Komponentenparameter Title und Body der ParameterChild-Komponente werden durch Argumente im HTML-Tag festgelegt, das die Instanz der Komponente rendert. Die folgende ParameterParent-Komponente rendert zwei ParameterChild-Komponenten:

  • Die erste ParameterChild-Komponente wird ohne Angabe von Parameterargumenten gerendert.
  • Die zweite ParameterChild-Komponente empfängt Werte für Title und Body von der ParameterParent-Komponente, die mit einem expliziten C#-Ausdruck die Werte der Eigenschaften von PanelBody festlegt.

Pages/ParameterParent.razor:

@page "/parameter-parent"

<h1>Child component (without attribute values)</h1>

<ParameterChild />

<h1>Child component (with attribute values)</h1>

<ParameterChild Title="Set by Parent"
                Body="@(new PanelBody() { Text = "Set by parent.", Style = "italic" })" />

Das folgende gerenderte HTML-Markup der ParameterParent-Komponente enthält Standardwerte der ParameterChild-Komponente, wenn die ParameterParent-Komponente keine Komponentenparameterwerte angibt. Wenn die ParameterParent-Komponente Komponentenparameterwerte angibt, ersetzen diese die Standardwerte der ParameterChild-Komponente.

Hinweis

Im folgenden gerenderten HTML-Markup werden aus Gründen der Übersichtlichkeit keine gerenderten CSS-Formatklassen angezeigt.

<h1>Child component (without attribute values)</h1>

<div>
    <div>Set By Child</div>
    <div>Set by child.</div>
</div>

<h1>Child component (with attribute values)</h1>

<div>
    <div>Set by Parent</div>
    <div>Set by parent.</div>
</div>

Weisen Sie mit dem reservierten Symbol @ von Razor einem Komponentenparameter ein C#-Feld, eine C#-Eigenschaft oder ein Ergebnis einer C#-Methode als HTML-Attributwert zu. Die folgende ParameterParent2-Komponente zeigt vier Instanzen der vorangehenden ParameterChild-Komponente an und legt ihre Title-Parameterwerte folgendermaßen fest:

  • Der Wert des title-Felds.
  • Das Ergebnis der GetTitle-C#-Methode.
  • Das aktuelle lokale Datum im langen Format mit ToLongDateString unter Verwendung eines impliziten C#-Ausdrucks.
  • Die Title-Eigenschaft des panelData-Objekts.

Das Präfix @ ist für Zeichenfolgeparameter erforderlich. Andernfalls geht das Framework davon aus, dass ein Zeichenfolgenliteral festgelegt ist.

Außerhalb von Zeichenfolgen empfehlen wir die Verwendung des Präfixes @ für Nichtliterale, auch wenn sie nicht unbedingt erforderlich sind.

Wir empfehlen die Verwendung des Präfixes @ nicht für Literale (z. B. boolesche Werte), Schlüsselwörter (z. B. this) oder null, aber Sie können sie auf Wunsch verwenden. Zum Beispiel ist IsFixed="@true" ungewöhnlich, wird aber unterstützt.

Anführungszeichen um die Werte von Parameterattributen sind gemäß der HTML5-Spezifikation in den meisten Fällen optional. Beispielsweise wird Value=this anstelle von Value="this" unterstützt. Wir empfehlen jedoch die Verwendung von Anführungszeichen, da sie leichter zu merken und bei webbasierten Technologien weit verbreitet sind.

In der gesamten Dokumentation gibt für Codebeispiele Folgendes:

  • Es werden immer Anführungszeichen verwendet. Beispiel: Value="this".
  • Nichtliterale Zeichen verwenden stets das Präfix @, auch wenn es optional ist. Beispiele: Title="@title", wobei title eine Zeichenfolgenvariable ist. Count="@ct", wobei ct eine numerische Variable ist.
  • Literale außerhalb von Razor-Ausdrücken vermeiden stets @. Beispiel: IsFixed="true".

Pages/ParameterParent2.razor:

@page "/parameter-parent-2"

<ParameterChild Title="@title" />

<ParameterChild Title="@GetTitle()" />

<ParameterChild Title="@DateTime.Now.ToLongDateString()" />

<ParameterChild Title="@panelData.Title" />

@code {
    private string title = "From Parent field";
    private PanelData panelData = new();

    private string GetTitle()
    {
        return "From Parent method";
    }

    private class PanelData
    {
        public string Title { get; set; } = "From Parent object";
    }
}

Hinweis

Wenn Sie einem Komponentenparameter ein C#-Element zuweisen, stellen Sie dem Element das Symbol @ voran und nie das HTML-Attribut des Parameters.

Richtig:

<ParameterChild Title="@title" />

Falsch:

<ParameterChild @Title="title" />

Anders als bei Razor-Seiten (.cshtml) kann Blazor keine asynchronen Aufgaben in einem Razor-Ausdruck ausführen, während eine Komponente gerendert wird. Der Grund hierfür ist, dass Blazor für das Rendering interaktiver Benutzeroberflächen konzipiert ist. In einer interaktiven Benutzeroberfläche muss auf dem Bildschirm immer etwas angezeigt werden. Daher ist es nicht sinnvoll, den Renderingfluss zu blockieren. Stattdessen wird die asynchrone Arbeit während eines der asynchronen Lebenszyklusereignisse ausgeführt. Nach jedem asynchronen Lebenszyklusereignis kann die Komponente wieder gerendert werden. Folgende Razor-Syntax wird nicht unterstützt:

<ParameterChild Title="@await ..." />

Der Code im vorangehenden Beispiel generiert einen Compilerfehler, wenn die App erstellt wird:

Der 'await'-Operator kann nur innerhalb einer 'async'-Methode verwendet werden. Markieren Sie ggf. diese Methode mit dem 'async'-Modifizierer, und ändern Sie ihren Rückgabetyp in 'Task'.

Um einen Wert für den Parameter Title im vorangehenden Beispiel asynchron abzurufen, kann die Komponente das Lebenszyklusereignis OnInitializedAsync verwenden. Dies wird im folgenden Beispiel veranschaulicht:

<ParameterChild Title="@title" />

@code {
    private string? title;

    protected override async Task OnInitializedAsync()
    {
        title = await ...;
    }
}

Weitere Informationen finden Sie unter Rendering von Razor-Komponenten in ASP.NET Core.

Die Verwendung eines expliziten Razor-Ausdrucks zur Verkettung von Text mit einem Ausdrucksergebnis für die Zuweisung zu einem Parameter wird nicht unterstützt. Im folgenden Beispiel wird versucht, den Text Set by mit dem Eigenschaftswert eines Objekts zu verketten. Obwohl diese Syntax in einer Razor-Seite (.cshtml) unterstützt wird, ist sie nicht für die Zuweisung zum Title-Parameter des untergeordneten Elements in einer Komponente gültig. Folgende Razor-Syntax wird nicht unterstützt:

<ParameterChild Title="Set by @(panelData.Title)" />

Der Code im vorangehenden Beispiel generiert einen Compilerfehler, wenn die App erstellt wird:

Komponentenattribute unterstützen keine komplexen Inhalte (C# und Markup gemischt).

Um die Zuweisung eines zusammengesetzten Werts zu unterstützen, verwenden Sie eine Methode, ein Feld oder eine Eigenschaft. Im folgenden Beispiel wird die Verkettung von Set by und dem Eigenschaftswert eines Objekts in der C#-Methode GetTitle ausgeführt:

Pages/ParameterParent3.razor:

@page "/parameter-parent-3"

<ParameterChild Title="@GetTitle()" />

@code {
    private PanelData panelData = new();

    private string GetTitle() => $"Set by {panelData.Title}";

    private class PanelData
    {
        public string Title { get; set; } = "Parent";
    }
}

Weitere Informationen finden Sie unter Razor-Syntaxreferenz für ASP.NET Core.

Warnung

Die Bereitstellung von Anfangswerten für Komponentenparameter wird unterstützt. Erstellen Sie jedoch keine Komponente, die in die eigenen Parameter schreibt, nachdem die Komponente zum ersten Mal gerendert wurde. Weitere Informationen finden Sie im Abschnitt Überschriebene Parameter dieses Artikels.

Komponentenparameter sollten als Auto-Eigenschaften deklariert werden, d. h., ihre get- oder set-Zugriffsmethode sollte keine benutzerdefinierte Logik enthalten. Beispielsweise ist die folgende StartData-Eigenschaft eine Auto-Eigenschaft:

[Parameter]
public DateTime StartData { get; set; }

Platzieren Sie die benutzerdefinierte Logik nicht in der get- oder set-Zugriffsmethode, da Komponentenparameter ausschließlich zur Verwendung als Kanal für eine übergeordnete Komponente vorgesehen sind, über den Informationen an eine untergeordnete Komponente fließen. Wenn eine set-Zugriffsmethode einer untergeordneten Komponenteneigenschaft Logik enthält, die das erneute Rendern der übergeordneten Komponente bewirkt, resultiert daraus eine Rendering-Endlosschleife.

So transformieren Sie einen empfangenen Parameterwert:

  • Belassen Sie die Parametereigenschaft als Auto-Eigenschaft, um die bereitgestellten Rohdaten darzustellen.
  • Erstellen Sie eine andere Eigenschaft oder Methode, um die transformierten Daten basierend auf der Parametereigenschaft bereitzustellen.

Überschreiben Sie OnParametersSetAsync, um einen empfangenen Parameter jedes Mal zu transformieren, wenn neue Daten empfangen werden.

Das Schreiben eines Anfangswerts in einen Komponentenparameter wird unterstützt, da anfängliche Wertzuweisungen das automatische Komponentenrendering von Blazor nicht beeinträchtigen. Die folgende Zuweisung der aktuellen lokalen DateTime mit DateTime.Now zu StartData ist eine gültige Syntax in einer Komponente:

[Parameter]
public DateTime StartData { get; set; } = DateTime.Now;

Weisen Sie nach der anfänglichen Zuweisung von DateTime.Now im Entwicklercode keinen Wert zu StartData zu. Weitere Informationen finden Sie im Abschnitt Überschriebene Parameter dieses Artikels.

Wenden Sie das [EditorRequired]-Attribut an, um einen erforderlichen Komponentenparameter anzugeben. Wenn kein Parameterwert angegeben wird, zeigen Editoren oder Buildtools dem Benutzer u. U. Warnungen an. Dieses Attribut ist nur für Eigenschaften gültig, die ebenfalls mit dem [Parameter]-Attribut markiert sind. EditorRequiredAttribute wird zur Entwurfszeit und beim Erstellen der App erzwungen. Das Attribut wird zur Laufzeit nicht erzwungen und garantiert keinen Nicht-null-Parameterwert.

[Parameter]
[EditorRequired]
public string? Title { get; set; }

Einzeilige Attributlisten werden ebenfalls unterstützt:

[Parameter, EditorRequired]
public string? Title { get; set; }

Tuples (API-Dokumentation) werden für Komponentenparameter und RenderFragment-Typen unterstützt. Im folgenden Komponentenparameterbeispiel werden drei Werte in einem Tuple übergeben:

Shared/RenderTupleChild.razor:

<div class="card w-50" style="margin-bottom:15px">
    <div class="card-header font-weight-bold"><code>Tuple</code> Card</div>
    <div class="card-body">
        <ul>
            <li>Integer: @Data?.Item1</li>
            <li>String: @Data?.Item2</li>
            <li>Boolean: @Data?.Item3</li>
        </ul>
    </div>
</div>

@code {
    [Parameter]
    public Tuple<int, string, bool>? Data { get; set; }
}

Pages/RenderTupleParent.razor:

@page "/render-tuple-parent"

<h1>Render <code>Tuple</code> Parent</h1>

<RenderTupleChild Data="@data" />

@code {
    private Tuple<int, string, bool> data = new(999, "I aim to misbehave.", true);
}

Nur unbenannte Tupel werden für C# 7.0 oder höher in Razor-Komponenten unterstützt. Die Unterstützung von benannten Tupeln in Razor-Komponenten ist für ein künftiges ASP.NET Core-Release geplant. Weitere Informationen finden Sie unter Problem mit dem Blazor-Transpiler mit benannten Tupeln (dotnet/aspnetcore #28982).

Quote ©2005 Universal Pictures: Serenity (Nathan Fillion)

Routenparameter

Komponenten können in der Routenvorlage der @page-Anweisung Routenparameter angeben. Der Blazor-Router verwendet Routenparameter, um entsprechende Komponentenparameter aufzufüllen.

Optionale Routenparameter werden unterstützt. Im folgenden Beispiel weist der optionale Parameter text den Wert des Routensegments der Eigenschaft Text der Komponente zu. Wenn das Segment nicht vorhanden ist, wird der Wert von Text in der OnInitialized-Lebenszyklusmethode auf fantastic festgelegt.

Pages/RouteParameter.razor:

@page "/route-parameter/{text?}"

<h1>Blazor is @Text!</h1>

@code {
    [Parameter]
    public string? Text { get; set; }

    protected override void OnInitialized()
    {
        Text = Text ?? "fantastic";
    }
}

Informationen zu Catch-All-Routenparametern ({*pageRoute}), die Pfade über mehrere Ordnergrenzen hinweg erfassen, finden Sie unter Routing und Navigation in Blazor in ASP.NET Core .

Untergeordnete Inhaltsrenderfragmente

Komponenten können den Inhalt anderer Komponenten festlegen. Die zuweisende Komponente stellt den Inhalt zwischen dem öffnenden und schließenden Tag der untergeordneten Komponente bereit.

Im folgenden Beispiel enthält die RenderFragmentChild-Komponente einen Parameter der ChildContent-Komponenten, der ein als RenderFragment zu renderndes Segment der Benutzeroberfläche darstellt. In der endgültigen HTML-Ausgabe wird der Inhalt an der Position von ChildContent im Razor-Markup der Komponente gerendert.

Shared/RenderFragmentChild.razor:

<div class="card w-25" style="margin-bottom:15px">
    <div class="card-header font-weight-bold">Child content</div>
    <div class="card-body">@ChildContent</div>
</div>

@code {
    [Parameter]
    public RenderFragment? ChildContent { get; set; }
}

Wichtig

Die Eigenschaft, die den Inhalt von RenderFragment empfängt, muss gemäß der Konvention ChildContent benannt werden.

Ereignisrückrufe werden für RenderFragment nicht unterstützt.

Die folgende RenderFragmentParent-Komponente stellt Inhalt für das Rendern des RenderFragmentChild durch Einfügen des Inhalts zwischen dem öffnenden und schließenden Tag der untergeordneten Komponente bereit.

Pages/RenderFragmentParent.razor:

@page "/render-fragment-parent"

<h1>Render child content</h1>

<RenderFragmentChild>
    Content of the child component is supplied
    by the parent component.
</RenderFragmentChild>

Aufgrund der Art und Weise, in der Blazor untergeordneten Inhalt rendert, erfordert das Rendern von Komponenten in einer for-Schleife eine lokale Indexvariable, wenn die inkrementierende Schleifenvariable im Inhalt der RenderFragmentChild-Komponente verwendet wird. Das folgende Beispiel kann der vorherigen RenderFragmentParent-Komponente hinzugefügt werden:

<h1>Three children with an index variable</h1>

@for (int c = 0; c < 3; c++)
{
    var current = c;

    <RenderFragmentChild>
        Count: @current
    </RenderFragmentChild>
}

Alternativ können Sie anstelle einer for-Schleife eine foreach-Schleife mit Enumerable.Range verwenden. Das folgende Beispiel kann der vorherigen RenderFragmentParent-Komponente hinzugefügt werden:

<h1>Second example of three children with an index variable</h1>

@foreach (var c in Enumerable.Range(0,3))
{
    <RenderFragmentChild>
        Count: @c
    </RenderFragmentChild>
}

Renderfragmente werden dazu verwendet, untergeordnete Inhalte in allen Blazor Apps zu rendern und werden in den folgenden Artikeln und Artikelabschnitten mit Beispielen beschrieben:

Hinweis

Die in das Blazor Framework integrierten Razor Komponenten verwenden die selbe ChildContent Komponentenparameterkonvention, um ihren Inhalt festzulegen. Sie können die Komponenten sehen, die untergeordnete Inhalte festlegen, indem Sie in der API Dokumentation nach dem Komponenten-Parametereigenschaftsnamen ChildContent suchen (filtert API mit dem Suchbegriff "ChildContent").

Rendern von Fragmenten für wiederverwendbare Renderinglogik

Sie können untergeordnete Komponenten ausschließlich als Möglichkeit zum Wiederverwenden von Renderinglogik ausklammern. Definieren Sie in jedem @code Block der Komponente ein RenderFragment und rendern Sie das Fragment von einem beliebigen Ort aus so oft wie nötig:

<h1>Hello, world!</h1>

@RenderWelcomeInfo

<p>Render the welcome info a second time:</p>

@RenderWelcomeInfo

@code {
    private RenderFragment RenderWelcomeInfo = __builder =>
    {
        <p>Welcome to your new app!</p>
    };
}

Weitere Informationen finden Sie unter Wiederverwenden von Renderinglogik.

Überschriebene Parameter

Das Blazor-Framework erzwingt im Allgemeinen eine sichere Parameterzuordnung von über- und untergeordneten Elementen:

  • Parameter werden nicht unerwartet überschrieben.
  • Nebenwirkungen werden auf ein Minimum reduziert. Beispielsweise werden zusätzliche Rendervorgänge vermieden, da Sie zu unendlichen Renderingschleifen führen können.

Eine untergeordnete Komponente empfängt neue Parameterwerte, die möglicherweise vorhandene Werte überschreiben, wenn die übergeordnete Komponente erneut gerendert wird. Das versehentliche Überschreiben von Parameterwerten in einer untergeordneten Komponente tritt häufig auf, wenn die Komponente mit mindestens einem datengebundenen Parameter entwickelt wird und der Entwickler direkt in einen Parameter im untergeordneten Element schreibt:

  • Die untergeordnete Komponente wird mit mindestens einem Parameterwert aus der übergeordneten Komponente gerendert.
  • Das untergeordnete Element schreibt direkt in den Wert eines Parameters.
  • Die übergeordnete Komponente wird erneut gerendert und überschreibt den Wert des Parameters des untergeordneten Elements.

Die Möglichkeit, Parameterwerte zu überschreiben, gilt auch für set-Eigenschaftenzugriffsmethoden der untergeordneten Komponente.

Wichtig

Grundsätzlich wird empfohlen, keine Komponenten zu erstellen, die direkt in die eigenen Parameter schreiben, nachdem die Komponente zum ersten Mal gerendert wurde.

Angenommen, die folgende Expander-Komponente:

  • Rendert untergeordneten Inhalt.
  • Schaltet die Anzeige von untergeordnetem Inhalt mit einem Komponentenparameter (Expanded) um.

Nachdem die folgende Expander-Komponente einen überschriebenen Parameter gezeigt hat, wird eine modifizierte Expander-Komponente gezeigt, um den richtigen Ansatz für dieses Szenario zu veranschaulichen. Die folgenden Beispiele können in eine lokale Beispiel-App eingefügt werden, um die beschriebenen Verhaltensweisen nachzuvollziehen.

Shared/Expander.razor:

<div @onclick="Toggle" class="card bg-light mb-3" style="width:30rem">
    <div class="card-body">
        <h2 class="card-title">Toggle (<code>Expanded</code> = @Expanded)</h2>

        @if (Expanded)
        {
            <p class="card-text">@ChildContent</p>
        }
    </div>
</div>

@code {
    [Parameter]
    public bool Expanded { get; set; }

    [Parameter]
    public RenderFragment? ChildContent { get; set; }

    private void Toggle()
    {
        Expanded = !Expanded;
    }
}

Die Expander-Komponente wird der folgenden übergeordneten ExpanderExample-Komponente hinzugefügt, die StateHasChanged aufrufen kann:

Pages/ExpanderExample.razor:

@page "/expander-example"

<Expander Expanded="true">
    Expander 1 content
</Expander>

<Expander Expanded="true" />

<button @onclick="StateHasChanged">
    Call StateHasChanged
</button>

Anfänglich verhalten sich die Expander-Komponenten unabhängig, wenn ihre Expanded-Eigenschaften umgeschaltet werden. Die untergeordneten Komponenten behalten ihre Zustände erwartungsgemäß bei.

Wenn StateHasChanged in einer übergeordneten Komponente aufgerufen wird, rendert das Blazor-Framework untergeordnete Komponenten neu, wenn sich ihre Parameter geändert haben könnten:

  • Bei einer Gruppe von Parametertypen, die von Blazor explizit überprüft werden, rendert Blazor eine untergeordnete Komponente neu, wenn erkannt wird, dass sich Parameter geändert haben.
  • Bei nicht überprüften Parametertypen wird die untergeordnete Blazor-Komponente neu gerendert – unabhängig davon, ob sich die Parameter geändert haben oder nicht. Untergeordneter Inhalt fällt in diese Kategorie von Parametertypen, weil untergeordneter Inhalt dem Typ RenderFragment entspricht, einem Delegat, der auf andere stummschaltbare Objekte verweist.

Für die ExpanderExample-Komponente:

  • Die erste Expander-Komponente legt untergeordneten Inhalt in einem potenziell stummschaltbaren RenderFragment fest, sodass ein Aufruf von StateHasChanged in der übergeordneten Komponente die Komponente automatisch neu rendert und möglicherweise den Wert von Expanded mit dem Anfangswert true überschreibt.
  • Die zweite Expander-Komponente legt keinen untergeordneten Inhalt fest. Daher gibt es keinen potenziell stummschaltbaren RenderFragment. Durch einen Aufruf von StateHasChanged in der übergeordneten Komponente wird die untergeordnete Komponente nicht automatisch neu gerendert, sodass der Expanded-Wert der Komponente nicht überschrieben wird.

Um den Zustand im vorangehenden Szenario beizubehalten, verwenden Sie ein privates Feld in der Expander-Komponente, um dessen Umschaltungszustand beizubehalten.

Die folgende überarbeitete Expander-Komponente:

  • akzeptiert den Wert des Expanded-Komponentenparameters aus der übergeordneten Komponente.
  • weist den Wert des Komponentenparameters einem privaten Feld (expanded) im OnInitialized-Ereignis zu.
  • Verwendet das private Feld, um den internen Umschaltzustand zu verwalten. Dies veranschaulicht, wie das direkte Schreiben in einen Parameter vermieden wird.

Hinweis

Die Hinweise in diesem Abschnitt gelten auch für ähnliche Logik in den set-Zugriffsmethoden für Komponentenparameter, die zu ähnlichen unerwünschten Nebenwirkungen führen kann.

Shared/Expander.razor:

<div @onclick="Toggle" class="card bg-light mb-3" style="width:30rem">
    <div class="card-body">
        <h2 class="card-title">Toggle (<code>expanded</code> = @expanded)</h2>

        @if (expanded)
        {
            <p class="card-text">@ChildContent</p>
        }
    </div>
</div>

@code {
    private bool expanded;

    [Parameter]
    public bool Expanded { get; set; }

    [Parameter]
    public RenderFragment? ChildContent { get; set; }

    protected override void OnInitialized()
    {
        expanded = Expanded;
    }

    private void Toggle()
    {
        expanded = !expanded;
    }
}

Beispiele für bidirektionale übergeordnete und untergeordnete Bindungen finden Sie unter Datenbindung in Blazor in ASP.NET Core. Weitere Informationen finden Sie unter Bidirektionaler Blazor-Bindungsfehler (dotnet/aspnetcore #24599).

Weitere Informationen zur Änderungserkennung, einschließlich Informationen zu den genauen Typen, die von Blazor überprüft werden, finden Sie unter Razor-Komponentenrendering unter ASP.NET Core.

Attributsplatting und arbiträre Parameter

Komponenten können zusätzlich zu den deklarierten Parametern der Komponente weitere Attribute erfassen und rendern. Zusätzliche Attribute können in einem Wörterbuch erfasst und dann für ein Element gesplattet werden, wenn die Komponente mithilfe des Razor-Anweisungsattributs @attributes gerendert wird. Diese Option ist nützlich, um eine Komponente zu definieren, die ein Markupelement erzeugt, das viele verschiedene Anpassungen unterstützt. Beispielsweise kann es mühsam sein, Attribute für ein <input>-Element separat zu definieren, das viele Parameter unterstützt.

In der folgenden Splat-Komponente:

  • Das erste <input>-Element (id="useIndividualParams") verwendet einzelne Komponentenparameter.
  • Das zweite <input>-Element (id="useAttributesDict") verwendet Attributsplatting.

Pages/Splat.razor:

@page "/splat"

<input id="useIndividualParams"
       maxlength="@maxlength"
       placeholder="@placeholder"
       required="@required"
       size="@size" />

<input id="useAttributesDict"
       @attributes="InputAttributes" />

@code {
    private string maxlength = "10";
    private string placeholder = "Input placeholder text";
    private string required = "required";
    private string size = "50";

    private Dictionary<string, object> InputAttributes { get; set; } =
        new()
        {
            { "maxlength", "10" },
            { "placeholder", "Input placeholder text" },
            { "required", "required" },
            { "size", "50" }
        };
}

Die gerenderten <input>-Elemente auf der Webseite sind identisch:

<input id="useIndividualParams"
       maxlength="10"
       placeholder="Input placeholder text"
       required="required"
       size="50">

<input id="useAttributesDict"
       maxlength="10"
       placeholder="Input placeholder text"
       required="required"
       size="50">

Definieren Sie einen Komponentenparameter, bei dem die CaptureUnmatchedValues-Eigenschaft auf true festgelegt ist, damit beliebige Attribute akzeptiert werden:

@code {
    [Parameter(CaptureUnmatchedValues = true)]
    public Dictionary<string, object>? InputAttributes { get; set; }
}

Wenn die CaptureUnmatchedValues-Eigenschaft auf [Parameter] festgelegt ist, kann der Parameter alle Attribute abgleichen, die keinem anderen Parameter entsprechen. Eine Komponente kann nur einen einzelnen Parameter mit CaptureUnmatchedValues definieren. Der mit CaptureUnmatchedValues verwendete Eigenschaftentyp muss von Dictionary<string, object> mit Zeichenfolgenschlüsseln zugewiesen werden können. Die Verwendung von IEnumerable<KeyValuePair<string, object>> oder IReadOnlyDictionary<string, object> ist in diesem Szenario ebenfalls möglich.

Die Position von @attributes relativ zur Position der Elementattribute ist wichtig. Wenn @attributes-Anweisungen für das Element gesplattet werden, werden die Attribute von rechts nach links (letztes bis erstes) verarbeitet. Sehen Sie sich das folgende Beispiel für eine übergeordnete Komponente an, die eine untergeordnete Komponente verwendet:

Shared/AttributeOrderChild1.razor:

<div @attributes="AdditionalAttributes" extra="5" />

@code {
    [Parameter(CaptureUnmatchedValues = true)]
    public IDictionary<string, object>? AdditionalAttributes { get; set; }
}

Pages/AttributeOrderParent1.razor:

@page "/attribute-order-parent-1"

<AttributeOrderChild1 extra="10" />

Das extra-Attribut der AttributeOrderChild1-Komponente ist rechts von @attributes festgelegt. Das gerenderte <div>-Element der AttributeOrderParent1-Komponente enthält extra="5", wenn dieses über das zusätzliche Attribut weitergeleitet wird, da die Attribute von rechts nach links (letztes bis erstes) verarbeitet werden:

<div extra="5" />

Im folgenden Beispiel wird die Reihenfolge von extra und @attributes im <div>-Element der untergeordneten Komponente umgekehrt:

Shared/AttributeOrderChild2.razor:

<div extra="5" @attributes="AdditionalAttributes" />

@code {
    [Parameter(CaptureUnmatchedValues = true)]
    public IDictionary<string, object>? AdditionalAttributes { get; set; }
}

Pages/AttributeOrderParent2.razor:

@page "/attribute-order-parent-2"

<AttributeOrderChild2 extra="10" />

Das <div>-Element auf der gerenderten Webseite der übergeordneten Komponente enthält extra="10", wenn es über das zusätzliche Attribut übergeben wird:

<div extra="10" />

Erfassen von Verweisen auf Komponenten

Komponentenverweise bieten eine Möglichkeit, auf eine Komponenteninstanz zum Ausgeben von Befehlen zu verweisen. So erfassen Sie einen Komponentenverweis:

  • Fügen Sie der untergeordneten Komponente ein @ref-Attribut hinzu.
  • Definieren Sie ein Feld mit demselben Typ wie die untergeordnete Komponente.

Wenn die Komponente gerendert wird, wird das Feld mit der Komponenteninstanz aufgefüllt. Anschließend können Sie .NET-Methoden für die Instanz aufrufen.

Betrachten Sie die folgende ReferenceChild-Komponente, die eine Meldung protokolliert, wenn die ChildMethod aufgerufen wird.

Shared/ReferenceChild.razor:

@using Microsoft.Extensions.Logging
@inject ILogger<ReferenceChild> logger

@code {
    public void ChildMethod(int value)
    {
        logger.LogInformation("Received {Value} in ChildMethod", value);
    }
}

Ein Komponentenverweis wird erst aufgefüllt, nachdem die Komponente gerendert wurde und die Ausgabe das Element von ReferenceChild enthält. Vor dem Rendern der Komponente gibt es nichts, auf das verwiesen werden kann.

Sie können Komponentenverweise bearbeiten, nachdem die Komponente das Rendering abgeschlossen hat, indem Sie die OnAfterRender- oder die OnAfterRenderAsync-Methode verwenden.

Um eine Verweisvariable mit einem Ereignishandler zu verwenden, verwenden Sie einen Lambdaausdruck oder weisen einen Ereignishandlerdelegaten in den Methoden OnAfterRender oder OnAfterRenderAsync zu. So ist sichergestellt, dass die Verweisvariable vor dem Ereignishandler zugewiesen wird.

Im folgenden Ansatz mit einem Lambdaausdruck wird die vorherige ReferenceChild-Komponente verwendet.

Pages/ReferenceParent1.razor:

@page "/reference-parent-1"

<button @onclick="@(() => childComponent?.ChildMethod(5))">
    Call <code>ReferenceChild.ChildMethod</code> with an argument of 5
</button>

<ReferenceChild @ref="childComponent" />

@code {
    private ReferenceChild? childComponent;
}

Im folgenden Ansatz mit einem Delegaten wird die vorherige ReferenceChild-Komponente verwendet.

Pages/ReferenceParent2.razor:

@page "/reference-parent-2"

<button @onclick="@(() => callChildMethod?.Invoke())">
    Call <code>ReferenceChild.ChildMethod</code> with an argument of 5
</button>

<ReferenceChild @ref="childComponent" />

@code {
    private ReferenceChild? childComponent;
    private Action? callChildMethod;

    protected override void OnAfterRender(bool firstRender)
    {
        if (firstRender)
        {
            callChildMethod = CallChildMethod;
        }
    }

    private void CallChildMethod()
    {
        childComponent?.ChildMethod(5);
    }
}

Beim Erfassen von Komponentenverweisen wird zwar eine ähnliche Syntax verwendet, um Elementverweise zu erfassen, das Erfassen von Komponentenverweisen ist jedoch kein JavaScript-Interop-Feature. Komponentenverweise werden nicht an JavaScript-Code übermittelt. Komponentenverweise werden nur in .NET-Code verwendet.

Wichtig

Verwenden Sie keine Komponentenverweise verwenden, um den Zustand von untergeordneten Komponenten zu verändern. Verwenden Sie stattdessen normale deklarative Komponentenparameter, um Daten an untergeordnete Komponenten zu übergeben. Die Verwendung von Komponentenparametern bewirkt, dass untergeordnete Komponenten automatisch zu den richtigen Zeitpunkten erneut gerendert werden. Weitere Informationen finden Sie im Abschnitt Komponentenparameter und im Artikel Datenbindung in Blazor in ASP.NET Core.

Synchronisierungskontext

Blazor verwendet einen Synchronisierungskontext (SynchronizationContext), um einen einzelnen logischen Ausführungsthread zu erzwingen. Die Lebenszyklusmethoden einer Komponente und Ereignisrückrufe, die von Blazor ausgelöst werden, werden in diesem Synchronisierungskontext ausgeführt.

Der Synchronisierungskontext des Blazor Server versucht, eine Singlethreadumgebung zu emulieren, sodass er genau mit dem WebAssembly-Modell im Browser übereinstimmt, das ein Singlethreadmodell ist. Zu jedem Zeitpunkt wird die Arbeit für genau einen Thread ausgeführt, woraus der Eindruck eines einzelnen logischen Threads entsteht. Zwei Vorgänge werden nicht gleichzeitig ausgeführt.

Vermeiden Sie eine Threadblockierung von Aufrufen.

Rufen Sie generell in Komponenten nicht die folgenden Methoden auf. Die folgenden Methoden blockieren die Threadausführung und hindern somit die App an der Wiederaufnahme der Arbeit, bis der zugrunde liegende Task beendet ist:

Hinweis

In den Beispielen in der Blazor-Dokumentation werden die in diesem Abschnitt erwähnten Threadblockierungsmethoden nur zu Demonstrationszwecken und nicht als Codierungsempfehlung verwendet. Beispielsweise simulieren einige Komponentencodedemonstrationen einen Prozess mit langer Ausführungsdauer, indem Thread.Sleep aufgerufen wird.

Externes Aufrufen von Komponentenmethoden zur Aktualisierung des Status

Wenn eine Komponente aufgrund eines externen Ereignisses (z. B. eines Timers oder anderer Benachrichtigungen) aktualisiert werden muss, verwenden Sie die InvokeAsync-Methode, mit der die Codeausführung an den Synchronisierungskontext von Blazor weitergeleitet wird. Betrachten Sie beispielsweise den folgenden Benachrichtigungsdienst, der jede lauschende Komponente über den aktualisierten Zustand benachrichtigen kann. Die Update-Methode kann überall in der App aufgerufen werden.

TimerService.cs:

public class TimerService : IDisposable
{
    private int elapsedCount;
    private readonly static TimeSpan heartbeatTickRate = TimeSpan.FromSeconds(5);
    private readonly ILogger<TimerService> logger;
    private readonly NotifierService notifier;
    private PeriodicTimer? timer;

    public TimerService(NotifierService notifier,
        ILogger<TimerService> logger)
    {
        this.notifier = notifier;
        this.logger = logger;
    }

    public async Task Start()
    {
        if (timer is null)
        {
            timer = new(heartbeatTickRate);
            logger.LogInformation("Started");

            using (timer)
            {
                while (await timer.WaitForNextTickAsync())
                {
                    elapsedCount += 1;
                    await notifier.Update("elapsedCount", elapsedCount);
                    logger.LogInformation($"elapsedCount: {elapsedCount}");
                }
            }
        }
    }

    public void Dispose()
    {
        timer?.Dispose();
    }
}

NotifierService.cs:

public class NotifierService
{
    public async Task Update(string key, int value)
    {
        if (Notify != null)
        {
            await Notify.Invoke(key, value);
        }
    }

    public event Func<string, int, Task>? Notify;
}

Registrieren des Diensts:

  • Registrieren Sie in einer Blazor WebAssembly-App die Dienste als Singleton in Program.cs:

    builder.Services.AddSingleton<NotifierService>();
    builder.Services.AddSingleton<TimerService>();
    
  • Registrieren Sie in einer Blazor Server-App die Dienste bereichsbezogen in Program.cs:

    builder.Services.AddScoped<NotifierService>();
    builder.Services.AddScoped<TimerService>();
    

Verwenden Sie NotifierService, um eine Komponente zu aktualisieren.

Pages/ReceiveNotifications.razor:

@page "/receive-notifications"
@implements IDisposable
@inject NotifierService Notifier
@inject TimerService Timer

<h1>Receive Notifications</h1>

<h2>Timer Service</h2>

<button @onclick="StartTimer">Start Timer</button>

<h2>Notifications</h2>

<p>
    Status:
    @if (lastNotification.key is not null)
    {
        <span>@lastNotification.key = @lastNotification.value</span>
    }
    else
    {
        <span>Awaiting first notification</span>
    }
</p>

@code {
    private (string key, int value) lastNotification;

    protected override void OnInitialized()
    {
        Notifier.Notify += OnNotify;
    }

    public async Task OnNotify(string key, int value)
    {
        await InvokeAsync(() =>
        {
            lastNotification = (key, value);
            StateHasChanged();
        });
    }

    private async Task StartTimer()
    {
        await Timer.Start();
    }

    public void Dispose()
    {
        Notifier.Notify -= OnNotify;
    }
}

Im vorherigen Beispiel:

  • NotifierService ruft die OnNotify-Methode der Komponente außerhalb des Synchronisierungskontexts von Blazor auf. InvokeAsync wird verwendet, um zum richtigen Kontext zu wechseln und ein Rendering in die Warteschlange zu stellen. Weitere Informationen finden Sie unter Rendering von Razor-Komponenten in ASP.NET Core.
  • Die Komponente implementiert IDisposable. Das Abonnement des OnNotify-Delegaten wird in der Dispose-Methode gekündigt. Diese wird vom Framework aufgerufen, wenn die Komponente verworfen wird. Weitere Informationen finden Sie unter Rendering von Razor-Komponenten in ASP.NET Core.

Verwenden von @key zur Steuerung der Beibehaltung von Elementen und Komponenten

Wenn Sie eine Element- oder Komponentenliste rendern und die Elemente oder Komponenten nachfolgend geändert werden, muss Blazor bestimmen, welche der vorherigen Elemente oder Komponenten beibehalten werden können und wie Modellobjekte diesen zugeordnet werden sollen. Normalerweise erfolgt dieser Prozess automatisch und kann ignoriert werden, aber in einigen Fällen sollten Sie den Prozess steuern.

Betrachten Sie die folgenden Komponenten Details und People:

  • Die Details-Komponente empfängt Daten (Data) von der übergeordneten People-Komponente, die in einem <input>-Element angezeigt wird. Jedes angezeigte <input>-Element kann den Fokus der Seite vom Benutzer erhalten, wenn er eines der <input>-Elemente auswählt.
  • Die People-Komponente erstellt mithilfe der Details-Komponente eine Liste anzuzeigender person-Objekte. Alle drei Sekunden wird der Sammlung eine neue Person hinzugefügt.

Diese Demonstration ermöglicht Ihnen Folgendes:

  • Auswählen eines <input> aus mehreren gerenderten Details-Komponenten
  • Untersuchen des Verhaltens des Fokus der Seite, wenn die Größe der people-Sammlung automatisch zunimmt

Shared/Details.razor:

<input value="@Data" />

@code {
    [Parameter]
    public string? Data { get; set; }
}

In der folgenden People-Komponente führt jede Iteration für das Hinzufügen einer Person in OnTimerCallback dazu, dass Blazor die gesamte Sammlung neu erstellt. Der Fokus der Seite bleibt auf derselben Indexposition der <input>-Elemente, sodass sich der Fokus jedes Mal verschiebt, wenn eine Person hinzugefügt wird. Das Abweichen des Fokus von der Auswahl durch den Benutzer ist kein wünschenswertes Verhalten. Nachdem mit der folgenden Komponente das unzureichende Verhalten veranschaulicht wurde, wird das @key-Anweisungsattribut verwendet, um die Benutzerfreundlichkeit zu verbessern.

Pages/People.razor:

@page "/people"
@using System.Timers
@implements IDisposable

@foreach (var person in people)
{
    <Details Data="@person.Data" />
}

@code {
    private Timer timer = new Timer(3000);

    public List<Person> people =
        new()
        {
            { new Person { Data = "Person 1" } },
            { new Person { Data = "Person 2" } },
            { new Person { Data = "Person 3" } }
        };

    protected override void OnInitialized()
    {
        timer.Elapsed += (sender, eventArgs) => OnTimerCallback();
        timer.Start();
    }

    private void OnTimerCallback()
    {
        _ = InvokeAsync(() =>
        {
            people.Insert(0,
                new Person
                {
                    Data = $"INSERTED {DateTime.Now.ToString("hh:mm:ss tt")}"
                });
            StateHasChanged();
        });
    }

    public void Dispose() => timer.Dispose();

    public class Person
    {
        public string? Data { get; set; }
    }
}

Der Inhalt der people-Sammlung ändert sich durch eingefügte, gelöschte oder neu sortierte Einträge. Erneutes Rendering kann zu sichtbaren Verhaltensunterschieden führen. Jedes Mal, wenn eine Person in die people-Sammlung eingefügt wird, erhält das Element den Fokus, das dem Element mit dem aktuellen Fokus vorangestellt ist. Der Fokus des Benutzers geht verloren.

Das Zuordnen von Elementen oder Komponenten zu einer Sammlung kann mit dem @key-Anweisungsattribut gesteuert werden. Die Verwendung von @key garantiert, dass Elemente oder Komponenten basierend auf dem Wert von @key beibehalten werden. Wenn in der Details-Komponente im vorherigen Beispiel @key auf person festgelegt ist, erfolgt in Blazor kein erneutes Rendering von Details-Komponenten, die sich nicht geändert haben.

Um für die People-Komponente das @key-Anweisungsattribut mit der people-Sammlung zu verwenden, aktualisieren Sie das <Details>-Element wie folgt:

<Details @key="person" Data="@person.Data" />

Wenn sich die people-Sammlung ändert, bleibt die Zuordnung zwischen Details-Instanzen und person-Instanzen erhalten. Wenn am Anfang der Sammlung eine Person eingefügt wird, wird an der entsprechenden Position eine neue Details-Instanz eingefügt. Andere Instanzen bleiben unverändert. Daher geht der Fokus des Benutzers nicht verloren, wenn der Sammlung Personen hinzugefügt werden.

Andere Sammlungsaktualisierungen weisen das gleiche Verhalten auf, wenn das @key-Anweisungsattribut verwendet wird:

  • Wenn eine Instanz aus der Sammlung gelöscht wird, wird nur die entsprechende Komponenteninstanz von der Benutzeroberfläche entfernt. Andere Instanzen bleiben unverändert.
  • Wenn Sammmlungseinträge neu sortiert werden, werden die entsprechenden Komponenteninstanzen beibehalten und auf der Benutzeroberfläche neu angeordnet.

Wichtig

Schlüssel sind für jedes Containerelement oder jede Komponente lokal. Schlüssel werden nicht dokumentübergreifend global verglichen.

Empfohlene Verwendung von @key

In der Regel ist es sinnvoll, @key zu verwenden, wenn eine Liste gerendert wird (z. B. in einem foreach-Block) und ein geeigneter Wert vorhanden ist, um @key zu definieren.

Sie können @key auch verwenden, um eine Element- oder Komponentenunterstruktur zu bewahren, wenn sich ein Objekt nicht ändert, wie in den folgenden Beispielen gezeigt.

Beispiel 1:

<li @key="person">
    <input value="@person.Data" />
</li>

Beispiel 2:

<div @key="person">
    @* other HTML elements *@
</div>

Wenn sich eine person-Instanz ändert, erzwingt das @key-Anweisungsattribut in Blazor Folgendes:

  • Verwerfen des gesamten <li>- oder <div>-Elements und seiner Nachfolger
  • Neuerstellen der Unterstruktur innerhalb der Benutzeroberfläche mit neuen Elementen und Komponenten

Dies ist hilfreich, um zu gewährleisten, dass kein Benutzeroberflächenzustand beibehalten wird, wenn sich die Sammlung innerhalb einer Unterstruktur ändert.

Bereich von @key

Die @key-Attributanweisung ist auf ihre eigenen gleichgeordneten Elemente innerhalb des übergeordneten Elements begrenzt.

Betrachten Sie das folgende Beispiel. Die Schlüssel first und second werden im selben Bereich des <div>-Elements miteinander verglichen:

<div>
    <div @key="first">...</div>
    <div @key="second">...</div>
</div>

Im folgenden Beispiel werden die Schlüssel first und second in ihren eigenen Bereichen veranschaulicht, die unabhängig voneinander stehen und keinen Einfluss aufeinander haben. Jeder @key-Bereich gilt nur für sein übergeordnetes <div>-Element, nicht für die übergeordneten <div>-Elemente:

<div>
    <div @key="first">...</div>
</div>
<div>
    <div @key="second">...</div>
</div>

Für die zuvor gezeigte Details-Komponente rendern die folgenden Beispiele person-Daten innerhalb desselben @key-Bereichs und veranschaulichen typische Anwendungsfälle für @key:

<div>
    @foreach (var person in people)
    {
        <Details @key="person" Data="@person.Data" />
    }
</div>
@foreach (var person in people)
{
    <div @key="person">
        <Details Data="@person.Data" />
    </div>
}
<ol>
    @foreach (var person in people)
    {
        <li @key="person">
            <Details Data="@person.Data" />
        </li>
    }
</ol>

Die folgenden Beispiele beschränken sich nur auf den Bereich @key für das <div>- oder <li>-Element, das jede Details-Komponenteninstanz umschließt. Daher werden person-Daten für jedes Member der people-Sammlung nicht für jede person-Instanz für die gerenderten Details-Komponenten schlüsselgebunden. Vermeiden Sie die folgenden Muster bei Verwendung von @key:

@foreach (var person in people)
{
    <div>
        <Details @key="person" Data="@person.Data" />
    </div>
}
<ol>
    @foreach (var person in people)
    {
        <li>
            <Details @key="person" Data="@person.Data" />
        </li>
    }
</ol>

Ungeeignete Fälle für @key

Beim Rendern mit @key wird die Leistung beeinträchtigt. Die Beeinträchtigung der Leistung ist nicht erheblich. Sie sollten @key jedoch nur angeben, wenn sich die Beibehaltung des Elements oder der Komponente positiv auf die App auswirken.

Auch wenn @key nicht verwendet wird, behält Blazor die untergeordneten Element- und Komponenteninstanzen so weit wie möglich bei. Der einzige Vorteil bei der Verwendung von @key besteht in der Kontrolle darüber, wie Modellinstanzen den beibehaltenen Komponenteninstanzen zugeordnet werden, anstatt die Zuordnung durch Blazor bestimmen zu lassen.

Zu verwendende Werte für @key

Im Allgemeinen ist es sinnvoll, für @key folgende Arten von Werten bereitzustellen:

  • Modellobjektinstanzen. Beispielsweise wurde im vorherigen Beispiel die Person-Instanz (person) verwendet. Dadurch wird die Beibehaltung basierend auf der Objektverweisgleichheit sichergestellt.
  • Eindeutige Bezeichner. Eindeutige Bezeichner können beispielsweise auf Primärschlüsselwerten vom Typ int, string oder Guid basieren.

Stellen Sie sicher, dass die für @key verwendeten Werte nicht kollidieren. Wenn innerhalb desselben übergeordneten Elements kollidierende Werte erkannt werden, löst Blazor eine Ausnahme aus, da alte Elemente oder Komponenten nicht deterministisch neuen Elementen oder Komponenten zugeordnet werden können. Verwenden Sie nur eindeutige Werte wie Objektinstanzen oder Primärschlüsselwerte.

Hinzufügen eines Attributs

Attribute können in Komponenten mit der @attribute-Anweisung hinzugefügt werden. Im folgenden Beispiel wird das [Authorize]-Attribut auf die Klasse der Komponente angewendet:

@page "/"
@attribute [Authorize]

Attribute für bedingte HTML-Elemente

HTML-Elementattributeigenschaften werden basierend auf dem .NET-Wert bedingt festgelegt. Wenn der Wert false oder null ist, wird die Eigenschaft nicht festgelegt. Wenn der Wert true ist, wird die Eigenschaft festgelegt.

Im folgenden Beispiel bestimmt IsCompleted, ob die checked-Eigenschaft des <input>-Elements festgelegt wird.

Pages/ConditionalAttribute.razor:

@page "/conditional-attribute"

<label>
    <input type="checkbox" checked="@IsCompleted" />
    Is Completed?
</label>

<button @onclick="@(() => IsCompleted = !IsCompleted)">
    Change IsCompleted
</button>

@code {
    [Parameter]
    public bool IsCompleted { get; set; }
}

Weitere Informationen finden Sie unter Razor-Syntaxreferenz für ASP.NET Core.

Warnung

Einige HTML-Attribute wie aria-pressed funktionieren nicht ordnungsgemäß, wenn der .NET-Typ bool ist. Verwenden Sie in diesen Fällen statt bool einen string-Typ.

Unformatierter HTML-Code

Zeichenfolgen werden normalerweise mithilfe von DOM-Textknoten gerendert. Das bedeutet, dass das darin enthaltene Markup vollständig ignoriert und als Literaltext behandelt wird. Sie können unformatierten HTML-Code rendern, indem Sie den HTML-Inhalt mit einem MarkupString-Wert umschließen. Der Wert wird als HTML oder SVG analysiert und in das DOM eingefügt.

Warnung

Das Rendern von unformatiertem HTML-Code, der aus einer nicht vertrauenswürdigen Quelle stammt, gilt als Sicherheitsrisiko und sollte immer vermieden werden.

Im folgenden Beispiel wird veranschaulicht, wie der MarkupString-Typ verwendet wird, um der gerenderten Ausgabe einer Komponente einen Block mit statischem HTML-Inhalt hinzuzufügen.

Pages/MarkupStringExample.razor:

@page "/markup-string-example"

@((MarkupString)myMarkup)

@code {
    private string myMarkup =
        "<p class=\"text-danger\">This is a dangerous <em>markup string</em>.</p>";
}

Razor-Vorlagen

Renderfragmente können mithilfe der Razor-Vorlagensyntax definiert werden, um einen Benutzeroberflächen-Codeausschnitt zu definieren. Für Razor-Vorlagen wird das folgende Format verwendet:

@<{HTML tag}>...</{HTML tag}>

Im folgenden Beispiel wird veranschaulicht, wie Sie RenderFragment- und RenderFragment<TValue>-Werte angeben und Vorlagen direkt in einer-Komponente rendern können. Renderingfragmente können auch als Argumente an Komponentenvorlagen übergeben werden.

Pages/RazorTemplate.razor:

@page "/razor-template"

@timeTemplate

@petTemplate(new Pet { Name = "Nutty Rex" })

@code {
    private RenderFragment timeTemplate = @<p>The time is @DateTime.Now.</p>;
    private RenderFragment<Pet> petTemplate = (pet) => @<p>Pet: @pet.Name</p>;

    private class Pet
    {
        public string? Name { get; set; }
    }
}

Gerenderte Ausgabe des vorangehenden Codes:

<p>The time is 4/19/2021 8:54:46 AM.</p>
<p>Pet: Nutty Rex</p>

Statische Ressourcen

In Blazor gilt für statische Ressourcen die Konvention von ASP.NET Core-Apps. Statische Ressourcen befinden sich in einem oder mehreren web root-Ordnern (wwwroot) des Projekts unter dem Ordner wwwroot.

Verwenden Sie einen zur Basis relativen Pfad (/), um auf den Webstamm einer statischen Ressource zu verweisen. Im folgenden Beispiel befindet sich logo.png physisch im Ordner {PROJECT ROOT}/wwwroot/images. {PROJECT ROOT} ist der Projektstamm der App.

<img alt="Company logo" src="/images/logo.png" />

Komponenten unterstützen keine Notation mit Tilde und Schrägstrich (~/).

Informationen zum Festlegen des Basispfads einer App finden Sie unter Hosten und Bereitstellen von Blazor in ASP.NET Core .

Keine Unterstützung von Taghilfsprogrammen in Komponenten

Tag Helpers werden in Komponenten nicht unterstützt. Sie können Taghilfsobjekte in Blazor bereitstellen, indem Sie eine Komponente mit der gleichen Funktionalität wie das Taghilfsprogramm erstellen und diese stattdessen verwenden.

SVG-Bilder

Da Blazor HTML rendert, werden browsergestützte Bilder wie skalierbare Vektorgrafiken (Scalable Vector Graphics, SVG-Grafiken.svg) über das <img>-Tag unterstützt:

<img alt="Example image" src="image.svg" />

Ebenso werden SVG-Bilder in den CSS-Regeln einer Stylesheetdatei (.css) unterstützt:

.element-class {
    background-image: url("image.svg");
}

Blazor unterstützt das <foreignObject>-Element, um beliebigen HTML-Code in einer SVG anzuzeigen. Das Markup kann beliebige HTML-, RenderFragment- oder Razor-Komponenten darstellen.

Dies wird im folgenden Beispiel veranschaulicht:

  • Darstellung eines string (@message)
  • Bidirektionale Bindung mit einem <input>-Element und einem value-Feld
  • Eine Robot-Komponente
<svg width="200" height="200" xmlns="http://www.w3.org/2000/svg">
    <rect x="0" y="0" rx="10" ry="10" width="200" height="200" stroke="black" 
        fill="none" />
    <foreignObject x="20" y="20" width="160" height="160">
        <p>@message</p>
    </foreignObject>
</svg>

<svg xmlns="http://www.w3.org/2000/svg">
    <foreignObject width="200" height="200">
        <label>
            Two-way binding:
            <input @bind="value" @bind:event="oninput" />
        </label>
    </foreignObject>
</svg>

<svg xmlns="http://www.w3.org/2000/svg">
    <foreignObject>
        <Robot />
    </foreignObject>
</svg>

@code {
    private string message = "Lorem ipsum dolor sit amet, consectetur adipiscing " +
        "elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.";

    private string? value;
}

Verhalten beim Rendern von Leerzeichen

Wenn die @preservewhitespace-Direktive mit dem Wert trueverwendet wird, werden standardmäßig zusätzliche Leerzeichen entfernt, wenn Folgendes gilt:

  • Sie stehen in einem Element am Anfang oder am Ende.
  • Se stehen in einem RenderFragment/RenderFragment<TValue>-Parameter (z. B. untergeordneter Inhalt, der an eine andere Komponente übergeben wird) am Anfang oder am Ende.
  • Sie stehen am Anfang oder Ende eines C#-Codeblocks wie @if oder @foreach.

Das Entfernen der Leerzeichen kann sich auf die gerenderte Ausgabe auswirken, wenn eine CSS-Regel wie white-space: pre verwendet wird. Führen Sie eine der folgenden Aktionen durch, um diese Leistungsoptimierung zu deaktivieren und die Leerzeichen beizubehalten:

  • Fügen Sie oben in der Razor-Datei (.razor) die Anweisung @preservewhitespace true hinzu, um sie auf eine bestimmte Komponente anzuwenden.
  • Fügen Sie die Anweisung @preservewhitespace true in einer _Imports.razor-Datei hinzu, um sie auf ein Unterverzeichnis oder auf das gesamte Projekt anzuwenden.

In den meisten Fällen ist keine Aktion erforderlich, weil sich Apps normalerweise wie gewohnt (aber schneller) verhalten. Falls das Entfernen der Leerzeichen für eine bestimmte Komponente zu Renderingproblemen führt, sollten Sie darin @preservewhitespace true nutzen, um diese Optimierung zu deaktivieren.

Unterstützung generischer Typparameter

Die @typeparam-Direktive deklariert einen generischen Typparameter für die generierte Komponentenklasse:

@typeparam TItem

C#-Syntax mit where-Typeinschränkungen wird unterstützt:

@typeparam TEntity where TEntity : IEntity

Im folgenden Beispiel wird die Komponente ListGenericTypeItems1 generisch als TExample typisiert.

Shared/ListGenericTypeItems1.razor:

@typeparam TExample

@if (ExampleList is not null)
{
    <ul>
        @foreach (var item in ExampleList)
        {
            <li>@item</li>
        }
    </ul>
}

@code {
    [Parameter]
    public IEnumerable<TExample>? ExampleList{ get; set; }
}

Die folgende GenericTypeExample1-Komponente rendert zwei ListGenericTypeItems1-Komponenten:

  • Zeichenfolgen- oder Integerdaten werden dem Parameter ExampleList jeder Komponente zugewiesen.
  • Für den Typparameter (TExample) jeder Komponente wird je nach Typ der zugewiesenen Daten der Typ string oder int festgelegt.

Pages/GenericTypeExample1.razor:

@page "/generic-type-example-1"

<h1>Generic Type Example 1</h1>

<ListGenericTypeItems1 ExampleList="@(new List<string> { "Item 1", "Item 2" })" 
                       TExample="string" />

<ListGenericTypeItems1 ExampleList="@(new List<int> { 1, 2, 3 })" 
                       TExample="int" />

Weitere Informationen finden Sie unter Razor-Syntaxreferenz für ASP.NET Core. Ein Beispiel für die generische Typisierung mit Komponentenvorlagen finden Sie unter ASP.NET Core Blazor-Komponentenvorlagen.

Unterstützung für kaskadierte generische Typen

Eine Vorgängerkomponente kann mithilfe des [CascadingTypeParameter]-Attributs einen Typparameter nach Namen an Nachfolger kaskadieren. Mit diesem Attribut kann ein generischer Typrückschluss den angegebenen Typparameter automatisch bei Nachfolgern verwenden, die einen Typparameter mit demselben Namen besitzen.

Wenn @attribute [CascadingTypeParameter(...)] Sie einer Komponente hinzufügen, wird das angegebene generische Typargument automatisch von Nachfolgern verwendet, für die Folgendes gilt:

  • Sie werden als untergeordneter Inhalt für die Komponente im selben .razor Dokument geschachtelt.
  • Sie deklarieren auch einen @typeparam mit genau demselben Namen.
  • Für den Typparameter sollte kein anderer Wert explizit angegeben oder implizit abgeleitet werden. Wenn ein anderer Wert bereitgestellt oder abgeleitet wird, besitzt er Vorrang vor dem kaskadierten generischen Typ.

Beim Empfang eines kaskadierten Typparameters erhalten Komponenten den Parameterwert vom nächstgelegenen Vorgänger, der über einen CascadingTypeParameterAttribute mit einem übereinstimmenden Namen verfügt. Kaskadierte generische Typparameter werden innerhalb einer bestimmten Teilstruktur überschrieben.

Der Abgleich wird nur anhand des Namens ausgeführt. Daher empfiehlt es sich, einen kaskadierten generischen Typparameter mit einem generischen Namen zu vermeiden, z. B. T oder TItem. Wenn sich ein Entwickler für die Kaskadierung eines Typparameters entscheidet, verspricht er implizit, dass der Name eindeutig genug ist, um nicht mit anderen kaskadierten Typparametern von nicht verwandten Komponenten in Konflikt zu geraten.

Generische Typen können mit einem der folgenden Ansätze mit übergeordneten Komponenten an untergeordnete Komponenten kaskadiert werden. Dies wird in den folgenden beiden Unterabschnitten veranschaulicht:

  • Legen Sie den kaskadierten generischen Typ explizit fest.
  • Leiten Sie den kaskadierten generischen Typ ab.

Die folgenden Unterabschnitte enthalten Beispiele für die vorherigen Ansätze, die die folgenden beiden ListDisplay-Komponenten verwenden. Die Komponenten empfangen und rendern Listendaten und sind generisch als TExample typisiert. Diese Komponenten dienen zu Demonstrationszwecken und unterscheiden sich nur in der Textfarbe, in der die Liste gerendert wird. Wenn Sie mit den Komponenten aus den folgenden Unterabschnitten in einer lokalen Test-App experimentieren möchten, fügen Sie diese zunächst der App hinzu.

Shared/ListDisplay1.razor:

@typeparam TExample

@if (ExampleList is not null)
{
    <ul style="color:blue">
        @foreach (var item in ExampleList)
        {
            <li>@item</li>
        }
    </ul>
}

@code {
    [Parameter]
    public IEnumerable<TExample>? ExampleList { get; set; }
}

Shared/ListDisplay2.razor:

@typeparam TExample

@if (ExampleList is not null)
{
    <ul style="color:red">
        @foreach (var item in ExampleList)
        {
            <li>@item</li>
        }
    </ul>
}

@code {
    [Parameter]
    public IEnumerable<TExample>? ExampleList { get; set; }
}

Explizite generische Typen auf Grundlage von Vorgängerkomponenten

In der Demonstration in diesem Abschnitt wird ein Typ explizit für TExample kaskadiert.

Hinweis

In diesem Abschnitt werden die beiden ListDisplay-Komponenten aus dem Abschnitt Unterstützung für kaskadierte generische Typen verwendet.

Die folgende ListGenericTypeItems2-Komponente empfängt Daten und kaskadiert einen generischen Typparameter namens TExample an ihre Nachfolgerkomponenten. In der nächsten übergeordneten Komponente wird die ListGenericTypeItems2-Komponente verwendet, um Listendaten mit der vorherigen ListDisplay-Komponente anzuzeigen.

Shared/ListGenericTypeItems2.razor:

@attribute [CascadingTypeParameter(nameof(TExample))]
@typeparam TExample

<h2>List Generic Type Items 2</h2>

@ChildContent

@code {
    [Parameter]
    public RenderFragment? ChildContent { get; set; }
}

Die folgende übergeordnete Komponente GenericTypeExample2 legt den untergeordneten Inhalt (RenderFragment) von zwei ListGenericTypeItems2-Komponenten fest, die die ListGenericTypeItems2-Typen (TExample) angeben, die an untergeordnete Komponenten kaskadiert werden. ListDisplay-Komponenten werden mit den im Beispiel gezeigten Listenelementdaten gerendert. Zeichenfolgendaten werden mit der ersten ListGenericTypeItems2-Komponente verwendet, und Integerdaten werden mit der zweiten ListGenericTypeItems2-Komponente verwendet.

Pages/GenericTypeExample2.razor:

@page "/generic-type-example-2"

<h1>Generic Type Example 2</h1>

<ListGenericTypeItems2 TExample="string">
    <ListDisplay1 ExampleList="@(new List<string> { "Item 1", "Item 2" })" />
    <ListDisplay2 ExampleList="@(new List<string> { "Item 3", "Item 4" })" />
</ListGenericTypeItems2>

<ListGenericTypeItems2 TExample="int">
    <ListDisplay1 ExampleList="@(new List<int> { 1, 2, 3 })" />
    <ListDisplay2 ExampleList="@(new List<int> { 4, 5, 6 })" />
</ListGenericTypeItems2>

Die explizite Angabe des Typs ermöglicht auch die Verwendung von kaskadierenden Werten und Parametern, um Daten für untergeordnete Komponenten bereitzustellen. Dies wird im Folgenden veranschaulicht.

Shared/ListDisplay3.razor:

@typeparam TExample

@if (ExampleList is not null)
{
    <ul style="color:blue">
        @foreach (var item in ExampleList)
        {
            <li>@item</li>
        }
    </ul>
}

@code {
    [CascadingParameter]
    protected IEnumerable<TExample>? ExampleList { get; set; }
}

Shared/ListDisplay4.razor:

@typeparam TExample

@if (ExampleList is not null)
{
    <ul style="color:red">
        @foreach (var item in ExampleList)
        {
            <li>@item</li>
        }
    </ul>
}

@code {
    [CascadingParameter]
    protected IEnumerable<TExample>? ExampleList { get; set; }
}

Shared/ListGenericTypeItems3.razor:

@attribute [CascadingTypeParameter(nameof(TExample))]
@typeparam TExample

<h2>List Generic Type Items 3</h2>

@ChildContent

@if (ExampleList is not null)
{
    <ul style="color:green">
        @foreach(var item in ExampleList)
        {
            <li>@item</li>
        }
    </ul>

    <p>
        Type of <code>TExample</code>: @typeof(TExample)
    </p>
}

@code {
    [CascadingParameter]
    protected IEnumerable<TExample>? ExampleList { get; set; }

    [Parameter]
    public RenderFragment? ChildContent { get; set; }
}

Beim Kaskadieren der Daten im folgenden Beispiel muss der Typ für die ListGenericTypeItems3-Komponente bereitgestellt werden.

Pages/GenericTypeExample3.razor:

@page "/generic-type-example-3"

<h1>Generic Type Example 3</h1>

<CascadingValue Value="@stringData">
    <ListGenericTypeItems3 TExample="string">
        <ListDisplay3 />
        <ListDisplay4 />
    </ListGenericTypeItems3>
</CascadingValue>

<CascadingValue Value="@integerData">
    <ListGenericTypeItems3 TExample="int">
        <ListDisplay3 />
        <ListDisplay4 />
    </ListGenericTypeItems3>
</CascadingValue>

@code {
    private List<string> stringData = new() { "Item 1", "Item 2" };
    private List<int> integerData = new() { 1, 2, 3 };
}

Wenn mehrere generische Typen kaskadiert werden, müssen Werte für alle generischen Typen in der Menge übergeben werden. Im folgenden Beispiel sind TItem, TValue und TEdit generische GridColumn-Typen, aber die übergeordnete Komponente, die GridColumn einfügt, gibt den TItem-Typ nicht an:

<GridColumn TValue="string" TEdit="@TextEdit" />

Im vorherigen Beispiel wird ein Kompilierzeitfehler ausgelöst, der besagt, dass der GridColumn-Komponente der Typparameter TItem fehlt. Gültiger Code gibt alle Typen an:

<GridColumn TValue="string" TEdit="@TextEdit" TItem="@User" />

Ableiten generischer Typen auf Grundlage von Vorgängerkomponenten

In der Demonstration in diesem Abschnitt wird ein Typ kaskadiert, der für TExample abgeleitet wurde.

Hinweis

In diesem Abschnitt werden die beiden ListDisplay-Komponenten aus dem Abschnitt Unterstützung für kaskadierte generische Typen verwendet.

Shared/ListGenericTypeItems4.razor:

@attribute [CascadingTypeParameter(nameof(TExample))]
@typeparam TExample

<h2>List Generic Type Items 4</h2>

@ChildContent

@if (ExampleList is not null)
{
    <ul style="color:green">
        @foreach(var item in ExampleList)
        {
            <li>@item</li>
        }
    </ul>

    <p>
        Type of <code>TExample</code>: @typeof(TExample)
    </p>
}

@code {
    [Parameter]
    public IEnumerable<TExample>? ExampleList { get; set; }

    [Parameter]
    public RenderFragment? ChildContent { get; set; }
}

Die folgende GenericTypeExample4-Komponente mit abgeleiteten kaskadierten Typen enthält verschiedene Daten, die angezeigt werden können.

Pages/GenericTypeExample4.razor:

@page "/generic-type-example-4"

<h1>Generic Type Example 4</h1>

<ListGenericTypeItems4 ExampleList="@(new List<string> { "Item 5", "Item 6" })">
    <ListDisplay1 ExampleList="@(new List<string> { "Item 1", "Item 2" })" />
    <ListDisplay2 ExampleList="@(new List<string> { "Item 3", "Item 4" })" />
</ListGenericTypeItems4>

<ListGenericTypeItems4 ExampleList="@(new List<int> { 7, 8, 9 })">
    <ListDisplay1 ExampleList="@(new List<int> { 1, 2, 3 })" />
    <ListDisplay2 ExampleList="@(new List<int> { 4, 5, 6 })" />
</ListGenericTypeItems4>

Die folgende GenericTypeExample5-Komponente mit abgeleiteten kaskadierten Typen enthält dieselben Daten, die angezeigt werden können. Im folgenden Beispiel werden die Daten direkt den Komponenten zugewiesen.

Pages/GenericTypeExample5.razor:

@page "/generic-type-example-5"

<h1>Generic Type Example 5</h1>

<ListGenericTypeItems4 ExampleList="@stringData">
    <ListDisplay1 ExampleList="@stringData" />
    <ListDisplay2 ExampleList="@stringData" />
</ListGenericTypeItems4>

<ListGenericTypeItems4 ExampleList="@integerData">
    <ListDisplay1 ExampleList="@integerData" />
    <ListDisplay2 ExampleList="@integerData" />
</ListGenericTypeItems4>

@code {
    private List<string> stringData = new() { "Item 1", "Item 2" };
    private List<int> integerData = new() { 1, 2, 3 };
}

Rendern von Razor-Komponenten aus JavaScript

Razor Komponenten können für vorhandene JS-Apps dynamisch aus JavaScript (JS) gerendert werden.

Um eine Razor-Komponente aus JS zu rendern, registrieren Sie die Komponente als Stammkomponente für das JS-Rendering, und weisen Sie der Komponente einen Bezeichner zu:

  • Ändern Sie in einer Blazor Server-App den Aufruf von AddServerSideBlazor in Program.cs:

    builder.Services.AddServerSideBlazor(options =>
    {
        options.RootComponents.RegisterForJavaScript<Counter>(identifier: "counter");
    });
    

    Hinweis

    Das vorangehende Codebeispiel erfordert einen Namespace für die Komponenten der App (z. B. using BlazorSample.Pages;) in der Program.cs-Datei.

  • Ändern Sie in einer Blazor WebAssembly-App den Aufruf von RegisterForJavaScript für RootComponents in Program.cs:

    builder.RootComponents.RegisterForJavaScript<Counter>(identifier: "counter");
    

    Hinweis

    Das vorangehende Codebeispiel erfordert einen Namespace für die Komponenten der App (z. B. using BlazorSample.Pages;) in der Program.cs-Datei.

Laden Sie Blazor in die JS App (blazor.server.js oder blazor.webassembly.js). Rendern Sie die Komponente aus JS mithilfe des registrierten Bezeichners in ein Containerelement, und übergeben Sie Komponentenparameter nach Bedarf:

let containerElement = document.getElementById('my-counter');
await Blazor.rootComponents.add(containerElement, 'counter', { incrementAmount: 10 });

Benutzerdefinierte Blazor-Elemente

Die experimentelle Unterstützung ist für das Erstellen benutzerdefinierter Elemente mithilfe des NuGet-Pakets Microsoft.AspNetCore.Components.CustomElements verfügbar. Benutzerdefinierte Elemente verwenden HTML-Standardschnittstellen, um benutzerdefinierte HTML-Elemente zu implementieren.

Warnung

Experimentelle Features werden für die Untersuchung der Funktionsfähigkeit von Features bereitgestellt und dürfen nicht in einer stabilen Version enthalten sein.

Registrieren Sie eine Stammkomponente als benutzerdefiniertes Element:

  • Ändern Sie in einer Blazor Server-App den Aufruf von AddServerSideBlazor in Program.cs:

    builder.Services.AddServerSideBlazor(options =>
    {
        options.RootComponents.RegisterAsCustomElement<Counter>("my-counter");
    });
    

    Hinweis

    Das vorangehende Codebeispiel erfordert einen Namespace für die Komponenten der App (z. B. using BlazorSample.Pages;) in der Program.cs-Datei.

  • Ändern Sie in einer Blazor WebAssembly-App den Aufruf von RegisterAsCustomElement für RootComponents in Program.cs:

    builder.RootComponents.RegisterAsCustomElement<Counter>("my-counter");
    

    Hinweis

    Das vorangehende Codebeispiel erfordert einen Namespace für die Komponenten der App (z. B. using BlazorSample.Pages;) in der Program.cs-Datei.

Fügen Sie das folgende <script>-Tag in den HTML-Code der App vor dem Skripttag Blazor ein:

<script src="/_content/Microsoft.AspNetCore.Components.CustomElements/BlazorCustomElements.js"></script>

Verwenden Sie das benutzerdefinierte Element mit einem beliebigen Webframework. Das vorangehende benutzerdefinierte counter-Element wird z. B. in einer React-App mit folgendem Markup verwendet:

<my-counter increment-amount={incrementAmount}></my-counter>

Ein vollständiges Beispiel zum Erstellen benutzerdefinierter Elemente mit Blazor finden Sie im Beispielprojekt für benutzerdefinierte Blazor-Elemente.

Warnung

Das Feature für benutzerdefinierte Elemente ist derzeit experimentell, wird nicht unterstützt und kann jederzeit geändert oder entfernt werden. Wir freuen uns über Ihr Feedback, wie gut dieser Ansatz Ihre Anforderungen erfüllt.

Generieren von Angular- und React-Komponenten

Frameworkspezifische JavaScript-JS-Komponenten werden aus Razor-Komponenten für Webframeworks, z. B. Angular oder React, generiert. Diese Funktion ist nicht in .NET 6 enthalten, wird aber durch die neue Unterstützung für das Rendern von Razor-Komponenten aus JS aktiviert. Das Beispiel für die JS-Komponentengenerierung auf GitHub veranschaulicht, wie Sie Angular- und React-Komponenten aus Razor-Komponenten generieren. Weitere Informationen finden Sie in der README.md-Datei der Beispiel-App auf GitHub.

Warnung

Die Angular- und React-Komponentenfeatures sind derzeit experimentell, werden nicht unterstützt und können jederzeit geändert oder entfernt werden. Wir freuen uns über Ihr Feedback, wie gut dieser Ansatz Ihre Anforderungen erfüllt.

Blazor-Apps werden mit Razor-Komponenten erstellt, die informell als Blazor-Komponenten bezeichnet werden. Eine Komponente ist ein eigenständiger Teil der Benutzeroberfläche mit Verarbeitungslogik, die dynamisches Verhalten ermöglicht. Komponenten können geschachtelt, wiederverwendet, von Projekten gemeinsam genutzt und in MVC- sowie Razor Pages-Apps verwendet werden.

Komponentenklassen

Komponenten werden mithilfe einer Kombination aus C# und HTML-Markup in Razor-Komponentendateien mit der Dateierweiterung .razor implementiert.

Razor-Syntax

Für Komponenten wird die Razor-Syntax verwendet. Von Komponenten werden zwei Razor-Features häufig verwendet: Anweisungen und Anweisungsattribute. Dies sind reservierte Schlüsselwörter, die im Razor-Markup mit dem Präfix @ angezeigt werden:

  • Anweisungen: Diese ändern, wie Komponentenmarkup analysiert oder ausgeführt wird. Beispielsweise gibt die @page-Anweisung eine routingfähige Komponente mit einer Routenvorlage an und kann direkt über die Anforderung eines Benutzers im Browser unter einer bestimmten URL erreicht werden.
  • Anweisungsattribute: Diese ändern, wie ein Komponentenelement analysiert oder ausgeführt wird. Beispielsweise bindet das @bind-Anweisungsattribut für ein <input>-Element Daten an den Wert des Elements.

In Komponenten verwendete Anweisungen und Anweisungsattribute werden in diesem Artikel und in anderen Artikeln der Blazor-Dokumentation näher erläutert. Allgemeine Informationen zur Razor-Syntax finden Sie in der Referenz zur Razor-Syntax für ASP.NET Core.

Namen

Der Name einer Komponente muss mit einem Großbuchstaben beginnen:

  • ProductDetail.razor ist gültig.
  • productDetail.razor ist ungültig.

Allgemeine Blazor-Namenskonventionen, die in der gesamten Blazor-Dokumentation verwendet werden:

  • Für Komponentendateipfade wird die Pascal-Schreibweise† verwendet. Sie werden vor den Komponentencodebeispielen angezeigt. Pfade geben typische Ordnerspeicherorte an. Beispielsweise gibt Pages/ProductDetail.razor an, dass der Dateiname der ProductDetail-Komponente ProductDetail.razor lautet und die Komponente im Ordner Pages der App enthalten ist.
  • In den URLs für Komponentendateipfade routingfähiger Komponenten werden die Wörter in der Routenvorlage einer Komponente durch Bindestriche anstelle von Leerzeichen getrennt. Beispielsweise wird die Komponente ProductDetail mit der Routenvorlage /product-detail (@page "/product-detail") in einem Browser an der relativen URL /product-detail angefordert.

† Bei der Pascal-Schreibweise (Camel-Case-Schreibweise mit großem Anfangsbuchstaben) werden keine Leerzeichen und Interpunktionszeichen verwendet, und der erste Buchstabe jedes Worts, einschließlich des ersten Worts, ist ein Großbuchstabe.

Routing

Das Routing in Blazor erfolgt, indem mit der @page-Anweisung eine Routenvorlage für jede zugängliche Komponente in der App bereitgestellt wird. Wenn eine Razor-Datei mit einer @page-Anweisung kompiliert wird, wird der generierten Klasse RouteAttribute hinzugefügt und so die Routenvorlage angegeben. Zur Laufzeit sucht der Router nach Komponentenklassen mit einem RouteAttribute und rendert die Komponente, deren Routenvorlage mit der angeforderten URL übereinstimmt.

Für die folgende HelloWorld-Komponente wird die Routenvorlage /hello-world verwendet. Die gerenderte Webseite für die Komponente wird über die relative URL /hello-world erreicht. Wenn Sie eine Blazor-App lokal mit dem Standardprotokoll, -host und -port ausführen, wird die HelloWorld-Komponente im Browser unter https://localhost:5001/hello-world angefordert. Komponenten, die Webseiten erzeugen, befinden sich in der Regel im Ordner Pages. Sie können Komponenten jedoch in beliebigen Ordnern speichern, einschließlich geschachtelter Ordner.

Pages/HelloWorld.razor:

@page "/hello-world"

<h1>Hello World!</h1>

Die vorangehende Komponente wird im Browser unter /hello-world geladen, unabhängig davon, ob Sie die Komponente der Benutzeroberflächennavigation der App hinzufügen. Optional können Komponenten zur NavMenu-Komponente hinzugefügt werden, sodass in der Benutzeroberflächennavigation der App ein Link zur Komponente angezeigt wird.

Bei der vorherigen HelloWorld-Komponente können Sie eine NavLink-Komponente zur NavMenu-Komponente im Shared-Ordner hinzufügen. Weitere Informationen, einschließlich Beschreibungen der Komponenten NavLink und NavMenu, finden Sie unter Routing und Navigation in Blazor in ASP.NET Core.

Markup

Die Benutzeroberfläche einer Komponente wird mithilfe der Razor-Syntax definiert, die aus Razor-Markup, C# und HTML besteht. Wenn eine App kompiliert wird, werden das HTML-Markup und die C#-Renderinglogik in eine Komponentenklasse konvertiert. Der Name der generierten Klasse entspricht dem Namen der Datei.

Member der Komponentenklasse werden in einem oder mehreren @code-Blöcken definiert. In @code-Blöcken wird der Komponentenzustand angegeben und mit C# verarbeitet:

  • Eigenschaften- und Feldinitialisierer
  • Parameterwerte aus Argumenten, die von übergeordneten Komponenten und Routenparametern übergeben werden
  • Methoden für die Behandlung von Benutzerereignissen, Lebenszyklusereignissen und benutzerdefinierter Komponentenlogik

Komponentenmember werden in der Renderinglogik mithilfe von C#-Ausdrücken verwendet, die mit dem Symbol @ beginnen. Beispielsweise wird ein C#-Feld gerendert, indem @ dem Feldnamen vorangestellt wird. Durch die unten gezeigte Markup-Komponente wird Folgendes ausgewertet und gerendert:

  • headingFontStyle für den CSS-Eigenschaftswert font-style des Überschriftenelements.
  • headingText für den Inhalt des Überschriftenelements.

Pages/Markup.razor:

@page "/markup"

<h1 style="font-style:@headingFontStyle">@headingText</h1>

@code {
    private string headingFontStyle = "italic";
    private string headingText = "Put on your new Blazor!";
}

Hinweis

In den Beispielen in der gesamten Blazor-Dokumentation wird für private Member der private-Zugriffsmodifizierer angegeben. Der Bereich privater Member ist auf die Klasse einer Komponente beschränkt. In C# wird jedoch der private-Zugriffsmodifizierer angenommen, wenn kein Zugriffsmodifizierer vorhanden ist. Daher ist das explizite Markieren von Membern als private im eigenen Code optional. Weitere Informationen zu Zugriffsmodifizierern finden Sie unter Zugriffsmodifizierer (C#-Programmierhandbuch).

Das Blazor-Framework verarbeitet eine Komponente intern als Renderstruktur. Dies ist die Kombination aus dem Dokumentobjektmodell (DOM) einer Komponente und dem Cascading Style Sheet Object Model (CSSOM). Nachdem die Komponente zum ersten Mal gerendert wurde, wird ihre Renderstruktur als Reaktion auf Ereignisse erneut generiert. Blazor vergleicht die neue Renderstruktur mit der vorherigen und wendet alle Änderungen auf das DOM des Browsers für die Anzeige an. Weitere Informationen finden Sie unter Rendering von Razor-Komponenten in ASP.NET Core.

Komponenten sind normale C#-Klassen und können an beliebiger Stelle innerhalb eines Projekts eingefügt werden. Komponenten, die Webseiten erzeugen, befinden sich in der Regel im Ordner Pages. Nicht für Seiten verwendete Komponenten werden häufig im Ordner Shared oder einem benutzerdefinierten Ordner gespeichert, der dem Projekt hinzugefügt wurde.

Laut Razor-Syntax werden für C#-Steuerelementstrukturen, Anweisungen und Anweisungsattribute Kleinbuchstaben verwendet (z. B. @if, @code, @bind). Eigenschaftennamen stehen in Großbuchstaben (z. B. @Body für LayoutComponentBase.Body).

Asynchrone Methoden (async) unterstützen nicht die Rückgabe von void.

Das Blazor-Framework verfolgt keine asynchronen Methoden (async) nach, die void zurückgeben. Folglich werden keine Ausnahmen abgefangen, wenn void zurückgegeben wird. Lassen Sie asynchrone Methoden stets Task zurückgeben.

Geschachtelte Komponenten

Komponenten können andere Komponenten enthalten, sofern Sie sie mithilfe der HTML-Syntax deklarieren. Das Markup für die Verwendung einer Komponente sieht wie ein HTML-Tag aus, wobei der Name des Tags der Typ der Komponente ist.

Betrachten Sie die folgende Heading-Komponente, die von anderen Komponenten verwendet werden kann, um eine Überschrift anzuzeigen.

Shared/Heading.razor:

<h1 style="font-style:@headingFontStyle">Heading Example</h1>

@code {
    private string headingFontStyle = "italic";
}

Das folgende Markup in der HeadingExample-Komponente rendert die vorherige Heading-Komponente an der Position, an der das <Heading />-Tag angezeigt wird.

Pages/HeadingExample.razor:

@page "/heading-example"

<Heading />

Wenn eine Komponente ein HTML-Element mit einem großgeschriebenen ersten Buchstaben enthält, der nicht mit einem Komponentennamen in demselben Namespace identisch ist, wird eine Warnung ausgegeben, dass das Element einen unerwarteten Namen aufweist. Durch das Hinzufügen einer @using-Anweisung für den Namespace der Komponente wird die Komponente zur Verfügung gestellt, wodurch die Warnung nicht mehr auftritt. Weitere Informationen finden Sie im Abschnitt Namespaces.

Das in diesem Abschnitt gezeigte Beispiel für die Heading-Komponente weist keine @page-Anweisung auf. Daher können Benutzer nicht über eine Anforderung im Browser direkt auf die Heading-Komponente zugreifen. Jede Komponente mit einer @page-Anweisung kann jedoch in einer anderen Komponente geschachtelt werden. Wenn auf die Heading-Komponente direkt zugegriffen werden kann, indem am Anfang der Razor-Datei @page "/heading" eingeschlossen wird, wird die Komponente für Browseranforderungen sowohl unter /heading als auch unter /heading-example gerendert.

Namespaces

In der Regel wird der Namespace einer Komponente aus dem Stammnamespace der App und dem Speicherort (Ordner) der Komponente in der App abgeleitet. Wenn der Stammnamespace der App BlazorSample lautet und sich die Counter Komponente im Ordner Pages befindet, gilt Folgendes:

  • der Namespace der Counter-Komponente BlazorSample.Pages, und
  • der vollqualifizierten Typname der Komponente ist BlazorSample.Pages.Counter.

Fügen Sie für benutzerdefinierte Ordner, die Komponenten enthalten, der übergeordneten Komponente oder der _Imports.razor-Datei der App eine @using-Anweisung hinzu. Das folgende Beispiel stellt Komponenten im Ordner Components zur Verfügung:

@using BlazorSample.Components

Hinweis

@using-Direktive in der _Imports.razor-Datei werden nur auf Razor-Dateien (.razor) angewendet und nicht auf C#-Dateien (.cs).

Auf Komponenten kann auch mit ihrem vollqualifizierten Namen verwiesen werden. Hierfür ist keine @using-Anweisung erforderlich. Im folgenden Beispiel wird direkt auf die ProductDetail-Komponente im Ordner Components der App verwiesen:

<BlazorSample.Components.ProductDetail />

Der Namespace einer mit Razor erstellten Komponente basiert auf Folgendem (nach Priorität):

  • Die @namespace-Anweisung im Markup der Razor-Datei (z. B. @namespace BlazorSample.CustomNamespace).
  • Der RootNamespace des Projekts in der Projektdatei (z. B. <RootNamespace>BlazorSample</RootNamespace>).
  • Der Projektname, der aus dem Namen der Projektdatei (.csproj) und dem Pfad vom Projektstamm zur Komponente resultiert. Das Framework löst beispielsweise für die Index-Komponente den Pfad {PROJECT ROOT}/Pages/Index.razor mit dem Projektnamespace BlazorSample (BlazorSample.csproj) in den Namespace BlazorSample.Pages auf. {PROJECT ROOT} ist der Projektstammpfad. Komponenten folgen den Namensbindungsregeln für C#. Für die Index-Komponente in diesem Beispiel werden folgende Komponenten berücksichtigt:
    • Im gleichen Ordner: Pages.
    • Komponenten im Stammverzeichnis des Projekts, die nicht explizit einen anderen Namespace angeben

Folgendes wird nicht unterstützt:

  • Die global::-Qualifizierung.
  • Das Importieren von Komponenten mit using-Aliasanweisungen. @using Foo = Bar wird beispielsweise nicht unterstützt.
  • Unvollständig qualifizierte Namen. Beispielsweise können Sie einer Komponente nicht @using BlazorSample hinzufügen und dann mit <Shared.NavMenu></Shared.NavMenu> auf die NavMenu-Komponente im Ordner Shared (Shared/NavMenu.razor) der App verweisen.

Unterstützung für partielle Klassen

Komponenten werden als partielle C#-Klassen generiert und mit einer der folgenden Vorgehensweisen erstellt:

  • Eine einzelne Datei enthält C#-Code, der in einem oder mehreren @code-Blöcken, HTML-Markup und Razor-Markup definiert ist. In Blazor-Projektvorlagen werden Komponenten mithilfe einer einzelnen Datei definiert.
  • HTML- und Razor-Markup werden in einer Razor-Datei (.razor) gespeichert. C#-Code wird in einer CodeBehind-Datei gespeichert, die als partielle Klasse (.cs) definiert ist.

Hinweis

Ein Komponentenstylesheet, das komponentenspezifische Stile definiert, ist eine eigene Datei (.css). Die CSS-Isolation von Blazor wird weiter unten unter CSS-Isolation von Blazor in ASP.NET Core beschrieben.

Das folgende Beispiel zeigt die Counter-Standardkomponente mit einem @code-Block in einer App, die aus einer Blazor-Projektvorlage generiert wurde. Markup und C#-Code befinden sich in derselben Datei. Dies ist der häufigste Ansatz bei der Komponentenerstellung.

Pages/Counter.razor:

@page "/counter"

<h1>Counter</h1>

<p>Current count: @currentCount</p>

<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>

@code {
    private int currentCount = 0;

    private void IncrementCount()
    {
        currentCount++;
    }
}

In der folgenden Counter-Komponente wird das HTML- und Razor-Markup aus C#-Code unter Verwendung einer CodeBehind-Datei mit einer partiellen Klasse aufgeteilt:

Pages/CounterPartialClass.razor:

@page "/counter-partial-class"

<h1>Counter</h1>

<p>Current count: @currentCount</p>

<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>

Pages/CounterPartialClass.razor.cs:

namespace BlazorSample.Pages
{
    public partial class CounterPartialClass
    {
        private int currentCount = 0;

        void IncrementCount()
        {
            currentCount++;
        }
    }
}

@using-Direktive in der _Imports.razor-Datei werden nur auf Razor-Dateien (.razor) angewendet und nicht auf C#-Dateien (.cs). Fügen Sie einer partiellen Klassendatei ggf. Namespaces hinzu.

Typische Namespaces, die von Komponenten verwendet werden:

using System.Net.Http;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Components.Authorization;
using Microsoft.AspNetCore.Components.Forms;
using Microsoft.AspNetCore.Components.Routing;
using Microsoft.AspNetCore.Components.Web;
using Microsoft.AspNetCore.Components.Web.Virtualization;
using Microsoft.JSInterop;

Typische Namespaces enthalten auch den Namespace der App und den Namespace, der dem Ordner Shared der App entspricht:

using BlazorSample;
using BlazorSample.Shared;

Angeben einer Basisklasse

Die @inherits-Anweisung wird verwendet, um eine Basisklasse für eine Komponente anzugeben. Das folgende Beispiel zeigt, wie eine Komponente eine Basisklasse erben kann, um die Eigenschaften und Methoden der Komponente bereitzustellen. Die Basisklasse BlazorRocksBase wird von ComponentBase abgeleitet.

Pages/BlazorRocks.razor:

@page "/blazor-rocks"
@inherits BlazorRocksBase

<h1>@BlazorRocksText</h1>

BlazorRocksBase.cs:

using Microsoft.AspNetCore.Components;

namespace BlazorSample
{
    public class BlazorRocksBase : ComponentBase
    {
        public string BlazorRocksText { get; set; } =
            "Blazor rocks the browser!";
    }
}

Komponentenparameter

Komponentenparameter übergeben Daten an Komponenten und werden mit öffentlichen C#-Eigenschaften für die Komponentenklasse mit dem [Parameter]-Attribut definiert. Im folgenden Beispiel werden ein integrierter Verweistyp (System.String) und ein benutzerdefinierter Verweistyp (PanelBody) als Komponentenparameter übergeben.

PanelBody.cs:

public class PanelBody
{
    public string Text { get; set; }
    public string Style { get; set; }
}

Shared/ParameterChild.razor:

<div class="card w-25" style="margin-bottom:15px">
    <div class="card-header font-weight-bold">@Title</div>
    <div class="card-body" style="font-style:@Body.Style">
        @Body.Text
    </div>
</div>

@code {
    [Parameter]
    public string Title { get; set; } = "Set By Child";

    [Parameter]
    public PanelBody Body { get; set; } =
        new()
        {
            Text = "Set by child.",
            Style = "normal"
        };
}

Warnung

Die Bereitstellung von Anfangswerten für Komponentenparameter wird unterstützt. Erstellen Sie jedoch keine Komponente, die in die eigenen Parameter schreibt, nachdem die Komponente zum ersten Mal gerendert wurde. Weitere Informationen finden Sie im Abschnitt Überschriebene Parameter dieses Artikels.

Die Komponentenparameter Title und Body der ParameterChild-Komponente werden durch Argumente im HTML-Tag festgelegt, das die Instanz der Komponente rendert. Die folgende ParameterParent-Komponente rendert zwei ParameterChild-Komponenten:

  • Die erste ParameterChild-Komponente wird ohne Angabe von Parameterargumenten gerendert.
  • Die zweite ParameterChild-Komponente empfängt Werte für Title und Body von der ParameterParent-Komponente, die mit einem expliziten C#-Ausdruck die Werte der Eigenschaften von PanelBody festlegt.

Pages/ParameterParent.razor:

@page "/parameter-parent"

<h1>Child component (without attribute values)</h1>

<ParameterChild />

<h1>Child component (with attribute values)</h1>

<ParameterChild Title="Set by Parent"
                Body="@(new PanelBody() { Text = "Set by parent.", Style = "italic" })" />

Das folgende gerenderte HTML-Markup der ParameterParent-Komponente enthält Standardwerte der ParameterChild-Komponente, wenn die ParameterParent-Komponente keine Komponentenparameterwerte angibt. Wenn die ParameterParent-Komponente Komponentenparameterwerte angibt, ersetzen diese die Standardwerte der ParameterChild-Komponente.

Hinweis

Im folgenden gerenderten HTML-Markup werden aus Gründen der Übersichtlichkeit keine gerenderten CSS-Formatklassen angezeigt.

<h1>Child component (without attribute values)</h1>

<div>
    <div>Set By Child</div>
    <div>Set by child.</div>
</div>

<h1>Child component (with attribute values)</h1>

<div>
    <div>Set by Parent</div>
    <div>Set by parent.</div>
</div>

Weisen Sie mit dem reservierten Symbol @ von Razor einem Komponentenparameter ein C#-Feld, eine C#-Eigenschaft oder ein Ergebnis einer C#-Methode als HTML-Attributwert zu. Die folgende ParameterParent2-Komponente zeigt vier Instanzen der vorangehenden ParameterChild-Komponente an und legt ihre Title-Parameterwerte folgendermaßen fest:

  • Der Wert des title-Felds.
  • Das Ergebnis der GetTitle-C#-Methode.
  • Das aktuelle lokale Datum im langen Format mit ToLongDateString unter Verwendung eines impliziten C#-Ausdrucks.
  • Die Title-Eigenschaft des panelData-Objekts.

Das Präfix @ ist für Zeichenfolgeparameter erforderlich. Andernfalls geht das Framework davon aus, dass ein Zeichenfolgenliteral festgelegt ist.

Außerhalb von Zeichenfolgen empfehlen wir die Verwendung des Präfixes @ für Nichtliterale, auch wenn sie nicht unbedingt erforderlich sind.

Wir empfehlen die Verwendung des Präfixes @ nicht für Literale (z. B. boolesche Werte), Schlüsselwörter (z. B. this) oder null, aber Sie können sie auf Wunsch verwenden. Zum Beispiel ist IsFixed="@true" ungewöhnlich, wird aber unterstützt.

Anführungszeichen um die Werte von Parameterattributen sind gemäß der HTML5-Spezifikation in den meisten Fällen optional. Beispielsweise wird Value=this anstelle von Value="this" unterstützt. Wir empfehlen jedoch die Verwendung von Anführungszeichen, da sie leichter zu merken und bei webbasierten Technologien weit verbreitet sind.

In der gesamten Dokumentation gibt für Codebeispiele Folgendes:

  • Es werden immer Anführungszeichen verwendet. Beispiel: Value="this".
  • Nichtliterale Zeichen verwenden stets das Präfix @, auch wenn es optional ist. Beispiele: Title="@title", wobei title eine Zeichenfolgenvariable ist. Count="@ct", wobei ct eine numerische Variable ist.
  • Literale außerhalb von Razor-Ausdrücken vermeiden stets @. Beispiel: IsFixed="true".

Pages/ParameterParent2.razor:

@page "/parameter-parent-2"

<ParameterChild Title="@title" />

<ParameterChild Title="@GetTitle()" />

<ParameterChild Title="@DateTime.Now.ToLongDateString()" />

<ParameterChild Title="@panelData.Title" />

@code {
    private string title = "From Parent field";
    private PanelData panelData = new();

    private string GetTitle()
    {
        return "From Parent method";
    }

    private class PanelData
    {
        public string Title { get; set; } = "From Parent object";
    }
}

Hinweis

Wenn Sie einem Komponentenparameter ein C#-Element zuweisen, stellen Sie dem Element das Symbol @ voran und nie das HTML-Attribut des Parameters.

Richtig:

<ParameterChild Title="@title" />

Falsch:

<ParameterChild @Title="title" />

Anders als bei Razor-Seiten (.cshtml) kann Blazor keine asynchronen Aufgaben in einem Razor-Ausdruck ausführen, während eine Komponente gerendert wird. Der Grund hierfür ist, dass Blazor für das Rendering interaktiver Benutzeroberflächen konzipiert ist. In einer interaktiven Benutzeroberfläche muss auf dem Bildschirm immer etwas angezeigt werden. Daher ist es nicht sinnvoll, den Renderingfluss zu blockieren. Stattdessen wird die asynchrone Arbeit während eines der asynchronen Lebenszyklusereignisse ausgeführt. Nach jedem asynchronen Lebenszyklusereignis kann die Komponente wieder gerendert werden. Folgende Razor-Syntax wird nicht unterstützt:

<ParameterChild Title="@await ..." />

Der Code im vorangehenden Beispiel generiert einen Compilerfehler, wenn die App erstellt wird:

Der 'await'-Operator kann nur innerhalb einer 'async'-Methode verwendet werden. Markieren Sie ggf. diese Methode mit dem 'async'-Modifizierer, und ändern Sie ihren Rückgabetyp in 'Task'.

Um einen Wert für den Parameter Title im vorangehenden Beispiel asynchron abzurufen, kann die Komponente das Lebenszyklusereignis OnInitializedAsync verwenden. Dies wird im folgenden Beispiel veranschaulicht:

<ParameterChild Title="@title" />

@code {
    private string title;

    protected override async Task OnInitializedAsync()
    {
        title = await ...;
    }
}

Weitere Informationen finden Sie unter Rendering von Razor-Komponenten in ASP.NET Core.

Die Verwendung eines expliziten Razor-Ausdrucks zur Verkettung von Text mit einem Ausdrucksergebnis für die Zuweisung zu einem Parameter wird nicht unterstützt. Im folgenden Beispiel wird versucht, den Text Set by mit dem Eigenschaftswert eines Objekts zu verketten. Obwohl diese Syntax in einer Razor-Seite (.cshtml) unterstützt wird, ist sie nicht für die Zuweisung zum Title-Parameter des untergeordneten Elements in einer Komponente gültig. Folgende Razor-Syntax wird nicht unterstützt:

<ParameterChild Title="Set by @(panelData.Title)" />

Der Code im vorangehenden Beispiel generiert einen Compilerfehler, wenn die App erstellt wird:

Komponentenattribute unterstützen keine komplexen Inhalte (C# und Markup gemischt).

Um die Zuweisung eines zusammengesetzten Werts zu unterstützen, verwenden Sie eine Methode, ein Feld oder eine Eigenschaft. Im folgenden Beispiel wird die Verkettung von Set by und dem Eigenschaftswert eines Objekts in der C#-Methode GetTitle ausgeführt:

Pages/ParameterParent3.razor:

@page "/parameter-parent-3"

<ParameterChild Title="@GetTitle()" />

@code {
    private PanelData panelData = new();

    private string GetTitle() => $"Set by {panelData.Title}";

    private class PanelData
    {
        public string Title { get; set; } = "Parent";
    }
}

Weitere Informationen finden Sie unter Razor-Syntaxreferenz für ASP.NET Core.

Warnung

Die Bereitstellung von Anfangswerten für Komponentenparameter wird unterstützt. Erstellen Sie jedoch keine Komponente, die in die eigenen Parameter schreibt, nachdem die Komponente zum ersten Mal gerendert wurde. Weitere Informationen finden Sie im Abschnitt Überschriebene Parameter dieses Artikels.

Komponentenparameter sollten als Auto-Eigenschaften deklariert werden, d. h., ihre get- oder set-Zugriffsmethode sollte keine benutzerdefinierte Logik enthalten. Beispielsweise ist die folgende StartData-Eigenschaft eine Auto-Eigenschaft:

[Parameter]
public DateTime StartData { get; set; }

Platzieren Sie die benutzerdefinierte Logik nicht in der get- oder set-Zugriffsmethode, da Komponentenparameter ausschließlich zur Verwendung als Kanal für eine übergeordnete Komponente vorgesehen sind, über den Informationen an eine untergeordnete Komponente fließen. Wenn eine set-Zugriffsmethode einer untergeordneten Komponenteneigenschaft Logik enthält, die das erneute Rendern der übergeordneten Komponente bewirkt, resultiert daraus eine Rendering-Endlosschleife.

So transformieren Sie einen empfangenen Parameterwert:

  • Belassen Sie die Parametereigenschaft als Auto-Eigenschaft, um die bereitgestellten Rohdaten darzustellen.
  • Erstellen Sie eine andere Eigenschaft oder Methode, um die transformierten Daten basierend auf der Parametereigenschaft bereitzustellen.

Überschreiben Sie OnParametersSetAsync, um einen empfangenen Parameter jedes Mal zu transformieren, wenn neue Daten empfangen werden.

Das Schreiben eines Anfangswerts in einen Komponentenparameter wird unterstützt, da anfängliche Wertzuweisungen das automatische Komponentenrendering von Blazor nicht beeinträchtigen. Die folgende Zuweisung der aktuellen lokalen DateTime mit DateTime.Now zu StartData ist eine gültige Syntax in einer Komponente:

[Parameter]
public DateTime StartData { get; set; } = DateTime.Now;

Weisen Sie nach der anfänglichen Zuweisung von DateTime.Now im Entwicklercode keinen Wert zu StartData zu. Weitere Informationen finden Sie im Abschnitt Überschriebene Parameter dieses Artikels.

Routenparameter

Komponenten können in der Routenvorlage der @page-Anweisung Routenparameter angeben. Der Blazor-Router verwendet Routenparameter, um entsprechende Komponentenparameter aufzufüllen.

Optionale Routenparameter werden unterstützt. Im folgenden Beispiel weist der optionale Parameter text den Wert des Routensegments der Eigenschaft Text der Komponente zu. Wenn das Segment nicht vorhanden ist, wird der Wert von Text in der OnInitialized-Lebenszyklusmethode auf fantastic festgelegt.

Pages/RouteParameter.razor:

@page "/route-parameter/{text?}"

<h1>Blazor is @Text!</h1>

@code {
    [Parameter]
    public string Text { get; set; }

    protected override void OnInitialized()
    {
        Text = Text ?? "fantastic";
    }
}

Informationen zu Catch-All-Routenparametern ({*pageRoute}), die Pfade über mehrere Ordnergrenzen hinweg erfassen, finden Sie unter Routing und Navigation in Blazor in ASP.NET Core .

Untergeordnete Inhaltsrenderfragmente

Komponenten können den Inhalt anderer Komponenten festlegen. Die zuweisende Komponente stellt den Inhalt zwischen dem öffnenden und schließenden Tag der untergeordneten Komponente bereit.

Im folgenden Beispiel enthält die RenderFragmentChild-Komponente einen Parameter der ChildContent-Komponenten, der ein als RenderFragment zu renderndes Segment der Benutzeroberfläche darstellt. In der endgültigen HTML-Ausgabe wird der Inhalt an der Position von ChildContent im Razor-Markup der Komponente gerendert.

Shared/RenderFragmentChild.razor:

<div class="card w-25" style="margin-bottom:15px">
    <div class="card-header font-weight-bold">Child content</div>
    <div class="card-body">@ChildContent</div>
</div>

@code {
    [Parameter]
    public RenderFragment ChildContent { get; set; }
}

Wichtig

Die Eigenschaft, die den Inhalt von RenderFragment empfängt, muss gemäß der Konvention ChildContent benannt werden.

Ereignisrückrufe werden für RenderFragment nicht unterstützt.

Die folgende RenderFragmentParent-Komponente stellt Inhalt für das Rendern des RenderFragmentChild durch Einfügen des Inhalts zwischen dem öffnenden und schließenden Tag der untergeordneten Komponente bereit.

Pages/RenderFragmentParent.razor:

@page "/render-fragment-parent"

<h1>Render child content</h1>

<RenderFragmentChild>
    Content of the child component is supplied
    by the parent component.
</RenderFragmentChild>

Aufgrund der Art und Weise, in der Blazor untergeordneten Inhalt rendert, erfordert das Rendern von Komponenten in einer for-Schleife eine lokale Indexvariable, wenn die inkrementierende Schleifenvariable im Inhalt der RenderFragmentChild-Komponente verwendet wird. Das folgende Beispiel kann der vorherigen RenderFragmentParent-Komponente hinzugefügt werden:

<h1>Three children with an index variable</h1>

@for (int c = 0; c < 3; c++)
{
    var current = c;

    <RenderFragmentChild>
        Count: @current
    </RenderFragmentChild>
}

Alternativ können Sie anstelle einer for-Schleife eine foreach-Schleife mit Enumerable.Range verwenden. Das folgende Beispiel kann der vorherigen RenderFragmentParent-Komponente hinzugefügt werden:

<h1>Second example of three children with an index variable</h1>

@foreach (var c in Enumerable.Range(0,3))
{
    <RenderFragmentChild>
        Count: @c
    </RenderFragmentChild>
}

Renderfragmente werden dazu verwendet, untergeordnete Inhalte in allen Blazor Apps zu rendern und werden in den folgenden Artikeln und Artikelabschnitten mit Beispielen beschrieben:

Hinweis

Die in das Blazor Framework integrierten Razor Komponenten verwenden die selbe ChildContent Komponentenparameterkonvention, um ihren Inhalt festzulegen. Sie können die Komponenten sehen, die untergeordnete Inhalte festlegen, indem Sie in der API Dokumentation nach dem Komponenten-Parametereigenschaftsnamen ChildContent suchen (filtert API mit dem Suchbegriff "ChildContent").

Rendern von Fragmenten für wiederverwendbare Renderinglogik

Sie können untergeordnete Komponenten ausschließlich als Möglichkeit zum Wiederverwenden von Renderinglogik ausklammern. Definieren Sie in jedem @code Block der Komponente ein RenderFragment und rendern Sie das Fragment von einem beliebigen Ort aus so oft wie nötig:

<h1>Hello, world!</h1>

@RenderWelcomeInfo

<p>Render the welcome info a second time:</p>

@RenderWelcomeInfo

@code {
    private RenderFragment RenderWelcomeInfo = __builder =>
    {
        <p>Welcome to your new app!</p>
    };
}

Weitere Informationen finden Sie unter Wiederverwenden von Renderinglogik.

Überschriebene Parameter

Das Blazor-Framework erzwingt im Allgemeinen eine sichere Parameterzuordnung von über- und untergeordneten Elementen:

  • Parameter werden nicht unerwartet überschrieben.
  • Nebenwirkungen werden auf ein Minimum reduziert. Beispielsweise werden zusätzliche Rendervorgänge vermieden, da Sie zu unendlichen Renderingschleifen führen können.

Eine untergeordnete Komponente empfängt neue Parameterwerte, die möglicherweise vorhandene Werte überschreiben, wenn die übergeordnete Komponente erneut gerendert wird. Das versehentliche Überschreiben von Parameterwerten in einer untergeordneten Komponente tritt häufig auf, wenn die Komponente mit mindestens einem datengebundenen Parameter entwickelt wird und der Entwickler direkt in einen Parameter im untergeordneten Element schreibt:

  • Die untergeordnete Komponente wird mit mindestens einem Parameterwert aus der übergeordneten Komponente gerendert.
  • Das untergeordnete Element schreibt direkt in den Wert eines Parameters.
  • Die übergeordnete Komponente wird erneut gerendert und überschreibt den Wert des Parameters des untergeordneten Elements.

Die Möglichkeit, Parameterwerte zu überschreiben, gilt auch für set-Eigenschaftenzugriffsmethoden der untergeordneten Komponente.

Wichtig

Grundsätzlich wird empfohlen, keine Komponenten zu erstellen, die direkt in die eigenen Parameter schreiben, nachdem die Komponente zum ersten Mal gerendert wurde.

Angenommen, die folgende Expander-Komponente:

  • Rendert untergeordneten Inhalt.
  • Schaltet die Anzeige von untergeordnetem Inhalt mit einem Komponentenparameter (Expanded) um.

Nachdem die folgende Expander-Komponente einen überschriebenen Parameter gezeigt hat, wird eine modifizierte Expander-Komponente gezeigt, um den richtigen Ansatz für dieses Szenario zu veranschaulichen. Die folgenden Beispiele können in eine lokale Beispiel-App eingefügt werden, um die beschriebenen Verhaltensweisen nachzuvollziehen.

Shared/Expander.razor:

<div @onclick="Toggle" class="card bg-light mb-3" style="width:30rem">
    <div class="card-body">
        <h2 class="card-title">Toggle (<code>Expanded</code> = @Expanded)</h2>

        @if (Expanded)
        {
            <p class="card-text">@ChildContent</p>
        }
    </div>
</div>

@code {
    [Parameter]
    public bool Expanded { private get; set; }

    [Parameter]
    public RenderFragment ChildContent { get; set; }

    private void Toggle()
    {
        Expanded = !Expanded;
    }
}

Die Expander-Komponente wird der folgenden übergeordneten ExpanderExample-Komponente hinzugefügt, die StateHasChanged aufrufen kann:

Pages/ExpanderExample.razor:

@page "/expander-example"

<Expander Expanded="true">
    Expander 1 content
</Expander>

<Expander Expanded="true" />

<button @onclick="StateHasChanged">
    Call StateHasChanged
</button>

Anfänglich verhalten sich die Expander-Komponenten unabhängig, wenn ihre Expanded-Eigenschaften umgeschaltet werden. Die untergeordneten Komponenten behalten ihre Zustände erwartungsgemäß bei.

Wenn StateHasChanged in einer übergeordneten Komponente aufgerufen wird, rendert das Blazor-Framework untergeordnete Komponenten neu, wenn sich ihre Parameter geändert haben könnten:

  • Bei einer Gruppe von Parametertypen, die von Blazor explizit überprüft werden, rendert Blazor eine untergeordnete Komponente neu, wenn erkannt wird, dass sich Parameter geändert haben.
  • Bei nicht überprüften Parametertypen wird die untergeordnete Blazor-Komponente neu gerendert – unabhängig davon, ob sich die Parameter geändert haben oder nicht. Untergeordneter Inhalt fällt in diese Kategorie von Parametertypen, weil untergeordneter Inhalt dem Typ RenderFragment entspricht, einem Delegat, der auf andere stummschaltbare Objekte verweist.

Für die ExpanderExample-Komponente:

  • Die erste Expander-Komponente legt untergeordneten Inhalt in einem potenziell stummschaltbaren RenderFragment fest, sodass ein Aufruf von StateHasChanged in der übergeordneten Komponente die Komponente automatisch neu rendert und möglicherweise den Wert von Expanded mit dem Anfangswert true überschreibt.
  • Die zweite Expander-Komponente legt keinen untergeordneten Inhalt fest. Daher gibt es keinen potenziell stummschaltbaren RenderFragment. Durch einen Aufruf von StateHasChanged in der übergeordneten Komponente wird die untergeordnete Komponente nicht automatisch neu gerendert, sodass der Expanded-Wert der Komponente nicht überschrieben wird.

Um den Zustand im vorangehenden Szenario beizubehalten, verwenden Sie ein privates Feld in der Expander-Komponente, um dessen Umschaltungszustand beizubehalten.

Die folgende überarbeitete Expander-Komponente:

  • akzeptiert den Wert des Expanded-Komponentenparameters aus der übergeordneten Komponente.
  • weist den Wert des Komponentenparameters einem privaten Feld (expanded) im OnInitialized-Ereignis zu.
  • Verwendet das private Feld, um den internen Umschaltzustand zu verwalten. Dies veranschaulicht, wie das direkte Schreiben in einen Parameter vermieden wird.

Hinweis

Die Hinweise in diesem Abschnitt gelten auch für ähnliche Logik in den set-Zugriffsmethoden für Komponentenparameter, die zu ähnlichen unerwünschten Nebenwirkungen führen kann.

Shared/Expander.razor:

<div @onclick="Toggle" class="card bg-light mb-3" style="width:30rem">
    <div class="card-body">
        <h2 class="card-title">Toggle (<code>expanded</code> = @expanded)</h2>

        @if (expanded)
        {
            <p class="card-text">@ChildContent</p>
        }
    </div>
</div>

@code {
    private bool expanded;

    [Parameter]
    public bool Expanded { private get; set; }

    [Parameter]
    public RenderFragment ChildContent { get; set; }

    protected override void OnInitialized()
    {
        expanded = Expanded;
    }

    private void Toggle()
    {
        expanded = !expanded;
    }
}

Weitere Informationen finden Sie unter Bidirektionaler Blazor-Bindungsfehler (dotnet/aspnetcore #24599).

Weitere Informationen zur Änderungserkennung, einschließlich Informationen zu den genauen Typen, die von Blazor überprüft werden, finden Sie unter Razor-Komponentenrendering unter ASP.NET Core.

Attributsplatting und arbiträre Parameter

Komponenten können zusätzlich zu den deklarierten Parametern der Komponente weitere Attribute erfassen und rendern. Zusätzliche Attribute können in einem Wörterbuch erfasst und dann für ein Element gesplattet werden, wenn die Komponente mithilfe des Razor-Anweisungsattributs @attributes gerendert wird. Diese Option ist nützlich, um eine Komponente zu definieren, die ein Markupelement erzeugt, das viele verschiedene Anpassungen unterstützt. Beispielsweise kann es mühsam sein, Attribute für ein <input>-Element separat zu definieren, das viele Parameter unterstützt.

In der folgenden Splat-Komponente:

  • Das erste <input>-Element (id="useIndividualParams") verwendet einzelne Komponentenparameter.
  • Das zweite <input>-Element (id="useAttributesDict") verwendet Attributsplatting.

Pages/Splat.razor:

@page "/splat"

<input id="useIndividualParams"
       maxlength="@maxlength"
       placeholder="@placeholder"
       required="@required"
       size="@size" />

<input id="useAttributesDict"
       @attributes="InputAttributes" />

@code {
    private string maxlength = "10";
    private string placeholder = "Input placeholder text";
    private string required = "required";
    private string size = "50";

    private Dictionary<string, object> InputAttributes { get; set; } =
        new()
        {
            { "maxlength", "10" },
            { "placeholder", "Input placeholder text" },
            { "required", "required" },
            { "size", "50" }
        };
}

Die gerenderten <input>-Elemente auf der Webseite sind identisch:

<input id="useIndividualParams"
       maxlength="10"
       placeholder="Input placeholder text"
       required="required"
       size="50">

<input id="useAttributesDict"
       maxlength="10"
       placeholder="Input placeholder text"
       required="required"
       size="50">

Definieren Sie einen Komponentenparameter, bei dem die CaptureUnmatchedValues-Eigenschaft auf true festgelegt ist, damit beliebige Attribute akzeptiert werden:

@code {
    [Parameter(CaptureUnmatchedValues = true)]
    public Dictionary<string, object> InputAttributes { get; set; }
}

Wenn die CaptureUnmatchedValues-Eigenschaft auf [Parameter] festgelegt ist, kann der Parameter alle Attribute abgleichen, die keinem anderen Parameter entsprechen. Eine Komponente kann nur einen einzelnen Parameter mit CaptureUnmatchedValues definieren. Der mit CaptureUnmatchedValues verwendete Eigenschaftentyp muss von Dictionary<string, object> mit Zeichenfolgenschlüsseln zugewiesen werden können. Die Verwendung von IEnumerable<KeyValuePair<string, object>> oder IReadOnlyDictionary<string, object> ist in diesem Szenario ebenfalls möglich.

Die Position von @attributes relativ zur Position der Elementattribute ist wichtig. Wenn @attributes-Anweisungen für das Element gesplattet werden, werden die Attribute von rechts nach links (letztes bis erstes) verarbeitet. Sehen Sie sich das folgende Beispiel für eine übergeordnete Komponente an, die eine untergeordnete Komponente verwendet:

Shared/AttributeOrderChild1.razor:

<div @attributes="AdditionalAttributes" extra="5" />

@code {
    [Parameter(CaptureUnmatchedValues = true)]
    public IDictionary<string, object> AdditionalAttributes { get; set; }
}

Pages/AttributeOrderParent1.razor:

@page "/attribute-order-parent-1"

<AttributeOrderChild1 extra="10" />

Das extra-Attribut der AttributeOrderChild1-Komponente ist rechts von @attributes festgelegt. Das gerenderte <div>-Element der AttributeOrderParent1-Komponente enthält extra="5", wenn dieses über das zusätzliche Attribut weitergeleitet wird, da die Attribute von rechts nach links (letztes bis erstes) verarbeitet werden:

<div extra="5" />

Im folgenden Beispiel wird die Reihenfolge von extra und @attributes im <div>-Element der untergeordneten Komponente umgekehrt:

Shared/AttributeOrderChild2.razor:

<div extra="5" @attributes="AdditionalAttributes" />

@code {
    [Parameter(CaptureUnmatchedValues = true)]
    public IDictionary<string, object> AdditionalAttributes { get; set; }
}

Pages/AttributeOrderParent2.razor:

@page "/attribute-order-parent-2"

<AttributeOrderChild2 extra="10" />

Das <div>-Element auf der gerenderten Webseite der übergeordneten Komponente enthält extra="10", wenn es über das zusätzliche Attribut übergeben wird:

<div extra="10" />

Erfassen von Verweisen auf Komponenten

Komponentenverweise bieten eine Möglichkeit, auf eine Komponenteninstanz zum Ausgeben von Befehlen zu verweisen. So erfassen Sie einen Komponentenverweis:

  • Fügen Sie der untergeordneten Komponente ein @ref-Attribut hinzu.
  • Definieren Sie ein Feld mit demselben Typ wie die untergeordnete Komponente.

Wenn die Komponente gerendert wird, wird das Feld mit der Komponenteninstanz aufgefüllt. Anschließend können Sie .NET-Methoden für die Instanz aufrufen.

Betrachten Sie die folgende ReferenceChild-Komponente, die eine Meldung protokolliert, wenn die ChildMethod aufgerufen wird.

Shared/ReferenceChild.razor:

@using Microsoft.Extensions.Logging
@inject ILogger<ReferenceChild> logger

@code {
    public void ChildMethod(int value)
    {
        logger.LogInformation("Received {Value} in ChildMethod", value);
    }
}

Ein Komponentenverweis wird erst aufgefüllt, nachdem die Komponente gerendert wurde und die Ausgabe das Element von ReferenceChild enthält. Vor dem Rendern der Komponente gibt es nichts, auf das verwiesen werden kann.

Sie können Komponentenverweise bearbeiten, nachdem die Komponente das Rendering abgeschlossen hat, indem Sie die OnAfterRender- oder die OnAfterRenderAsync-Methode verwenden.

Um eine Verweisvariable mit einem Ereignishandler zu verwenden, verwenden Sie einen Lambdaausdruck oder weisen einen Ereignishandlerdelegaten in den Methoden OnAfterRender oder OnAfterRenderAsync zu. So ist sichergestellt, dass die Verweisvariable vor dem Ereignishandler zugewiesen wird.

Im folgenden Ansatz mit einem Lambdaausdruck wird die vorherige ReferenceChild-Komponente verwendet.

Pages/ReferenceParent1.razor:

@page "/reference-parent-1"

<button @onclick="@(() => childComponent.ChildMethod(5))">
    Call <code>ReferenceChild.ChildMethod</code> with an argument of 5
</button>

<ReferenceChild @ref="childComponent" />

@code {
    private ReferenceChild childComponent;
}

Im folgenden Ansatz mit einem Delegaten wird die vorherige ReferenceChild-Komponente verwendet.

Pages/ReferenceParent2.razor:

@page "/reference-parent-2"

<button @onclick="callChildMethod">
    Call <code>ReferenceChild.ChildMethod</code> with an argument of 5
</button>

<ReferenceChild @ref="childComponent" />

@code {
    private ReferenceChild childComponent;
    private Action callChildMethod;

    protected override void OnAfterRender(bool firstRender)
    {
        if (firstRender)
        {
            callChildMethod = CallChildMethod;
        }
    }

    private void CallChildMethod()
    {
        childComponent.ChildMethod(5);
    }
}

Beim Erfassen von Komponentenverweisen wird zwar eine ähnliche Syntax verwendet, um Elementverweise zu erfassen, das Erfassen von Komponentenverweisen ist jedoch kein JavaScript-Interop-Feature. Komponentenverweise werden nicht an JavaScript-Code übermittelt. Komponentenverweise werden nur in .NET-Code verwendet.

Wichtig

Verwenden Sie keine Komponentenverweise verwenden, um den Zustand von untergeordneten Komponenten zu verändern. Verwenden Sie stattdessen normale deklarative Komponentenparameter, um Daten an untergeordnete Komponenten zu übergeben. Die Verwendung von Komponentenparametern bewirkt, dass untergeordnete Komponenten automatisch zu den richtigen Zeitpunkten erneut gerendert werden. Weitere Informationen finden Sie im Abschnitt Komponentenparameter und im Artikel Datenbindung in Blazor in ASP.NET Core.

Synchronisierungskontext

Blazor verwendet einen Synchronisierungskontext (SynchronizationContext), um einen einzelnen logischen Ausführungsthread zu erzwingen. Die Lebenszyklusmethoden einer Komponente und Ereignisrückrufe, die von Blazor ausgelöst werden, werden in diesem Synchronisierungskontext ausgeführt.

Der Synchronisierungskontext des Blazor Server versucht, eine Singlethreadumgebung zu emulieren, sodass er genau mit dem WebAssembly-Modell im Browser übereinstimmt, das ein Singlethreadmodell ist. Zu jedem Zeitpunkt wird die Arbeit für genau einen Thread ausgeführt, woraus der Eindruck eines einzelnen logischen Threads entsteht. Zwei Vorgänge werden nicht gleichzeitig ausgeführt.

Vermeiden Sie eine Threadblockierung von Aufrufen.

Rufen Sie generell in Komponenten nicht die folgenden Methoden auf. Die folgenden Methoden blockieren die Threadausführung und hindern somit die App an der Wiederaufnahme der Arbeit, bis der zugrunde liegende Task beendet ist:

Hinweis

In den Beispielen in der Blazor-Dokumentation werden die in diesem Abschnitt erwähnten Threadblockierungsmethoden nur zu Demonstrationszwecken und nicht als Codierungsempfehlung verwendet. Beispielsweise simulieren einige Komponentencodedemonstrationen einen Prozess mit langer Ausführungsdauer, indem Thread.Sleep aufgerufen wird.

Externes Aufrufen von Komponentenmethoden zur Aktualisierung des Status

Wenn eine Komponente aufgrund eines externen Ereignisses (z. B. eines Timers oder anderer Benachrichtigungen) aktualisiert werden muss, verwenden Sie die InvokeAsync-Methode, mit der die Codeausführung an den Synchronisierungskontext von Blazor weitergeleitet wird. Betrachten Sie beispielsweise den folgenden Benachrichtigungsdienst, der jede lauschende Komponente über den aktualisierten Zustand benachrichtigen kann. Die Update-Methode kann überall in der App aufgerufen werden.

TimerService.cs:

using System;
using System.Timers;
using Microsoft.Extensions.Logging;

public class TimerService : IDisposable
{
    private int elapsedCount;
    private readonly ILogger<TimerService> logger;
    private readonly NotifierService notifier;
    private Timer timer;

    public TimerService(NotifierService notifier, ILogger<TimerService> logger)
    {
        this.notifier = notifier;
        this.logger = logger;
    }

    public void Start()
    {
        if (timer is null)
        {
            timer = new();
            timer.AutoReset = true;
            timer.Interval = 10000;
            timer.Elapsed += HandleTimer;
            timer.Enabled = true;
            logger.LogInformation("Started");
        }
    }

    private async void HandleTimer(object source, ElapsedEventArgs e)
    {
        elapsedCount += 1;
        await notifier.Update("elapsedCount", elapsedCount);
        logger.LogInformation($"elapsedCount: {elapsedCount}");
    }

    public void Dispose()
    {
        timer?.Dispose();
    }
}

NotifierService.cs:

using System;
using System.Threading.Tasks;

public class NotifierService
{
    public async Task Update(string key, int value)
    {
        if (Notify != null)
        {
            await Notify.Invoke(key, value);
        }
    }

    public event Func<string, int, Task> Notify;
}

Registrieren des Diensts:

  • Registrieren Sie in einer Blazor WebAssembly-App die Dienste als Singleton in Program.cs:

    builder.Services.AddSingleton<NotifierService>();
    builder.Services.AddSingleton<TimerService>();
    
  • Registrieren Sie in einer Blazor Server-App die Dienste bereichsbezogen in Startup.ConfigureServices:

    services.AddScoped<NotifierService>();
    services.AddScoped<TimerService>();
    

Verwenden Sie NotifierService, um eine Komponente zu aktualisieren.

Pages/ReceiveNotifications.razor:

@page "/receive-notifications"
@implements IDisposable
@inject NotifierService Notifier
@inject TimerService Timer

<h1>Receive Notifications</h1>

<h2>Timer Service</h2>

<button @onclick="StartTimer">Start Timer</button>

<h2>Notifications</h2>

<p>
    Status:
    @if (lastNotification.key is not null)
    {
        <span>@lastNotification.key = @lastNotification.value</span>
    }
    else
    {
        <span>Awaiting first notification</span>
    }
</p>

@code {
    private (string key, int value) lastNotification;

    protected override void OnInitialized()
    {
        Notifier.Notify += OnNotify;
    }

    public async Task OnNotify(string key, int value)
    {
        await InvokeAsync(() =>
        {
            lastNotification = (key, value);
            StateHasChanged();
        });
    }

    private void StartTimer()
    {
        Timer.Start();
    }

    public void Dispose()
    {
        Notifier.Notify -= OnNotify;
    }
}

Im vorherigen Beispiel:

  • NotifierService ruft die OnNotify-Methode der Komponente außerhalb des Synchronisierungskontexts von Blazor auf. InvokeAsync wird verwendet, um zum richtigen Kontext zu wechseln und ein Rendering in die Warteschlange zu stellen. Weitere Informationen finden Sie unter Rendering von Razor-Komponenten in ASP.NET Core.
  • Die Komponente implementiert IDisposable. Das Abonnement des OnNotify-Delegaten wird in der Dispose-Methode gekündigt. Diese wird vom Framework aufgerufen, wenn die Komponente verworfen wird. Weitere Informationen finden Sie unter Rendering von Razor-Komponenten in ASP.NET Core.

Verwenden von @key zur Steuerung der Beibehaltung von Elementen und Komponenten

Wenn Sie eine Element- oder Komponentenliste rendern und die Elemente oder Komponenten nachfolgend geändert werden, muss Blazor bestimmen, welche der vorherigen Elemente oder Komponenten beibehalten werden können und wie Modellobjekte diesen zugeordnet werden sollen. Normalerweise erfolgt dieser Prozess automatisch und kann ignoriert werden, aber in einigen Fällen sollten Sie den Prozess steuern.

Betrachten Sie die folgenden Komponenten Details und People:

  • Die Details-Komponente empfängt Daten (Data) von der übergeordneten People-Komponente, die in einem <input>-Element angezeigt wird. Jedes angezeigte <input>-Element kann den Fokus der Seite vom Benutzer erhalten, wenn er eines der <input>-Elemente auswählt.
  • Die People-Komponente erstellt mithilfe der Details-Komponente eine Liste anzuzeigender person-Objekte. Alle drei Sekunden wird der Sammlung eine neue Person hinzugefügt.

Diese Demonstration ermöglicht Ihnen Folgendes:

  • Auswählen eines <input> aus mehreren gerenderten Details-Komponenten
  • Untersuchen des Verhaltens des Fokus der Seite, wenn die Größe der people-Sammlung automatisch zunimmt

Shared/Details.razor:

<input value="@Data" />

@code {
    [Parameter]
    public string Data { get; set; }
}

In der folgenden People-Komponente führt jede Iteration für das Hinzufügen einer Person in OnTimerCallback dazu, dass Blazor die gesamte Sammlung neu erstellt. Der Fokus der Seite bleibt auf derselben Indexposition der <input>-Elemente, sodass sich der Fokus jedes Mal verschiebt, wenn eine Person hinzugefügt wird. Das Abweichen des Fokus von der Auswahl durch den Benutzer ist kein wünschenswertes Verhalten. Nachdem mit der folgenden Komponente das unzureichende Verhalten veranschaulicht wurde, wird das @key-Anweisungsattribut verwendet, um die Benutzerfreundlichkeit zu verbessern.

Pages/People.razor:

@page "/people"
@using System.Timers
@implements IDisposable

@foreach (var person in people)
{
    <Details Data="@person.Data" />
}

@code {
    private Timer timer = new Timer(3000);

    public List<Person> people =
        new()
        {
            { new Person { Data = "Person 1" } },
            { new Person { Data = "Person 2" } },
            { new Person { Data = "Person 3" } }
        };

    protected override void OnInitialized()
    {
        timer.Elapsed += (sender, eventArgs) => OnTimerCallback();
        timer.Start();
    }

    private void OnTimerCallback()
    {
        _ = InvokeAsync(() =>
        {
            people.Insert(0,
                new Person
                {
                    Data = $"INSERTED {DateTime.Now.ToString("hh:mm:ss tt")}"
                });
            StateHasChanged();
        });
    }

    public void Dispose() => timer.Dispose();

    public class Person
    {
        public string Data { get; set; }
    }
}

Der Inhalt der people-Sammlung ändert sich durch eingefügte, gelöschte oder neu sortierte Einträge. Erneutes Rendering kann zu sichtbaren Verhaltensunterschieden führen. Jedes Mal, wenn eine Person in die people-Sammlung eingefügt wird, erhält das Element den Fokus, das dem Element mit dem aktuellen Fokus vorangestellt ist. Der Fokus des Benutzers geht verloren.

Das Zuordnen von Elementen oder Komponenten zu einer Sammlung kann mit dem @key-Anweisungsattribut gesteuert werden. Die Verwendung von @key garantiert, dass Elemente oder Komponenten basierend auf dem Wert von @key beibehalten werden. Wenn in der Details-Komponente im vorherigen Beispiel @key auf person festgelegt ist, erfolgt in Blazor kein erneutes Rendering von Details-Komponenten, die sich nicht geändert haben.

Um für die People-Komponente das @key-Anweisungsattribut mit der people-Sammlung zu verwenden, aktualisieren Sie das <Details>-Element wie folgt:

<Details @key="person" Data="@person.Data" />

Wenn sich die people-Sammlung ändert, bleibt die Zuordnung zwischen Details-Instanzen und person-Instanzen erhalten. Wenn am Anfang der Sammlung eine Person eingefügt wird, wird an der entsprechenden Position eine neue Details-Instanz eingefügt. Andere Instanzen bleiben unverändert. Daher geht der Fokus des Benutzers nicht verloren, wenn der Sammlung Personen hinzugefügt werden.

Andere Sammlungsaktualisierungen weisen das gleiche Verhalten auf, wenn das @key-Anweisungsattribut verwendet wird:

  • Wenn eine Instanz aus der Sammlung gelöscht wird, wird nur die entsprechende Komponenteninstanz von der Benutzeroberfläche entfernt. Andere Instanzen bleiben unverändert.
  • Wenn Sammmlungseinträge neu sortiert werden, werden die entsprechenden Komponenteninstanzen beibehalten und auf der Benutzeroberfläche neu angeordnet.

Wichtig

Schlüssel sind für jedes Containerelement oder jede Komponente lokal. Schlüssel werden nicht dokumentübergreifend global verglichen.

Empfohlene Verwendung von @key

In der Regel ist es sinnvoll, @key zu verwenden, wenn eine Liste gerendert wird (z. B. in einem foreach-Block) und ein geeigneter Wert vorhanden ist, um @key zu definieren.

Sie können @key auch verwenden, um eine Element- oder Komponentenunterstruktur zu bewahren, wenn sich ein Objekt nicht ändert, wie in den folgenden Beispielen gezeigt.

Beispiel 1:

<li @key="person">
    <input value="@person.Data" />
</li>

Beispiel 2:

<div @key="person">
    @* other HTML elements *@
</div>

Wenn sich eine person-Instanz ändert, erzwingt das @key-Anweisungsattribut in Blazor Folgendes:

  • Verwerfen des gesamten <li>- oder <div>-Elements und seiner Nachfolger
  • Neuerstellen der Unterstruktur innerhalb der Benutzeroberfläche mit neuen Elementen und Komponenten

Dies ist hilfreich, um zu gewährleisten, dass kein Benutzeroberflächenzustand beibehalten wird, wenn sich die Sammlung innerhalb einer Unterstruktur ändert.

Bereich von @key

Die @key-Attributanweisung ist auf ihre eigenen gleichgeordneten Elemente innerhalb des übergeordneten Elements begrenzt.

Betrachten Sie das folgende Beispiel. Die Schlüssel first und second werden im selben Bereich des <div>-Elements miteinander verglichen:

<div>
    <div @key="first">...</div>
    <div @key="second">...</div>
</div>

Im folgenden Beispiel werden die Schlüssel first und second in ihren eigenen Bereichen veranschaulicht, die unabhängig voneinander stehen und keinen Einfluss aufeinander haben. Jeder @key-Bereich gilt nur für sein übergeordnetes <div>-Element, nicht für die übergeordneten <div>-Elemente:

<div>
    <div @key="first">...</div>
</div>
<div>
    <div @key="second">...</div>
</div>

Für die zuvor gezeigte Details-Komponente rendern die folgenden Beispiele person-Daten innerhalb desselben @key-Bereichs und veranschaulichen typische Anwendungsfälle für @key:

<div>
    @foreach (var person in people)
    {
        <Details @key="person" Data="@person.Data" />
    }
</div>
@foreach (var person in people)
{
    <div @key="person">
        <Details Data="@person.Data" />
    </div>
}
<ol>
    @foreach (var person in people)
    {
        <li @key="person">
            <Details Data="@person.Data" />
        </li>
    }
</ol>

Die folgenden Beispiele beschränken sich nur auf den Bereich @key für das <div>- oder <li>-Element, das jede Details-Komponenteninstanz umschließt. Daher werden person-Daten für jedes Member der people-Sammlung nicht für jede person-Instanz für die gerenderten Details-Komponenten schlüsselgebunden. Vermeiden Sie die folgenden Muster bei Verwendung von @key:

@foreach (var person in people)
{
    <div>
        <Details @key="person" Data="@person.Data" />
    </div>
}
<ol>
    @foreach (var person in people)
    {
        <li>
            <Details @key="person" Data="@person.Data" />
        </li>
    }
</ol>

Ungeeignete Fälle für @key

Beim Rendern mit @key wird die Leistung beeinträchtigt. Die Beeinträchtigung der Leistung ist nicht erheblich. Sie sollten @key jedoch nur angeben, wenn sich die Beibehaltung des Elements oder der Komponente positiv auf die App auswirken.

Auch wenn @key nicht verwendet wird, behält Blazor die untergeordneten Element- und Komponenteninstanzen so weit wie möglich bei. Der einzige Vorteil bei der Verwendung von @key besteht in der Kontrolle darüber, wie Modellinstanzen den beibehaltenen Komponenteninstanzen zugeordnet werden, anstatt die Zuordnung durch Blazor bestimmen zu lassen.

Zu verwendende Werte für @key

Im Allgemeinen ist es sinnvoll, für @key folgende Arten von Werten bereitzustellen:

  • Modellobjektinstanzen. Beispielsweise wurde im vorherigen Beispiel die Person-Instanz (person) verwendet. Dadurch wird die Beibehaltung basierend auf der Objektverweisgleichheit sichergestellt.
  • Eindeutige Bezeichner. Eindeutige Bezeichner können beispielsweise auf Primärschlüsselwerten vom Typ int, string oder Guid basieren.

Stellen Sie sicher, dass die für @key verwendeten Werte nicht kollidieren. Wenn innerhalb desselben übergeordneten Elements kollidierende Werte erkannt werden, löst Blazor eine Ausnahme aus, da alte Elemente oder Komponenten nicht deterministisch neuen Elementen oder Komponenten zugeordnet werden können. Verwenden Sie nur eindeutige Werte wie Objektinstanzen oder Primärschlüsselwerte.

Hinzufügen eines Attributs

Attribute können in Komponenten mit der @attribute-Anweisung hinzugefügt werden. Im folgenden Beispiel wird das [Authorize]-Attribut auf die Klasse der Komponente angewendet:

@page "/"
@attribute [Authorize]

Attribute für bedingte HTML-Elemente

HTML-Elementattributeigenschaften werden basierend auf dem .NET-Wert bedingt festgelegt. Wenn der Wert false oder null ist, wird die Eigenschaft nicht festgelegt. Wenn der Wert true ist, wird die Eigenschaft festgelegt.

Im folgenden Beispiel bestimmt IsCompleted, ob die checked-Eigenschaft des <input>-Elements festgelegt wird.

Pages/ConditionalAttribute.razor:

@page "/conditional-attribute"

<label>
    <input type="checkbox" checked="@IsCompleted" />
    Is Completed?
</label>

<button @onclick="@(() => IsCompleted = !IsCompleted)">
    Change IsCompleted
</button>

@code {
    [Parameter]
    public bool IsCompleted { get; set; }
}

Weitere Informationen finden Sie unter Razor-Syntaxreferenz für ASP.NET Core.

Warnung

Einige HTML-Attribute wie aria-pressed funktionieren nicht ordnungsgemäß, wenn der .NET-Typ bool ist. Verwenden Sie in diesen Fällen statt bool einen string-Typ.

Unformatierter HTML-Code

Zeichenfolgen werden normalerweise mithilfe von DOM-Textknoten gerendert. Das bedeutet, dass das darin enthaltene Markup vollständig ignoriert und als Literaltext behandelt wird. Sie können unformatierten HTML-Code rendern, indem Sie den HTML-Inhalt mit einem MarkupString-Wert umschließen. Der Wert wird als HTML oder SVG analysiert und in das DOM eingefügt.

Warnung

Das Rendern von unformatiertem HTML-Code, der aus einer nicht vertrauenswürdigen Quelle stammt, gilt als Sicherheitsrisiko und sollte immer vermieden werden.

Im folgenden Beispiel wird veranschaulicht, wie der MarkupString-Typ verwendet wird, um der gerenderten Ausgabe einer Komponente einen Block mit statischem HTML-Inhalt hinzuzufügen.

Pages/MarkupStringExample.razor:

@page "/markup-string-example"

@((MarkupString)myMarkup)

@code {
    private string myMarkup =
        "<p class=\"text-danger\">This is a dangerous <em>markup string</em>.</p>";
}

Razor-Vorlagen

Renderfragmente können mithilfe der Razor-Vorlagensyntax definiert werden, um einen Benutzeroberflächen-Codeausschnitt zu definieren. Für Razor-Vorlagen wird das folgende Format verwendet:

@<{HTML tag}>...</{HTML tag}>

Im folgenden Beispiel wird veranschaulicht, wie Sie RenderFragment- und RenderFragment<TValue>-Werte angeben und Vorlagen direkt in einer-Komponente rendern können. Renderingfragmente können auch als Argumente an Komponentenvorlagen übergeben werden.

Pages/RazorTemplate.razor:

@page "/razor-template"

@timeTemplate

@petTemplate(new Pet { Name = "Nutty Rex" })

@code {
    private RenderFragment timeTemplate = @<p>The time is @DateTime.Now.</p>;
    private RenderFragment<Pet> petTemplate = (pet) => @<p>Pet: @pet.Name</p>;

    private class Pet
    {
        public string Name { get; set; }
    }
}

Gerenderte Ausgabe des vorangehenden Codes:

<p>The time is 4/19/2021 8:54:46 AM.</p>
<p>Pet: Nutty Rex</p>

Statische Ressourcen

In Blazor gilt für statische Ressourcen die Konvention von ASP.NET Core-Apps. Statische Ressourcen befinden sich in einem oder mehreren web root-Ordnern (wwwroot) des Projekts unter dem Ordner wwwroot.

Verwenden Sie einen zur Basis relativen Pfad (/), um auf den Webstamm einer statischen Ressource zu verweisen. Im folgenden Beispiel befindet sich logo.png physisch im Ordner {PROJECT ROOT}/wwwroot/images. {PROJECT ROOT} ist der Projektstamm der App.

<img alt="Company logo" src="/images/logo.png" />

Komponenten unterstützen keine Notation mit Tilde und Schrägstrich (~/).

Informationen zum Festlegen des Basispfads einer App finden Sie unter Hosten und Bereitstellen von Blazor in ASP.NET Core .

Keine Unterstützung von Taghilfsprogrammen in Komponenten

Tag Helpers werden in Komponenten nicht unterstützt. Sie können Taghilfsobjekte in Blazor bereitstellen, indem Sie eine Komponente mit der gleichen Funktionalität wie das Taghilfsprogramm erstellen und diese stattdessen verwenden.

SVG-Bilder

Da Blazor HTML rendert, werden browsergestützte Bilder wie skalierbare Vektorgrafiken (Scalable Vector Graphics, SVG-Grafiken.svg) über das <img>-Tag unterstützt:

<img alt="Example image" src="image.svg" />

Ebenso werden SVG-Bilder in den CSS-Regeln einer Stylesheetdatei (.css) unterstützt:

.element-class {
    background-image: url("image.svg");
}

Verhalten beim Rendern von Leerzeichen

Wenn die @preservewhitespace-Direktive mit dem Wert trueverwendet wird, werden standardmäßig zusätzliche Leerzeichen entfernt, wenn Folgendes gilt:

  • Sie stehen in einem Element am Anfang oder am Ende.
  • Se stehen in einem RenderFragment/RenderFragment<TValue>-Parameter (z. B. untergeordneter Inhalt, der an eine andere Komponente übergeben wird) am Anfang oder am Ende.
  • Sie stehen am Anfang oder Ende eines C#-Codeblocks wie @if oder @foreach.

Das Entfernen der Leerzeichen kann sich auf die gerenderte Ausgabe auswirken, wenn eine CSS-Regel wie white-space: pre verwendet wird. Führen Sie eine der folgenden Aktionen durch, um diese Leistungsoptimierung zu deaktivieren und die Leerzeichen beizubehalten:

  • Fügen Sie oben in der Razor-Datei (.razor) die Anweisung @preservewhitespace true hinzu, um sie auf eine bestimmte Komponente anzuwenden.
  • Fügen Sie die Anweisung @preservewhitespace true in einer _Imports.razor-Datei hinzu, um sie auf ein Unterverzeichnis oder auf das gesamte Projekt anzuwenden.

In den meisten Fällen ist keine Aktion erforderlich, weil sich Apps normalerweise wie gewohnt (aber schneller) verhalten. Falls das Entfernen der Leerzeichen für eine bestimmte Komponente zu Renderingproblemen führt, sollten Sie darin @preservewhitespace true nutzen, um diese Optimierung zu deaktivieren.

Unterstützung generischer Typparameter

Die @typeparam-Direktive deklariert einen generischen Typparameter für die generierte Komponentenklasse:

@typeparam TItem

Im folgenden Beispiel wird die Komponente ListGenericTypeItems1 generisch als TExample typisiert.

Shared/ListGenericTypeItems1.razor:

@typeparam TExample

@if (ExampleList is not null)
{
    <ul>
        @foreach (var item in ExampleList)
        {
            <li>@item</li>
        }
    </ul>
}

@code {
    [Parameter]
    public IEnumerable<TExample> ExampleList{ get; set; }
}

Die folgende GenericTypeExample1-Komponente rendert zwei ListGenericTypeItems1-Komponenten:

  • Zeichenfolgen- oder Integerdaten werden dem Parameter ExampleList jeder Komponente zugewiesen.
  • Für den Typparameter (TExample) jeder Komponente wird je nach Typ der zugewiesenen Daten der Typ string oder int festgelegt.

Pages/GenericTypeExample1.razor:

@page "/generic-type-example-1"

<h1>Generic Type Example 1</h1>

<ListGenericTypeItems1 ExampleList="@(new List<string> { "Item 1", "Item 2" })" 
                       TExample="string" />

<ListGenericTypeItems1 ExampleList="@(new List<int> { 1, 2, 3 })" 
                       TExample="int" />

Weitere Informationen finden Sie in den folgenden Artikeln:

Blazor-Apps werden mit Razor-Komponenten erstellt, die informell als Blazor-Komponenten bezeichnet werden. Eine Komponente ist ein eigenständiger Teil der Benutzeroberfläche mit Verarbeitungslogik, die dynamisches Verhalten ermöglicht. Komponenten können geschachtelt, wiederverwendet, von Projekten gemeinsam genutzt und in MVC- sowie Razor Pages-Apps verwendet werden.

Komponentenklassen

Komponenten werden mithilfe einer Kombination aus C# und HTML-Markup in Razor-Komponentendateien mit der Dateierweiterung .razor implementiert.

Razor-Syntax

Für Komponenten wird die Razor-Syntax verwendet. Von Komponenten werden zwei Razor-Features häufig verwendet: Anweisungen und Anweisungsattribute. Dies sind reservierte Schlüsselwörter, die im Razor-Markup mit dem Präfix @ angezeigt werden:

  • Anweisungen: Diese ändern, wie Komponentenmarkup analysiert oder ausgeführt wird. Beispielsweise gibt die @page-Anweisung eine routingfähige Komponente mit einer Routenvorlage an und kann direkt über die Anforderung eines Benutzers im Browser unter einer bestimmten URL erreicht werden.
  • Anweisungsattribute: Diese ändern, wie ein Komponentenelement analysiert oder ausgeführt wird. Beispielsweise bindet das @bind-Anweisungsattribut für ein <input>-Element Daten an den Wert des Elements.

In Komponenten verwendete Anweisungen und Anweisungsattribute werden in diesem Artikel und in anderen Artikeln der Blazor-Dokumentation näher erläutert. Allgemeine Informationen zur Razor-Syntax finden Sie in der Referenz zur Razor-Syntax für ASP.NET Core.

Namen

Der Name einer Komponente muss mit einem Großbuchstaben beginnen:

  • ProductDetail.razor ist gültig.
  • productDetail.razor ist ungültig.

Allgemeine Blazor-Namenskonventionen, die in der gesamten Blazor-Dokumentation verwendet werden:

  • Für Komponentendateipfade wird die Pascal-Schreibweise† verwendet. Sie werden vor den Komponentencodebeispielen angezeigt. Pfade geben typische Ordnerspeicherorte an. Beispielsweise gibt Pages/ProductDetail.razor an, dass der Dateiname der ProductDetail-Komponente ProductDetail.razor lautet und die Komponente im Ordner Pages der App enthalten ist.
  • In den URLs für Komponentendateipfade routingfähiger Komponenten werden die Wörter in der Routenvorlage einer Komponente durch Bindestriche anstelle von Leerzeichen getrennt. Beispielsweise wird die Komponente ProductDetail mit der Routenvorlage /product-detail (@page "/product-detail") in einem Browser an der relativen URL /product-detail angefordert.

† Bei der Pascal-Schreibweise (Camel-Case-Schreibweise mit großem Anfangsbuchstaben) werden keine Leerzeichen und Interpunktionszeichen verwendet, und der erste Buchstabe jedes Worts, einschließlich des ersten Worts, ist ein Großbuchstabe.

Routing

Das Routing in Blazor erfolgt, indem mit der @page-Anweisung eine Routenvorlage für jede zugängliche Komponente in der App bereitgestellt wird. Wenn eine Razor-Datei mit einer @page-Anweisung kompiliert wird, wird der generierten Klasse RouteAttribute hinzugefügt und so die Routenvorlage angegeben. Zur Laufzeit sucht der Router nach Komponentenklassen mit einem RouteAttribute und rendert die Komponente, deren Routenvorlage mit der angeforderten URL übereinstimmt.

Für die folgende HelloWorld-Komponente wird die Routenvorlage /hello-world verwendet. Die gerenderte Webseite für die Komponente wird über die relative URL /hello-world erreicht. Wenn Sie eine Blazor-App lokal mit dem Standardprotokoll, -host und -port ausführen, wird die HelloWorld-Komponente im Browser unter https://localhost:5001/hello-world angefordert. Komponenten, die Webseiten erzeugen, befinden sich in der Regel im Ordner Pages. Sie können Komponenten jedoch in beliebigen Ordnern speichern, einschließlich geschachtelter Ordner.

Pages/HelloWorld.razor:

@page "/hello-world"

<h1>Hello World!</h1>

Die vorangehende Komponente wird im Browser unter /hello-world geladen, unabhängig davon, ob Sie die Komponente der Benutzeroberflächennavigation der App hinzufügen. Optional können Komponenten zur NavMenu-Komponente hinzugefügt werden, sodass in der Benutzeroberflächennavigation der App ein Link zur Komponente angezeigt wird.

Bei der vorherigen HelloWorld-Komponente können Sie eine NavLink-Komponente zur NavMenu-Komponente im Shared-Ordner hinzufügen. Weitere Informationen, einschließlich Beschreibungen der Komponenten NavLink und NavMenu, finden Sie unter Routing und Navigation in Blazor in ASP.NET Core.

Markup

Die Benutzeroberfläche einer Komponente wird mithilfe der Razor-Syntax definiert, die aus Razor-Markup, C# und HTML besteht. Wenn eine App kompiliert wird, werden das HTML-Markup und die C#-Renderinglogik in eine Komponentenklasse konvertiert. Der Name der generierten Klasse entspricht dem Namen der Datei.

Member der Komponentenklasse werden in einem oder mehreren @code-Blöcken definiert. In @code-Blöcken wird der Komponentenzustand angegeben und mit C# verarbeitet:

  • Eigenschaften- und Feldinitialisierer
  • Parameterwerte aus Argumenten, die von übergeordneten Komponenten und Routenparametern übergeben werden
  • Methoden für die Behandlung von Benutzerereignissen, Lebenszyklusereignissen und benutzerdefinierter Komponentenlogik

Komponentenmember werden in der Renderinglogik mithilfe von C#-Ausdrücken verwendet, die mit dem Symbol @ beginnen. Beispielsweise wird ein C#-Feld gerendert, indem @ dem Feldnamen vorangestellt wird. Durch die unten gezeigte Markup-Komponente wird Folgendes ausgewertet und gerendert:

  • headingFontStyle für den CSS-Eigenschaftswert font-style des Überschriftenelements.
  • headingText für den Inhalt des Überschriftenelements.

Pages/Markup.razor:

@page "/markup"

<h1 style="font-style:@headingFontStyle">@headingText</h1>

@code {
    private string headingFontStyle = "italic";
    private string headingText = "Put on your new Blazor!";
}

Hinweis

In den Beispielen in der gesamten Blazor-Dokumentation wird für private Member der private-Zugriffsmodifizierer angegeben. Der Bereich privater Member ist auf die Klasse einer Komponente beschränkt. In C# wird jedoch der private-Zugriffsmodifizierer angenommen, wenn kein Zugriffsmodifizierer vorhanden ist. Daher ist das explizite Markieren von Membern als private im eigenen Code optional. Weitere Informationen zu Zugriffsmodifizierern finden Sie unter Zugriffsmodifizierer (C#-Programmierhandbuch).

Das Blazor-Framework verarbeitet eine Komponente intern als Renderstruktur. Dies ist die Kombination aus dem Dokumentobjektmodell (DOM) einer Komponente und dem Cascading Style Sheet Object Model (CSSOM). Nachdem die Komponente zum ersten Mal gerendert wurde, wird ihre Renderstruktur als Reaktion auf Ereignisse erneut generiert. Blazor vergleicht die neue Renderstruktur mit der vorherigen und wendet alle Änderungen auf das DOM des Browsers für die Anzeige an. Weitere Informationen finden Sie unter Rendering von Razor-Komponenten in ASP.NET Core.

Komponenten sind normale C#-Klassen und können an beliebiger Stelle innerhalb eines Projekts eingefügt werden. Komponenten, die Webseiten erzeugen, befinden sich in der Regel im Ordner Pages. Nicht für Seiten verwendete Komponenten werden häufig im Ordner Shared oder einem benutzerdefinierten Ordner gespeichert, der dem Projekt hinzugefügt wurde.

Laut Razor-Syntax werden für C#-Steuerelementstrukturen, Anweisungen und Anweisungsattribute Kleinbuchstaben verwendet (z. B. @if, @code, @bind). Eigenschaftennamen stehen in Großbuchstaben (z. B. @Body für LayoutComponentBase.Body).

Asynchrone Methoden (async) unterstützen nicht die Rückgabe von void.

Das Blazor-Framework verfolgt keine asynchronen Methoden (async) nach, die void zurückgeben. Folglich werden keine Ausnahmen abgefangen, wenn void zurückgegeben wird. Lassen Sie asynchrone Methoden stets Task zurückgeben.

Geschachtelte Komponenten

Komponenten können andere Komponenten enthalten, sofern Sie sie mithilfe der HTML-Syntax deklarieren. Das Markup für die Verwendung einer Komponente sieht wie ein HTML-Tag aus, wobei der Name des Tags der Typ der Komponente ist.

Betrachten Sie die folgende Heading-Komponente, die von anderen Komponenten verwendet werden kann, um eine Überschrift anzuzeigen.

Shared/Heading.razor:

<h1 style="font-style:@headingFontStyle">Heading Example</h1>

@code {
    private string headingFontStyle = "italic";
}

Das folgende Markup in der HeadingExample-Komponente rendert die vorherige Heading-Komponente an der Position, an der das <Heading />-Tag angezeigt wird.

Pages/HeadingExample.razor:

@page "/heading-example"

<Heading />

Wenn eine Komponente ein HTML-Element mit einem großgeschriebenen ersten Buchstaben enthält, der nicht mit einem Komponentennamen in demselben Namespace identisch ist, wird eine Warnung ausgegeben, dass das Element einen unerwarteten Namen aufweist. Durch das Hinzufügen einer @using-Anweisung für den Namespace der Komponente wird die Komponente zur Verfügung gestellt, wodurch die Warnung nicht mehr auftritt. Weitere Informationen finden Sie im Abschnitt Namespaces.

Das in diesem Abschnitt gezeigte Beispiel für die Heading-Komponente weist keine @page-Anweisung auf. Daher können Benutzer nicht über eine Anforderung im Browser direkt auf die Heading-Komponente zugreifen. Jede Komponente mit einer @page-Anweisung kann jedoch in einer anderen Komponente geschachtelt werden. Wenn auf die Heading-Komponente direkt zugegriffen werden kann, indem am Anfang der Razor-Datei @page "/heading" eingeschlossen wird, wird die Komponente für Browseranforderungen sowohl unter /heading als auch unter /heading-example gerendert.

Namespaces

In der Regel wird der Namespace einer Komponente aus dem Stammnamespace der App und dem Speicherort (Ordner) der Komponente in der App abgeleitet. Wenn der Stammnamespace der App BlazorSample lautet und sich die Counter Komponente im Ordner Pages befindet, gilt Folgendes:

  • der Namespace der Counter-Komponente BlazorSample.Pages, und
  • der vollqualifizierten Typname der Komponente ist BlazorSample.Pages.Counter.

Fügen Sie für benutzerdefinierte Ordner, die Komponenten enthalten, der übergeordneten Komponente oder der _Imports.razor-Datei der App eine @using-Anweisung hinzu. Das folgende Beispiel stellt Komponenten im Ordner Components zur Verfügung:

@using BlazorSample.Components

Hinweis

@using-Direktive in der _Imports.razor-Datei werden nur auf Razor-Dateien (.razor) angewendet und nicht auf C#-Dateien (.cs).

Auf Komponenten kann auch mit ihrem vollqualifizierten Namen verwiesen werden. Hierfür ist keine @using-Anweisung erforderlich. Im folgenden Beispiel wird direkt auf die ProductDetail-Komponente im Ordner Components der App verwiesen:

<BlazorSample.Components.ProductDetail />

Der Namespace einer mit Razor erstellten Komponente basiert auf Folgendem (nach Priorität):

  • Die @namespace-Anweisung im Markup der Razor-Datei (z. B. @namespace BlazorSample.CustomNamespace).
  • Der RootNamespace des Projekts in der Projektdatei (z. B. <RootNamespace>BlazorSample</RootNamespace>).
  • Der Projektname, der aus dem Namen der Projektdatei (.csproj) und dem Pfad vom Projektstamm zur Komponente resultiert. Das Framework löst beispielsweise für die Index-Komponente den Pfad {PROJECT ROOT}/Pages/Index.razor mit dem Projektnamespace BlazorSample (BlazorSample.csproj) in den Namespace BlazorSample.Pages auf. {PROJECT ROOT} ist der Projektstammpfad. Komponenten folgen den Namensbindungsregeln für C#. Für die Index-Komponente in diesem Beispiel werden folgende Komponenten berücksichtigt:
    • Im gleichen Ordner: Pages.
    • Komponenten im Stammverzeichnis des Projekts, die nicht explizit einen anderen Namespace angeben

Folgendes wird nicht unterstützt:

  • Die global::-Qualifizierung.
  • Das Importieren von Komponenten mit using-Aliasanweisungen. @using Foo = Bar wird beispielsweise nicht unterstützt.
  • Unvollständig qualifizierte Namen. Beispielsweise können Sie einer Komponente nicht @using BlazorSample hinzufügen und dann mit <Shared.NavMenu></Shared.NavMenu> auf die NavMenu-Komponente im Ordner Shared (Shared/NavMenu.razor) der App verweisen.

Unterstützung für partielle Klassen

Komponenten werden als partielle C#-Klassen generiert und mit einer der folgenden Vorgehensweisen erstellt:

  • Eine einzelne Datei enthält C#-Code, der in einem oder mehreren @code-Blöcken, HTML-Markup und Razor-Markup definiert ist. In Blazor-Projektvorlagen werden Komponenten mithilfe einer einzelnen Datei definiert.
  • HTML- und Razor-Markup werden in einer Razor-Datei (.razor) gespeichert. C#-Code wird in einer CodeBehind-Datei gespeichert, die als partielle Klasse (.cs) definiert ist.

Das folgende Beispiel zeigt die Counter-Standardkomponente mit einem @code-Block in einer App, die aus einer Blazor-Projektvorlage generiert wurde. Markup und C#-Code befinden sich in derselben Datei. Dies ist der häufigste Ansatz bei der Komponentenerstellung.

Pages/Counter.razor:

@page "/counter"

<h1>Counter</h1>

<p>Current count: @currentCount</p>

<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>

@code {
    private int currentCount = 0;

    private void IncrementCount()
    {
        currentCount++;
    }
}

In der folgenden Counter-Komponente wird das HTML- und Razor-Markup aus C#-Code unter Verwendung einer CodeBehind-Datei mit einer partiellen Klasse aufgeteilt:

Pages/CounterPartialClass.razor:

@page "/counter-partial-class"

<h1>Counter</h1>

<p>Current count: @currentCount</p>

<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>

Pages/CounterPartialClass.razor.cs:

namespace BlazorSample.Pages
{
    public partial class CounterPartialClass
    {
        private int currentCount = 0;

        void IncrementCount()
        {
            currentCount++;
        }
    }
}

@using-Direktive in der _Imports.razor-Datei werden nur auf Razor-Dateien (.razor) angewendet und nicht auf C#-Dateien (.cs). Fügen Sie einer partiellen Klassendatei ggf. Namespaces hinzu.

Typische Namespaces, die von Komponenten verwendet werden:

using System.Net.Http;
using Microsoft.AspNetCore.Components.Forms;
using Microsoft.AspNetCore.Components.Routing;
using Microsoft.AspNetCore.Components.Web;
using Microsoft.JSInterop;

Typische Namespaces enthalten auch den Namespace der App und den Namespace, der dem Ordner Shared der App entspricht:

using BlazorSample;
using BlazorSample.Shared;

Angeben einer Basisklasse

Die @inherits-Anweisung wird verwendet, um eine Basisklasse für eine Komponente anzugeben. Das folgende Beispiel zeigt, wie eine Komponente eine Basisklasse erben kann, um die Eigenschaften und Methoden der Komponente bereitzustellen. Die Basisklasse BlazorRocksBase wird von ComponentBase abgeleitet.

Pages/BlazorRocks.razor:

@page "/blazor-rocks"
@inherits BlazorRocksBase

<h1>@BlazorRocksText</h1>

BlazorRocksBase.cs:

using Microsoft.AspNetCore.Components;

namespace BlazorSample
{
    public class BlazorRocksBase : ComponentBase
    {
        public string BlazorRocksText { get; set; } =
            "Blazor rocks the browser!";
    }
}

Komponentenparameter

Komponentenparameter übergeben Daten an Komponenten und werden mit öffentlichen C#-Eigenschaften für die Komponentenklasse mit dem [Parameter]-Attribut definiert. Im folgenden Beispiel werden ein integrierter Verweistyp (System.String) und ein benutzerdefinierter Verweistyp (PanelBody) als Komponentenparameter übergeben.

PanelBody.cs:

public class PanelBody
{
    public string Text { get; set; }
    public string Style { get; set; }
}

Shared/ParameterChild.razor:

<div class="card w-25" style="margin-bottom:15px">
    <div class="card-header font-weight-bold">@Title</div>
    <div class="card-body" style="font-style:@Body.Style">
        @Body.Text
    </div>
</div>

@code {
    [Parameter]
    public string Title { get; set; } = "Set By Child";

    [Parameter]
    public PanelBody Body { get; set; } =
        new PanelBody()
        {
            Text = "Set by child.",
            Style = "normal"
        };
}

Warnung

Die Bereitstellung von Anfangswerten für Komponentenparameter wird unterstützt. Erstellen Sie jedoch keine Komponente, die in die eigenen Parameter schreibt, nachdem die Komponente zum ersten Mal gerendert wurde. Weitere Informationen finden Sie im Abschnitt Überschriebene Parameter dieses Artikels.

Die Komponentenparameter Title und Body der ParameterChild-Komponente werden durch Argumente im HTML-Tag festgelegt, das die Instanz der Komponente rendert. Die folgende ParameterParent-Komponente rendert zwei ParameterChild-Komponenten:

  • Die erste ParameterChild-Komponente wird ohne Angabe von Parameterargumenten gerendert.
  • Die zweite ParameterChild-Komponente empfängt Werte für Title und Body von der ParameterParent-Komponente, die mit einem expliziten C#-Ausdruck die Werte der Eigenschaften von PanelBody festlegt.

Pages/ParameterParent.razor:

@page "/parameter-parent"

<h1>Child component (without attribute values)</h1>

<ParameterChild />

<h1>Child component (with attribute values)</h1>

<ParameterChild Title="Set by Parent"
                Body="@(new PanelBody() { Text = "Set by parent.", Style = "italic" })" />

Das folgende gerenderte HTML-Markup der ParameterParent-Komponente enthält Standardwerte der ParameterChild-Komponente, wenn die ParameterParent-Komponente keine Komponentenparameterwerte angibt. Wenn die ParameterParent-Komponente Komponentenparameterwerte angibt, ersetzen diese die Standardwerte der ParameterChild-Komponente.

Hinweis

Im folgenden gerenderten HTML-Markup werden aus Gründen der Übersichtlichkeit keine gerenderten CSS-Formatklassen angezeigt.

<h1>Child component (without attribute values)</h1>

<div>
    <div>Set By Child</div>
    <div>Set by child.</div>
</div>

<h1>Child component (with attribute values)</h1>

<div>
    <div>Set by Parent</div>
    <div>Set by parent.</div>
</div>

Weisen Sie mit dem reservierten Symbol @ von Razor einem Komponentenparameter ein C#-Feld, eine C#-Eigenschaft oder ein Ergebnis einer C#-Methode als HTML-Attributwert zu. Die folgende ParameterParent2-Komponente zeigt vier Instanzen der vorangehenden ParameterChild-Komponente an und legt ihre Title-Parameterwerte folgendermaßen fest:

  • Der Wert des title-Felds.
  • Das Ergebnis der GetTitle-C#-Methode.
  • Das aktuelle lokale Datum im langen Format mit ToLongDateString unter Verwendung eines impliziten C#-Ausdrucks.
  • Die Title-Eigenschaft des panelData-Objekts.

Das Präfix @ ist für Zeichenfolgeparameter erforderlich. Andernfalls geht das Framework davon aus, dass ein Zeichenfolgenliteral festgelegt ist.

Außerhalb von Zeichenfolgen empfehlen wir die Verwendung des Präfixes @ für Nichtliterale, auch wenn sie nicht unbedingt erforderlich sind.

Wir empfehlen die Verwendung des Präfixes @ nicht für Literale (z. B. boolesche Werte), Schlüsselwörter (z. B. this) oder null, aber Sie können sie auf Wunsch verwenden. Zum Beispiel ist IsFixed="@true" ungewöhnlich, wird aber unterstützt.

Anführungszeichen um die Werte von Parameterattributen sind gemäß der HTML5-Spezifikation in den meisten Fällen optional. Beispielsweise wird Value=this anstelle von Value="this" unterstützt. Wir empfehlen jedoch die Verwendung von Anführungszeichen, da sie leichter zu merken und bei webbasierten Technologien weit verbreitet sind.

In der gesamten Dokumentation gibt für Codebeispiele Folgendes:

  • Es werden immer Anführungszeichen verwendet. Beispiel: Value="this".
  • Nichtliterale Zeichen verwenden stets das Präfix @, auch wenn es optional ist. Beispiele: Title="@title", wobei title eine Zeichenfolgenvariable ist. Count="@ct", wobei ct eine numerische Variable ist.
  • Literale außerhalb von Razor-Ausdrücken vermeiden stets @. Beispiel: IsFixed="true".

Pages/ParameterParent2.razor:

@page "/parameter-parent-2"

<ParameterChild Title="@title" />

<ParameterChild Title="@GetTitle()" />

<ParameterChild Title="@DateTime.Now.ToLongDateString()" />

<ParameterChild Title="@panelData.Title" />

@code {
    private string title = "From Parent field";
    private PanelData panelData = new PanelData();

    private string GetTitle()
    {
        return "From Parent method";
    }

    private class PanelData
    {
        public string Title { get; set; } = "From Parent object";
    }
}

Hinweis

Wenn Sie einem Komponentenparameter ein C#-Element zuweisen, stellen Sie dem Element das Symbol @ voran und nie das HTML-Attribut des Parameters.

Richtig:

<ParameterChild Title="@title" />

Falsch:

<ParameterChild @Title="title" />

Anders als bei Razor-Seiten (.cshtml) kann Blazor keine asynchronen Aufgaben in einem Razor-Ausdruck ausführen, während eine Komponente gerendert wird. Der Grund hierfür ist, dass Blazor für das Rendering interaktiver Benutzeroberflächen konzipiert ist. In einer interaktiven Benutzeroberfläche muss auf dem Bildschirm immer etwas angezeigt werden. Daher ist es nicht sinnvoll, den Renderingfluss zu blockieren. Stattdessen wird die asynchrone Arbeit während eines der asynchronen Lebenszyklusereignisse ausgeführt. Nach jedem asynchronen Lebenszyklusereignis kann die Komponente wieder gerendert werden. Folgende Razor-Syntax wird nicht unterstützt:

<ParameterChild Title="@await ..." />

Der Code im vorangehenden Beispiel generiert einen Compilerfehler, wenn die App erstellt wird:

Der 'await'-Operator kann nur innerhalb einer 'async'-Methode verwendet werden. Markieren Sie ggf. diese Methode mit dem 'async'-Modifizierer, und ändern Sie ihren Rückgabetyp in 'Task'.

Um einen Wert für den Parameter Title im vorangehenden Beispiel asynchron abzurufen, kann die Komponente das Lebenszyklusereignis OnInitializedAsync verwenden. Dies wird im folgenden Beispiel veranschaulicht:

<ParameterChild Title="@title" />

@code {
    private string title;

    protected override async Task OnInitializedAsync()
    {
        title = await ...;
    }
}

Weitere Informationen finden Sie unter Rendering von Razor-Komponenten in ASP.NET Core.

Die Verwendung eines expliziten Razor-Ausdrucks zur Verkettung von Text mit einem Ausdrucksergebnis für die Zuweisung zu einem Parameter wird nicht unterstützt. Im folgenden Beispiel wird versucht, den Text Set by mit dem Eigenschaftswert eines Objekts zu verketten. Obwohl diese Syntax in einer Razor-Seite (.cshtml) unterstützt wird, ist sie nicht für die Zuweisung zum Title-Parameter des untergeordneten Elements in einer Komponente gültig. Folgende Razor-Syntax wird nicht unterstützt:

<ParameterChild Title="Set by @(panelData.Title)" />

Der Code im vorangehenden Beispiel generiert einen Compilerfehler, wenn die App erstellt wird:

Komponentenattribute unterstützen keine komplexen Inhalte (C# und Markup gemischt).

Um die Zuweisung eines zusammengesetzten Werts zu unterstützen, verwenden Sie eine Methode, ein Feld oder eine Eigenschaft. Im folgenden Beispiel wird die Verkettung von Set by und dem Eigenschaftswert eines Objekts in der C#-Methode GetTitle ausgeführt:

Pages/ParameterParent3.razor:

@page "/parameter-parent-3"

<ParameterChild Title="@GetTitle()" />

@code {
    private PanelData panelData = new PanelData();

    private string GetTitle() => $"Set by {panelData.Title}";

    private class PanelData
    {
        public string Title { get; set; } = "Parent";
    }
}

Weitere Informationen finden Sie unter Razor-Syntaxreferenz für ASP.NET Core.

Warnung

Die Bereitstellung von Anfangswerten für Komponentenparameter wird unterstützt. Erstellen Sie jedoch keine Komponente, die in die eigenen Parameter schreibt, nachdem die Komponente zum ersten Mal gerendert wurde. Weitere Informationen finden Sie im Abschnitt Überschriebene Parameter dieses Artikels.

Komponentenparameter sollten als Auto-Eigenschaften deklariert werden, d. h., ihre get- oder set-Zugriffsmethode sollte keine benutzerdefinierte Logik enthalten. Beispielsweise ist die folgende StartData-Eigenschaft eine Auto-Eigenschaft:

[Parameter]
public DateTime StartData { get; set; }

Platzieren Sie die benutzerdefinierte Logik nicht in der get- oder set-Zugriffsmethode, da Komponentenparameter ausschließlich zur Verwendung als Kanal für eine übergeordnete Komponente vorgesehen sind, über den Informationen an eine untergeordnete Komponente fließen. Wenn eine set-Zugriffsmethode einer untergeordneten Komponenteneigenschaft Logik enthält, die das erneute Rendern der übergeordneten Komponente bewirkt, resultiert daraus eine Rendering-Endlosschleife.

So transformieren Sie einen empfangenen Parameterwert:

  • Belassen Sie die Parametereigenschaft als Auto-Eigenschaft, um die bereitgestellten Rohdaten darzustellen.
  • Erstellen Sie eine andere Eigenschaft oder Methode, um die transformierten Daten basierend auf der Parametereigenschaft bereitzustellen.

Überschreiben Sie OnParametersSetAsync, um einen empfangenen Parameter jedes Mal zu transformieren, wenn neue Daten empfangen werden.

Das Schreiben eines Anfangswerts in einen Komponentenparameter wird unterstützt, da anfängliche Wertzuweisungen das automatische Komponentenrendering von Blazor nicht beeinträchtigen. Die folgende Zuweisung der aktuellen lokalen DateTime mit DateTime.Now zu StartData ist eine gültige Syntax in einer Komponente:

[Parameter]
public DateTime StartData { get; set; } = DateTime.Now;

Weisen Sie nach der anfänglichen Zuweisung von DateTime.Now im Entwicklercode keinen Wert zu StartData zu. Weitere Informationen finden Sie im Abschnitt Überschriebene Parameter dieses Artikels.

Routenparameter

Komponenten können in der Routenvorlage der @page-Anweisung Routenparameter angeben. Der Blazor-Router verwendet Routenparameter, um entsprechende Komponentenparameter aufzufüllen.

Pages/RouteParameter.razor:

@page "/route-parameter"
@page "/route-parameter/{text}"

<h1>Blazor is @Text!</h1>

@code {
    [Parameter]
    public string Text { get; set; }

    protected override void OnInitialized()
    {
        Text = Text ?? "fantastic";
    }
}

Optionale Routenparameter werden nicht unterstützt. Deshalb werden im vorherigen Beispiel zwei @page-Anweisungen angewendet. Die erste @page-Anweisung ermöglicht die Navigation zur Komponente ohne einen Routenparameter. Die zweite @page-Anweisung empfängt den {text}-Routenparameter und weist den Wert der Text-Eigenschaft zu.

Informationen zu Catch-All-Routenparametern ({*pageRoute}), die Pfade über mehrere Ordnergrenzen hinweg erfassen, finden Sie unter Routing und Navigation in Blazor in ASP.NET Core .

Untergeordnete Inhaltsrenderfragmente

Komponenten können den Inhalt anderer Komponenten festlegen. Die zuweisende Komponente stellt den Inhalt zwischen dem öffnenden und schließenden Tag der untergeordneten Komponente bereit.

Im folgenden Beispiel enthält die RenderFragmentChild-Komponente einen Parameter der ChildContent-Komponenten, der ein als RenderFragment zu renderndes Segment der Benutzeroberfläche darstellt. In der endgültigen HTML-Ausgabe wird der Inhalt an der Position von ChildContent im Razor-Markup der Komponente gerendert.

Shared/RenderFragmentChild.razor:

<div class="card w-25" style="margin-bottom:15px">
    <div class="card-header font-weight-bold">Child content</div>
    <div class="card-body">@ChildContent</div>
</div>

@code {
    [Parameter]
    public RenderFragment ChildContent { get; set; }
}

Wichtig

Die Eigenschaft, die den Inhalt von RenderFragment empfängt, muss gemäß der Konvention ChildContent benannt werden.

Ereignisrückrufe werden für RenderFragment nicht unterstützt.

Die folgende RenderFragmentParent-Komponente stellt Inhalt für das Rendern des RenderFragmentChild durch Einfügen des Inhalts zwischen dem öffnenden und schließenden Tag der untergeordneten Komponente bereit.

Pages/RenderFragmentParent.razor:

@page "/render-fragment-parent"

<h1>Render child content</h1>

<RenderFragmentChild>
    Content of the child component is supplied
    by the parent component.
</RenderFragmentChild>

Aufgrund der Art und Weise, in der Blazor untergeordneten Inhalt rendert, erfordert das Rendern von Komponenten in einer for-Schleife eine lokale Indexvariable, wenn die inkrementierende Schleifenvariable im Inhalt der RenderFragmentChild-Komponente verwendet wird. Das folgende Beispiel kann der vorherigen RenderFragmentParent-Komponente hinzugefügt werden:

<h1>Three children with an index variable</h1>

@for (int c = 0; c < 3; c++)
{
    var current = c;

    <RenderFragmentChild>
        Count: @current
    </RenderFragmentChild>
}

Alternativ können Sie anstelle einer for-Schleife eine foreach-Schleife mit Enumerable.Range verwenden. Das folgende Beispiel kann der vorherigen RenderFragmentParent-Komponente hinzugefügt werden:

<h1>Second example of three children with an index variable</h1>

@foreach (var c in Enumerable.Range(0,3))
{
    <RenderFragmentChild>
        Count: @c
    </RenderFragmentChild>
}

Renderfragmente werden dazu verwendet, untergeordnete Inhalte in allen Blazor Apps zu rendern und werden in den folgenden Artikeln und Artikelabschnitten mit Beispielen beschrieben:

Hinweis

Die in das Blazor Framework integrierten Razor Komponenten verwenden die selbe ChildContent Komponentenparameterkonvention, um ihren Inhalt festzulegen. Sie können die Komponenten sehen, die untergeordnete Inhalte festlegen, indem Sie in der API Dokumentation nach dem Komponenten-Parametereigenschaftsnamen ChildContent suchen (filtert API mit dem Suchbegriff "ChildContent").

Rendern von Fragmenten für wiederverwendbare Renderinglogik

Sie können untergeordnete Komponenten ausschließlich als Möglichkeit zum Wiederverwenden von Renderinglogik ausklammern. Definieren Sie in jedem @code Block der Komponente ein RenderFragment und rendern Sie das Fragment von einem beliebigen Ort aus so oft wie nötig:

<h1>Hello, world!</h1>

@RenderWelcomeInfo

<p>Render the welcome info a second time:</p>

@RenderWelcomeInfo

@code {
    private RenderFragment RenderWelcomeInfo = __builder =>
    {
        <p>Welcome to your new app!</p>
    };
}

Weitere Informationen finden Sie unter Wiederverwenden von Renderinglogik.

Überschriebene Parameter

Das Blazor-Framework erzwingt im Allgemeinen eine sichere Parameterzuordnung von über- und untergeordneten Elementen:

  • Parameter werden nicht unerwartet überschrieben.
  • Nebenwirkungen werden auf ein Minimum reduziert. Beispielsweise werden zusätzliche Rendervorgänge vermieden, da Sie zu unendlichen Renderingschleifen führen können.

Eine untergeordnete Komponente empfängt neue Parameterwerte, die möglicherweise vorhandene Werte überschreiben, wenn die übergeordnete Komponente erneut gerendert wird. Das versehentliche Überschreiben von Parameterwerten in einer untergeordneten Komponente tritt häufig auf, wenn die Komponente mit mindestens einem datengebundenen Parameter entwickelt wird und der Entwickler direkt in einen Parameter im untergeordneten Element schreibt:

  • Die untergeordnete Komponente wird mit mindestens einem Parameterwert aus der übergeordneten Komponente gerendert.
  • Das untergeordnete Element schreibt direkt in den Wert eines Parameters.
  • Die übergeordnete Komponente wird erneut gerendert und überschreibt den Wert des Parameters des untergeordneten Elements.

Die Möglichkeit, Parameterwerte zu überschreiben, gilt auch für set-Eigenschaftenzugriffsmethoden der untergeordneten Komponente.

Wichtig

Grundsätzlich wird empfohlen, keine Komponenten zu erstellen, die direkt in die eigenen Parameter schreiben, nachdem die Komponente zum ersten Mal gerendert wurde.

Angenommen, die folgende Expander-Komponente:

  • Rendert untergeordneten Inhalt.
  • Schaltet die Anzeige von untergeordnetem Inhalt mit einem Komponentenparameter (Expanded) um.

Nachdem die folgende Expander-Komponente einen überschriebenen Parameter gezeigt hat, wird eine modifizierte Expander-Komponente gezeigt, um den richtigen Ansatz für dieses Szenario zu veranschaulichen. Die folgenden Beispiele können in eine lokale Beispiel-App eingefügt werden, um die beschriebenen Verhaltensweisen nachzuvollziehen.

Shared/Expander.razor:

<div @onclick="Toggle" class="card bg-light mb-3" style="width:30rem">
    <div class="card-body">
        <h2 class="card-title">Toggle (<code>Expanded</code> = @Expanded)</h2>

        @if (Expanded)
        {
            <p class="card-text">@ChildContent</p>
        }
    </div>
</div>

@code {
    [Parameter]
    public bool Expanded { private get; set; }

    [Parameter]
    public RenderFragment ChildContent { get; set; }

    private void Toggle()
    {
        Expanded = !Expanded;
    }
}

Die Expander-Komponente wird der folgenden übergeordneten ExpanderExample-Komponente hinzugefügt, die StateHasChanged aufrufen kann:

Pages/ExpanderExample.razor:

@page "/expander-example"

<Expander Expanded="true">
    Expander 1 content
</Expander>

<Expander Expanded="true" />

<button @onclick="StateHasChanged">
    Call StateHasChanged
</button>

Anfänglich verhalten sich die Expander-Komponenten unabhängig, wenn ihre Expanded-Eigenschaften umgeschaltet werden. Die untergeordneten Komponenten behalten ihre Zustände erwartungsgemäß bei.

Wenn StateHasChanged in einer übergeordneten Komponente aufgerufen wird, rendert das Blazor-Framework untergeordnete Komponenten neu, wenn sich ihre Parameter geändert haben könnten:

  • Bei einer Gruppe von Parametertypen, die von Blazor explizit überprüft werden, rendert Blazor eine untergeordnete Komponente neu, wenn erkannt wird, dass sich Parameter geändert haben.
  • Bei nicht überprüften Parametertypen wird die untergeordnete Blazor-Komponente neu gerendert – unabhängig davon, ob sich die Parameter geändert haben oder nicht. Untergeordneter Inhalt fällt in diese Kategorie von Parametertypen, weil untergeordneter Inhalt dem Typ RenderFragment entspricht, einem Delegat, der auf andere stummschaltbare Objekte verweist.

Für die ExpanderExample-Komponente:

  • Die erste Expander-Komponente legt untergeordneten Inhalt in einem potenziell stummschaltbaren RenderFragment fest, sodass ein Aufruf von StateHasChanged in der übergeordneten Komponente die Komponente automatisch neu rendert und möglicherweise den Wert von Expanded mit dem Anfangswert true überschreibt.
  • Die zweite Expander-Komponente legt keinen untergeordneten Inhalt fest. Daher gibt es keinen potenziell stummschaltbaren RenderFragment. Durch einen Aufruf von StateHasChanged in der übergeordneten Komponente wird die untergeordnete Komponente nicht automatisch neu gerendert, sodass der Expanded-Wert der Komponente nicht überschrieben wird.

Um den Zustand im vorangehenden Szenario beizubehalten, verwenden Sie ein privates Feld in der Expander-Komponente, um dessen Umschaltungszustand beizubehalten.

Die folgende überarbeitete Expander-Komponente:

  • akzeptiert den Wert des Expanded-Komponentenparameters aus der übergeordneten Komponente.
  • weist den Wert des Komponentenparameters einem privaten Feld (expanded) im OnInitialized-Ereignis zu.
  • Verwendet das private Feld, um den internen Umschaltzustand zu verwalten. Dies veranschaulicht, wie das direkte Schreiben in einen Parameter vermieden wird.

Hinweis

Die Hinweise in diesem Abschnitt gelten auch für ähnliche Logik in den set-Zugriffsmethoden für Komponentenparameter, die zu ähnlichen unerwünschten Nebenwirkungen führen kann.

Shared/Expander.razor:

<div @onclick="Toggle" class="card bg-light mb-3" style="width:30rem">
    <div class="card-body">
        <h2 class="card-title">Toggle (<code>expanded</code> = @expanded)</h2>

        @if (expanded)
        {
            <p class="card-text">@ChildContent</p>
        }
    </div>
</div>

@code {
    private bool expanded;

    [Parameter]
    public bool Expanded { private get; set; }

    [Parameter]
    public RenderFragment ChildContent { get; set; }

    protected override void OnInitialized()
    {
        expanded = Expanded;
    }

    private void Toggle()
    {
        expanded = !expanded;
    }
}

Weitere Informationen finden Sie unter Bidirektionaler Blazor-Bindungsfehler (dotnet/aspnetcore #24599).

Weitere Informationen zur Änderungserkennung, einschließlich Informationen zu den genauen Typen, die von Blazor überprüft werden, finden Sie unter Razor-Komponentenrendering unter ASP.NET Core.

Attributsplatting und arbiträre Parameter

Komponenten können zusätzlich zu den deklarierten Parametern der Komponente weitere Attribute erfassen und rendern. Zusätzliche Attribute können in einem Wörterbuch erfasst und dann für ein Element gesplattet werden, wenn die Komponente mithilfe des Razor-Anweisungsattributs @attributes gerendert wird. Diese Option ist nützlich, um eine Komponente zu definieren, die ein Markupelement erzeugt, das viele verschiedene Anpassungen unterstützt. Beispielsweise kann es mühsam sein, Attribute für ein <input>-Element separat zu definieren, das viele Parameter unterstützt.

In der folgenden Splat-Komponente:

  • Das erste <input>-Element (id="useIndividualParams") verwendet einzelne Komponentenparameter.
  • Das zweite <input>-Element (id="useAttributesDict") verwendet Attributsplatting.

Pages/Splat.razor:

@page "/splat"

<input id="useIndividualParams"
       maxlength="@maxlength"
       placeholder="@placeholder"
       required="@required"
       size="@size" />

<input id="useAttributesDict"
       @attributes="InputAttributes" />

@code {
    private string maxlength = "10";
    private string placeholder = "Input placeholder text";
    private string required = "required";
    private string size = "50";

    private Dictionary<string, object> InputAttributes { get; set; } =
        new Dictionary<string, object>()
        {
            { "maxlength", "10" },
            { "placeholder", "Input placeholder text" },
            { "required", "required" },
            { "size", "50" }
        };
}

Die gerenderten <input>-Elemente auf der Webseite sind identisch:

<input id="useIndividualParams"
       maxlength="10"
       placeholder="Input placeholder text"
       required="required"
       size="50">

<input id="useAttributesDict"
       maxlength="10"
       placeholder="Input placeholder text"
       required="required"
       size="50">

Definieren Sie einen Komponentenparameter, bei dem die CaptureUnmatchedValues-Eigenschaft auf true festgelegt ist, damit beliebige Attribute akzeptiert werden:

@code {
    [Parameter(CaptureUnmatchedValues = true)]
    public Dictionary<string, object> InputAttributes { get; set; }
}

Wenn die CaptureUnmatchedValues-Eigenschaft auf [Parameter] festgelegt ist, kann der Parameter alle Attribute abgleichen, die keinem anderen Parameter entsprechen. Eine Komponente kann nur einen einzelnen Parameter mit CaptureUnmatchedValues definieren. Der mit CaptureUnmatchedValues verwendete Eigenschaftentyp muss von Dictionary<string, object> mit Zeichenfolgenschlüsseln zugewiesen werden können. Die Verwendung von IEnumerable<KeyValuePair<string, object>> oder IReadOnlyDictionary<string, object> ist in diesem Szenario ebenfalls möglich.

Die Position von @attributes relativ zur Position der Elementattribute ist wichtig. Wenn @attributes-Anweisungen für das Element gesplattet werden, werden die Attribute von rechts nach links (letztes bis erstes) verarbeitet. Sehen Sie sich das folgende Beispiel für eine übergeordnete Komponente an, die eine untergeordnete Komponente verwendet:

Shared/AttributeOrderChild1.razor:

<div @attributes="AdditionalAttributes" extra="5" />

@code {
    [Parameter(CaptureUnmatchedValues = true)]
    public IDictionary<string, object> AdditionalAttributes { get; set; }
}

Pages/AttributeOrderParent1.razor:

@page "/attribute-order-parent-1"

<AttributeOrderChild1 extra="10" />

Das extra-Attribut der AttributeOrderChild1-Komponente ist rechts von @attributes festgelegt. Das gerenderte <div>-Element der AttributeOrderParent1-Komponente enthält extra="5", wenn dieses über das zusätzliche Attribut weitergeleitet wird, da die Attribute von rechts nach links (letztes bis erstes) verarbeitet werden:

<div extra="5" />

Im folgenden Beispiel wird die Reihenfolge von extra und @attributes im <div>-Element der untergeordneten Komponente umgekehrt:

Shared/AttributeOrderChild2.razor:

<div extra="5" @attributes="AdditionalAttributes" />

@code {
    [Parameter(CaptureUnmatchedValues = true)]
    public IDictionary<string, object> AdditionalAttributes { get; set; }
}

Pages/AttributeOrderParent2.razor:

@page "/attribute-order-parent-2"

<AttributeOrderChild2 extra="10" />

Das <div>-Element auf der gerenderten Webseite der übergeordneten Komponente enthält extra="10", wenn es über das zusätzliche Attribut übergeben wird:

<div extra="10" />

Erfassen von Verweisen auf Komponenten

Komponentenverweise bieten eine Möglichkeit, auf eine Komponenteninstanz zum Ausgeben von Befehlen zu verweisen. So erfassen Sie einen Komponentenverweis:

  • Fügen Sie der untergeordneten Komponente ein @ref-Attribut hinzu.
  • Definieren Sie ein Feld mit demselben Typ wie die untergeordnete Komponente.

Wenn die Komponente gerendert wird, wird das Feld mit der Komponenteninstanz aufgefüllt. Anschließend können Sie .NET-Methoden für die Instanz aufrufen.

Betrachten Sie die folgende ReferenceChild-Komponente, die eine Meldung protokolliert, wenn die ChildMethod aufgerufen wird.

Shared/ReferenceChild.razor:

@using Microsoft.Extensions.Logging
@inject ILogger<ReferenceChild> logger

@code {
    public void ChildMethod(int value)
    {
        logger.LogInformation("Received {Value} in ChildMethod", value);
    }
}

Ein Komponentenverweis wird erst aufgefüllt, nachdem die Komponente gerendert wurde und die Ausgabe das Element von ReferenceChild enthält. Vor dem Rendern der Komponente gibt es nichts, auf das verwiesen werden kann.

Sie können Komponentenverweise bearbeiten, nachdem die Komponente das Rendering abgeschlossen hat, indem Sie die OnAfterRender- oder die OnAfterRenderAsync-Methode verwenden.

Um eine Verweisvariable mit einem Ereignishandler zu verwenden, verwenden Sie einen Lambdaausdruck oder weisen einen Ereignishandlerdelegaten in den Methoden OnAfterRender oder OnAfterRenderAsync zu. So ist sichergestellt, dass die Verweisvariable vor dem Ereignishandler zugewiesen wird.

Im folgenden Ansatz mit einem Lambdaausdruck wird die vorherige ReferenceChild-Komponente verwendet.

Pages/ReferenceParent1.razor:

@page "/reference-parent-1"

<button @onclick="@(() => childComponent.ChildMethod(5))">
    Call <code>ReferenceChild.ChildMethod</code> with an argument of 5
</button>

<ReferenceChild @ref="childComponent" />

@code {
    private ReferenceChild childComponent;
}

Im folgenden Ansatz mit einem Delegaten wird die vorherige ReferenceChild-Komponente verwendet.

Pages/ReferenceParent2.razor:

@page "/reference-parent-2"

<button @onclick="callChildMethod">
    Call <code>ReferenceChild.ChildMethod</code> with an argument of 5
</button>

<ReferenceChild @ref="childComponent" />

@code {
    private ReferenceChild childComponent;
    private Action callChildMethod;

    protected override void OnAfterRender(bool firstRender)
    {
        if (firstRender)
        {
            callChildMethod = CallChildMethod;
        }
    }

    private void CallChildMethod()
    {
        childComponent.ChildMethod(5);
    }
}

Beim Erfassen von Komponentenverweisen wird zwar eine ähnliche Syntax verwendet, um Elementverweise zu erfassen, das Erfassen von Komponentenverweisen ist jedoch kein JavaScript-Interop-Feature. Komponentenverweise werden nicht an JavaScript-Code übermittelt. Komponentenverweise werden nur in .NET-Code verwendet.

Wichtig

Verwenden Sie keine Komponentenverweise verwenden, um den Zustand von untergeordneten Komponenten zu verändern. Verwenden Sie stattdessen normale deklarative Komponentenparameter, um Daten an untergeordnete Komponenten zu übergeben. Die Verwendung von Komponentenparametern bewirkt, dass untergeordnete Komponenten automatisch zu den richtigen Zeitpunkten erneut gerendert werden. Weitere Informationen finden Sie im Abschnitt Komponentenparameter und im Artikel Datenbindung in Blazor in ASP.NET Core.

Synchronisierungskontext

Blazor verwendet einen Synchronisierungskontext (SynchronizationContext), um einen einzelnen logischen Ausführungsthread zu erzwingen. Die Lebenszyklusmethoden einer Komponente und Ereignisrückrufe, die von Blazor ausgelöst werden, werden in diesem Synchronisierungskontext ausgeführt.

Der Synchronisierungskontext des Blazor Server versucht, eine Singlethreadumgebung zu emulieren, sodass er genau mit dem WebAssembly-Modell im Browser übereinstimmt, das ein Singlethreadmodell ist. Zu jedem Zeitpunkt wird die Arbeit für genau einen Thread ausgeführt, woraus der Eindruck eines einzelnen logischen Threads entsteht. Zwei Vorgänge werden nicht gleichzeitig ausgeführt.

Vermeiden Sie eine Threadblockierung von Aufrufen.

Rufen Sie generell in Komponenten nicht die folgenden Methoden auf. Die folgenden Methoden blockieren die Threadausführung und hindern somit die App an der Wiederaufnahme der Arbeit, bis der zugrunde liegende Task beendet ist:

Hinweis

In den Beispielen in der Blazor-Dokumentation werden die in diesem Abschnitt erwähnten Threadblockierungsmethoden nur zu Demonstrationszwecken und nicht als Codierungsempfehlung verwendet. Beispielsweise simulieren einige Komponentencodedemonstrationen einen Prozess mit langer Ausführungsdauer, indem Thread.Sleep aufgerufen wird.

Externes Aufrufen von Komponentenmethoden zur Aktualisierung des Status

Wenn eine Komponente aufgrund eines externen Ereignisses (z. B. eines Timers oder anderer Benachrichtigungen) aktualisiert werden muss, verwenden Sie die InvokeAsync-Methode, mit der die Codeausführung an den Synchronisierungskontext von Blazor weitergeleitet wird. Betrachten Sie beispielsweise den folgenden Benachrichtigungsdienst, der jede lauschende Komponente über den aktualisierten Zustand benachrichtigen kann. Die Update-Methode kann überall in der App aufgerufen werden.

TimerService.cs:

using System;
using System.Timers;
using Microsoft.Extensions.Logging;

public class TimerService : IDisposable
{
    private int elapsedCount;
    private readonly ILogger<TimerService> logger;
    private readonly NotifierService notifier;
    private Timer timer;

    public TimerService(NotifierService notifier, ILogger<TimerService> logger)
    {
        this.notifier = notifier;
        this.logger = logger;
    }

    public void Start()
    {
        if (timer is null)
        {
            timer = new();
            timer.AutoReset = true;
            timer.Interval = 10000;
            timer.Elapsed += HandleTimer;
            timer.Enabled = true;
            logger.LogInformation("Started");
        }
    }

    private async void HandleTimer(object source, ElapsedEventArgs e)
    {
        elapsedCount += 1;
        await notifier.Update("elapsedCount", elapsedCount);
        logger.LogInformation($"elapsedCount: {elapsedCount}");
    }

    public void Dispose()
    {
        timer?.Dispose();
    }
}

NotifierService.cs:

using System;
using System.Threading.Tasks;

public class NotifierService
{
    public async Task Update(string key, int value)
    {
        if (Notify != null)
        {
            await Notify.Invoke(key, value);
        }
    }

    public event Func<string, int, Task> Notify;
}

Registrieren des Diensts:

  • Registrieren Sie in einer Blazor WebAssembly-App die Dienste als Singleton in Program.cs:

    builder.Services.AddSingleton<NotifierService>();
    builder.Services.AddSingleton<TimerService>();
    
  • Registrieren Sie in einer Blazor Server-App die Dienste bereichsbezogen in Startup.ConfigureServices:

    services.AddScoped<NotifierService>();
    services.AddScoped<TimerService>();
    

Verwenden Sie NotifierService, um eine Komponente zu aktualisieren.

Pages/ReceiveNotifications.razor:

@page "/receive-notifications"
@implements IDisposable
@inject NotifierService Notifier
@inject TimerService Timer

<h1>Receive Notifications</h1>

<h2>Timer Service</h2>

<button @onclick="StartTimer">Start Timer</button>

<h2>Notifications</h2>

<p>
    Status:
    @if (lastNotification.key is not null)
    {
        <span>@lastNotification.key = @lastNotification.value</span>
    }
    else
    {
        <span>Awaiting first notification</span>
    }
</p>

@code {
    private (string key, int value) lastNotification;

    protected override void OnInitialized()
    {
        Notifier.Notify += OnNotify;
    }

    public async Task OnNotify(string key, int value)
    {
        await InvokeAsync(() =>
        {
            lastNotification = (key, value);
            StateHasChanged();
        });
    }

    private void StartTimer()
    {
        Timer.Start();
    }

    public void Dispose()
    {
        Notifier.Notify -= OnNotify;
    }
}

Im vorherigen Beispiel:

  • NotifierService ruft die OnNotify-Methode der Komponente außerhalb des Synchronisierungskontexts von Blazor auf. InvokeAsync wird verwendet, um zum richtigen Kontext zu wechseln und ein Rendering in die Warteschlange zu stellen. Weitere Informationen finden Sie unter Rendering von Razor-Komponenten in ASP.NET Core.
  • Die Komponente implementiert IDisposable. Das Abonnement des OnNotify-Delegaten wird in der Dispose-Methode gekündigt. Diese wird vom Framework aufgerufen, wenn die Komponente verworfen wird. Weitere Informationen finden Sie unter Rendering von Razor-Komponenten in ASP.NET Core.

Verwenden von @key zur Steuerung der Beibehaltung von Elementen und Komponenten

Wenn Sie eine Element- oder Komponentenliste rendern und die Elemente oder Komponenten nachfolgend geändert werden, muss Blazor bestimmen, welche der vorherigen Elemente oder Komponenten beibehalten werden können und wie Modellobjekte diesen zugeordnet werden sollen. Normalerweise erfolgt dieser Prozess automatisch und kann ignoriert werden, aber in einigen Fällen sollten Sie den Prozess steuern.

Betrachten Sie die folgenden Komponenten Details und People:

  • Die Details-Komponente empfängt Daten (Data) von der übergeordneten People-Komponente, die in einem <input>-Element angezeigt wird. Jedes angezeigte <input>-Element kann den Fokus der Seite vom Benutzer erhalten, wenn er eines der <input>-Elemente auswählt.
  • Die People-Komponente erstellt mithilfe der Details-Komponente eine Liste anzuzeigender person-Objekte. Alle drei Sekunden wird der Sammlung eine neue Person hinzugefügt.

Diese Demonstration ermöglicht Ihnen Folgendes:

  • Auswählen eines <input> aus mehreren gerenderten Details-Komponenten
  • Untersuchen des Verhaltens des Fokus der Seite, wenn die Größe der people-Sammlung automatisch zunimmt

Shared/Details.razor:

<input value="@Data" />

@code {
    [Parameter]
    public string Data { get; set; }
}

In der folgenden People-Komponente führt jede Iteration für das Hinzufügen einer Person in OnTimerCallback dazu, dass Blazor die gesamte Sammlung neu erstellt. Der Fokus der Seite bleibt auf derselben Indexposition der <input>-Elemente, sodass sich der Fokus jedes Mal verschiebt, wenn eine Person hinzugefügt wird. Das Abweichen des Fokus von der Auswahl durch den Benutzer ist kein wünschenswertes Verhalten. Nachdem mit der folgenden Komponente das unzureichende Verhalten veranschaulicht wurde, wird das @key-Anweisungsattribut verwendet, um die Benutzerfreundlichkeit zu verbessern.

Pages/People.razor:

@page "/people"
@using System.Timers
@implements IDisposable

@foreach (var person in people)
{
    <Details Data="@person.Data" />
}

@code {
    private Timer timer = new Timer(3000);

    public List<Person> people =
        new List<Person>()
        {
            { new Person { Data = "Person 1" } },
            { new Person { Data = "Person 2" } },
            { new Person { Data = "Person 3" } }
        };

    protected override void OnInitialized()
    {
        timer.Elapsed += (sender, eventArgs) => OnTimerCallback();
        timer.Start();
    }

    private void OnTimerCallback()
    {
        _ = InvokeAsync(() =>
        {
            people.Insert(0,
                new Person
                {
                    Data = $"INSERTED {DateTime.Now.ToString("hh:mm:ss tt")}"
                });
            StateHasChanged();
        });
    }

    public void Dispose() => timer.Dispose();

    public class Person
    {
        public string Data { get; set; }
    }
}

Der Inhalt der people-Sammlung ändert sich durch eingefügte, gelöschte oder neu sortierte Einträge. Erneutes Rendering kann zu sichtbaren Verhaltensunterschieden führen. Jedes Mal, wenn eine Person in die people-Sammlung eingefügt wird, erhält das Element den Fokus, das dem Element mit dem aktuellen Fokus vorangestellt ist. Der Fokus des Benutzers geht verloren.

Das Zuordnen von Elementen oder Komponenten zu einer Sammlung kann mit dem @key-Anweisungsattribut gesteuert werden. Die Verwendung von @key garantiert, dass Elemente oder Komponenten basierend auf dem Wert von @key beibehalten werden. Wenn in der Details-Komponente im vorherigen Beispiel @key auf person festgelegt ist, erfolgt in Blazor kein erneutes Rendering von Details-Komponenten, die sich nicht geändert haben.

Um für die People-Komponente das @key-Anweisungsattribut mit der people-Sammlung zu verwenden, aktualisieren Sie das <Details>-Element wie folgt:

<Details @key="person" Data="@person.Data" />

Wenn sich die people-Sammlung ändert, bleibt die Zuordnung zwischen Details-Instanzen und person-Instanzen erhalten. Wenn am Anfang der Sammlung eine Person eingefügt wird, wird an der entsprechenden Position eine neue Details-Instanz eingefügt. Andere Instanzen bleiben unverändert. Daher geht der Fokus des Benutzers nicht verloren, wenn der Sammlung Personen hinzugefügt werden.

Andere Sammlungsaktualisierungen weisen das gleiche Verhalten auf, wenn das @key-Anweisungsattribut verwendet wird:

  • Wenn eine Instanz aus der Sammlung gelöscht wird, wird nur die entsprechende Komponenteninstanz von der Benutzeroberfläche entfernt. Andere Instanzen bleiben unverändert.
  • Wenn Sammmlungseinträge neu sortiert werden, werden die entsprechenden Komponenteninstanzen beibehalten und auf der Benutzeroberfläche neu angeordnet.

Wichtig

Schlüssel sind für jedes Containerelement oder jede Komponente lokal. Schlüssel werden nicht dokumentübergreifend global verglichen.

Empfohlene Verwendung von @key

In der Regel ist es sinnvoll, @key zu verwenden, wenn eine Liste gerendert wird (z. B. in einem foreach-Block) und ein geeigneter Wert vorhanden ist, um @key zu definieren.

Sie können @key auch verwenden, um eine Element- oder Komponentenunterstruktur zu bewahren, wenn sich ein Objekt nicht ändert, wie in den folgenden Beispielen gezeigt.

Beispiel 1:

<li @key="person">
    <input value="@person.Data" />
</li>

Beispiel 2:

<div @key="person">
    @* other HTML elements *@
</div>

Wenn sich eine person-Instanz ändert, erzwingt das @key-Anweisungsattribut in Blazor Folgendes:

  • Verwerfen des gesamten <li>- oder <div>-Elements und seiner Nachfolger
  • Neuerstellen der Unterstruktur innerhalb der Benutzeroberfläche mit neuen Elementen und Komponenten

Dies ist hilfreich, um zu gewährleisten, dass kein Benutzeroberflächenzustand beibehalten wird, wenn sich die Sammlung innerhalb einer Unterstruktur ändert.

Bereich von @key

Die @key-Attributanweisung ist auf ihre eigenen gleichgeordneten Elemente innerhalb des übergeordneten Elements begrenzt.

Betrachten Sie das folgende Beispiel. Die Schlüssel first und second werden im selben Bereich des <div>-Elements miteinander verglichen:

<div>
    <div @key="first">...</div>
    <div @key="second">...</div>
</div>

Im folgenden Beispiel werden die Schlüssel first und second in ihren eigenen Bereichen veranschaulicht, die unabhängig voneinander stehen und keinen Einfluss aufeinander haben. Jeder @key-Bereich gilt nur für sein übergeordnetes <div>-Element, nicht für die übergeordneten <div>-Elemente:

<div>
    <div @key="first">...</div>
</div>
<div>
    <div @key="second">...</div>
</div>

Für die zuvor gezeigte Details-Komponente rendern die folgenden Beispiele person-Daten innerhalb desselben @key-Bereichs und veranschaulichen typische Anwendungsfälle für @key:

<div>
    @foreach (var person in people)
    {
        <Details @key="person" Data="@person.Data" />
    }
</div>
@foreach (var person in people)
{
    <div @key="person">
        <Details Data="@person.Data" />
    </div>
}
<ol>
    @foreach (var person in people)
    {
        <li @key="person">
            <Details Data="@person.Data" />
        </li>
    }
</ol>

Die folgenden Beispiele beschränken sich nur auf den Bereich @key für das <div>- oder <li>-Element, das jede Details-Komponenteninstanz umschließt. Daher werden person-Daten für jedes Member der people-Sammlung nicht für jede person-Instanz für die gerenderten Details-Komponenten schlüsselgebunden. Vermeiden Sie die folgenden Muster bei Verwendung von @key:

@foreach (var person in people)
{
    <div>
        <Details @key="person" Data="@person.Data" />
    </div>
}
<ol>
    @foreach (var person in people)
    {
        <li>
            <Details @key="person" Data="@person.Data" />
        </li>
    }
</ol>

Ungeeignete Fälle für @key

Beim Rendern mit @key wird die Leistung beeinträchtigt. Die Beeinträchtigung der Leistung ist nicht erheblich. Sie sollten @key jedoch nur angeben, wenn sich die Beibehaltung des Elements oder der Komponente positiv auf die App auswirken.

Auch wenn @key nicht verwendet wird, behält Blazor die untergeordneten Element- und Komponenteninstanzen so weit wie möglich bei. Der einzige Vorteil bei der Verwendung von @key besteht in der Kontrolle darüber, wie Modellinstanzen den beibehaltenen Komponenteninstanzen zugeordnet werden, anstatt die Zuordnung durch Blazor bestimmen zu lassen.

Zu verwendende Werte für @key

Im Allgemeinen ist es sinnvoll, für @key folgende Arten von Werten bereitzustellen:

  • Modellobjektinstanzen. Beispielsweise wurde im vorherigen Beispiel die Person-Instanz (person) verwendet. Dadurch wird die Beibehaltung basierend auf der Objektverweisgleichheit sichergestellt.
  • Eindeutige Bezeichner. Eindeutige Bezeichner können beispielsweise auf Primärschlüsselwerten vom Typ int, string oder Guid basieren.

Stellen Sie sicher, dass die für @key verwendeten Werte nicht kollidieren. Wenn innerhalb desselben übergeordneten Elements kollidierende Werte erkannt werden, löst Blazor eine Ausnahme aus, da alte Elemente oder Komponenten nicht deterministisch neuen Elementen oder Komponenten zugeordnet werden können. Verwenden Sie nur eindeutige Werte wie Objektinstanzen oder Primärschlüsselwerte.

Hinzufügen eines Attributs

Attribute können in Komponenten mit der @attribute-Anweisung hinzugefügt werden. Im folgenden Beispiel wird das [Authorize]-Attribut auf die Klasse der Komponente angewendet:

@page "/"
@attribute [Authorize]

Attribute für bedingte HTML-Elemente

HTML-Elementattributeigenschaften werden basierend auf dem .NET-Wert bedingt festgelegt. Wenn der Wert false oder null ist, wird die Eigenschaft nicht festgelegt. Wenn der Wert true ist, wird die Eigenschaft festgelegt.

Im folgenden Beispiel bestimmt IsCompleted, ob die checked-Eigenschaft des <input>-Elements festgelegt wird.

Pages/ConditionalAttribute.razor:

@page "/conditional-attribute"

<label>
    <input type="checkbox" checked="@IsCompleted" />
    Is Completed?
</label>

<button @onclick="@(() => IsCompleted = !IsCompleted)">
    Change IsCompleted
</button>

@code {
    [Parameter]
    public bool IsCompleted { get; set; }
}

Weitere Informationen finden Sie unter Razor-Syntaxreferenz für ASP.NET Core.

Warnung

Einige HTML-Attribute wie aria-pressed funktionieren nicht ordnungsgemäß, wenn der .NET-Typ bool ist. Verwenden Sie in diesen Fällen statt bool einen string-Typ.

Unformatierter HTML-Code

Zeichenfolgen werden normalerweise mithilfe von DOM-Textknoten gerendert. Das bedeutet, dass das darin enthaltene Markup vollständig ignoriert und als Literaltext behandelt wird. Sie können unformatierten HTML-Code rendern, indem Sie den HTML-Inhalt mit einem MarkupString-Wert umschließen. Der Wert wird als HTML oder SVG analysiert und in das DOM eingefügt.

Warnung

Das Rendern von unformatiertem HTML-Code, der aus einer nicht vertrauenswürdigen Quelle stammt, gilt als Sicherheitsrisiko und sollte immer vermieden werden.

Im folgenden Beispiel wird veranschaulicht, wie der MarkupString-Typ verwendet wird, um der gerenderten Ausgabe einer Komponente einen Block mit statischem HTML-Inhalt hinzuzufügen.

Pages/MarkupStringExample.razor:

@page "/markup-string-example"

@((MarkupString)myMarkup)

@code {
    private string myMarkup =
        "<p class=\"text-danger\">This is a dangerous <em>markup string</em>.</p>";
}

Razor-Vorlagen

Renderfragmente können mithilfe der Razor-Vorlagensyntax definiert werden, um einen Benutzeroberflächen-Codeausschnitt zu definieren. Für Razor-Vorlagen wird das folgende Format verwendet:

@<{HTML tag}>...</{HTML tag}>

Im folgenden Beispiel wird veranschaulicht, wie Sie RenderFragment- und RenderFragment<TValue>-Werte angeben und Vorlagen direkt in einer-Komponente rendern können. Renderingfragmente können auch als Argumente an Komponentenvorlagen übergeben werden.

Pages/RazorTemplate.razor:

@page "/razor-template"

@timeTemplate

@petTemplate(new Pet { Name = "Nutty Rex" })

@code {
    private RenderFragment timeTemplate = @<p>The time is @DateTime.Now.</p>;
    private RenderFragment<Pet> petTemplate = (pet) => @<p>Pet: @pet.Name</p>;

    private class Pet
    {
        public string Name { get; set; }
    }
}

Gerenderte Ausgabe des vorangehenden Codes:

<p>The time is 4/19/2021 8:54:46 AM.</p>
<p>Pet: Nutty Rex</p>

Statische Ressourcen

In Blazor gilt für statische Ressourcen die Konvention von ASP.NET Core-Apps. Statische Ressourcen befinden sich in einem oder mehreren web root-Ordnern (wwwroot) des Projekts unter dem Ordner wwwroot.

Verwenden Sie einen zur Basis relativen Pfad (/), um auf den Webstamm einer statischen Ressource zu verweisen. Im folgenden Beispiel befindet sich logo.png physisch im Ordner {PROJECT ROOT}/wwwroot/images. {PROJECT ROOT} ist der Projektstamm der App.

<img alt="Company logo" src="/images/logo.png" />

Komponenten unterstützen keine Notation mit Tilde und Schrägstrich (~/).

Informationen zum Festlegen des Basispfads einer App finden Sie unter Hosten und Bereitstellen von Blazor in ASP.NET Core .

Keine Unterstützung von Taghilfsprogrammen in Komponenten

Tag Helpers werden in Komponenten nicht unterstützt. Sie können Taghilfsobjekte in Blazor bereitstellen, indem Sie eine Komponente mit der gleichen Funktionalität wie das Taghilfsprogramm erstellen und diese stattdessen verwenden.

SVG-Bilder

Da Blazor HTML rendert, werden browsergestützte Bilder wie skalierbare Vektorgrafiken (Scalable Vector Graphics, SVG-Grafiken.svg) über das <img>-Tag unterstützt:

<img alt="Example image" src="image.svg" />

Ebenso werden SVG-Bilder in den CSS-Regeln einer Stylesheetdatei (.css) unterstützt:

.element-class {
    background-image: url("image.svg");
}

Verhalten beim Rendern von Leerzeichen

Leerzeichen werden im Quellmarkup einer Komponente beibehalten. Text nur mit Leerzeichen wird im DOM des Browsers auch dann gerendert, wenn dies keine visuellen Auswirkungen hat.

Betrachten Sie das folgende Komponentenmarkup:

<ul>
    @foreach (var item in Items)
    {
        <li>
            @item.Text
        </li>
    }
</ul>

Im vorangehenden Beispiel werden folgende unnötige Leerzeichen gerendert:

  • Außerhalb des Codeblocks @foreach.
  • Im Bereich des Elements <li>.
  • Im Bereich der Ausgabe @item.Text.

Eine Liste von 100 Elementen führt zu mehr als 400 Leerzeichen. Keines der zusätzlichen Leerzeichen wirkt sich visuell auf die gerenderte Ausgabe aus.

Beim Rendern von statischem HTML-Code für Komponenten werden Leerzeichen innerhalb eines Tags nicht beibehalten. Zeigen Sie beispielsweise die gerenderte Ausgabe des folgenden <img>-Tags in einer Razor-Komponentendatei (.razor) an:

<img     alt="Example image"   src="img.png"     />

Leerzeichen aus dem vorangehenden Markup werden nicht beibehalten:

<img alt="Example image" src="img.png" />

Unterstützung generischer Typparameter

Die @typeparam-Direktive deklariert einen generischen Typparameter für die generierte Komponentenklasse:

@typeparam TItem

Im folgenden Beispiel wird die Komponente ListGenericTypeItems1 generisch als TExample typisiert.

Shared/ListGenericTypeItems1.razor:

@typeparam TExample

@if (ExampleList != null)
{
    <ul>
        @foreach (var item in ExampleList)
        {
            <li>@item</li>
        }
    </ul>
}

@code {
    [Parameter]
    public IEnumerable<TExample> ExampleList{ get; set; }
}

Die folgende GenericTypeExample1-Komponente rendert zwei ListGenericTypeItems1-Komponenten:

  • Zeichenfolgen- oder Integerdaten werden dem Parameter ExampleList jeder Komponente zugewiesen.
  • Für den Typparameter (TExample) jeder Komponente wird je nach Typ der zugewiesenen Daten der Typ string oder int festgelegt.

Pages/GenericTypeExample1.razor:

@page "/generic-type-example-1"

<h1>Generic Type Example 1</h1>

<ListGenericTypeItems1 ExampleList="@(new List<string> { "Item 1", "Item 2" })" 
                       TExample="string" />

<ListGenericTypeItems1 ExampleList="@(new List<int> { 1, 2, 3 })" 
                       TExample="int" />

Weitere Informationen finden Sie in den folgenden Artikeln: