Udostępnij za pośrednictwem


Komponenty ASP.NET Core Razor

Uwaga

Nie jest to najnowsza wersja tego artykułu. Aby zapoznać się z bieżącą wersją, zobacz artykuł w wersji .NET 9.

Ostrzeżenie

Ta wersja ASP.NET Core nie jest już obsługiwana. Aby uzyskać więcej informacji, zobacz zasady pomocy technicznej platformy .NET i platformy .NET Core. Aby zapoznać się z bieżącą wersją, zobacz artykuł w wersji .NET 9.

Ważne

Te informacje odnoszą się do produktu w wersji wstępnej, który może zostać znacząco zmodyfikowany, zanim zostanie wydany komercyjnie. Firma Microsoft nie udziela żadnych gwarancji, jawnych lub domniemanych, w odniesieniu do informacji podanych w tym miejscu.

Aby zapoznać się z bieżącą wersją, zobacz wersję tego artykułu dla .NET 9.

W tym artykule objaśniono, jak tworzyć i używać komponentów Razor w aplikacjach Blazor, w tym udziela się wskazówek dotyczących składni, nazewnictwa komponentów, przestrzeni nazw i parametrów komponentów Razor.

Składniki Razor

Blazor aplikacje są tworzone przy użyciu Razor składników, nieformalnie znanych jako Blazor składniki lub tylko składniki. Składnik to samodzielna część interfejsu użytkownika z logiką przetwarzania umożliwiającą zachowania dynamiczne. Składniki mogą być zagnieżdżone, ponownie używane, współużytkowane między projektami i używane w aplikacjach MVC i Razor Pages.

Składniki są renderowane w przechowywanej w pamięci reprezentacji modelu Document Object Model (DOM) przeglądarki nazywanej drzewem renderowania, która służy do aktualizowania interfejsu użytkownika w elastyczny i wydajny sposób.

Chociaż składniki "Razor" dzielą się pewnymi nazwami z innymi technologiami renderowania zawartości w ASP.NET Core, składniki Razor muszą być odróżniane od następujących różnych funkcji w ASP.NET Core:

Ważne

W przypadku korzystania z elementu Blazor Web Appwiększość przykładowych Blazor składników dokumentacji wymaga interakcyjności do działania i zademonstrowania pojęć omówionych w artykułach. interaktywność umożliwia użytkownikom interakcję z renderowanych komponentów. Obejmuje to odpowiedzi aplikacji na zdarzenia Document Object Model (DOM) oraz zmiany stanu, które są powiązane z elementami C# za pomocą programów obsługi zdarzeń i mechanizmów wiązania Blazor. Podczas testowania przykładowego składnika dostarczonego przez artykuł w Blazor Web Appupewnij się, że aplikacja przyjmuje globalną interakcyjność lub składnik przyjmuje tryb renderowania interaktywnego. Więcej informacji na ten temat dostarczają tryby renderowania ASP.NET Core, które są kolejnym artykułem w spisie treści po tym artykule.

Klasy składników

Składniki implementuje się przy użyciu kombinacji języka C# i znaczników HTML w plikach składników Razor z rozszerzeniem .razor.

ComponentBase jest klasą bazową składników opisanych przez Razor pliki składników. ComponentBase implementuje najniższą abstrakcję składników — IComponent interfejs. ComponentBase definiuje właściwości i metody składników dla podstawowych funkcji, na przykład w celu przetwarzania zestawu wbudowanych zdarzeń cyklu życia składników.

ComponentBase w dotnet/aspnetcore źródle referencyjnym: Źródło referencyjne zawiera dodatkowe uwagi dotyczące wbudowanych zdarzeń cyklu życia. Należy jednak pamiętać, że wewnętrzne implementacje funkcji składników mogą ulec zmianie w dowolnym momencie bez powiadomienia.

Uwaga

Linki dokumentacji do źródła referencyjnego platformy .NET zwykle ładują domyślną gałąź repozytorium, która odzwierciedla bieżące programowanie dla następnej wersji platformy .NET. Aby wybrać tag dla określonej wersji, użyj listy rozwijanej Przełącz gałęzie lub tagi. Aby uzyskać więcej informacji, zobacz Jak wybrać tag wersji kodu źródłowego platformy ASP.NET Core (dotnet/AspNetCore.Docs #26205).

Deweloperzy zazwyczaj tworzą Razor składniki na podstawie Razor plików składników (.razor) lub bazują na składnikach ComponentBase, ale składniki mogą być również kompilowane przez zaimplementowanie elementu IComponent. Składniki utworzone przez deweloperów, które implementują IComponent, mogą mieć niskopoziomową kontrolę nad renderowaniem, ale wiąże się to z koniecznością ręcznego wyzwalania renderowania przy użyciu zdarzeń i metod cyklu życia, które deweloper musi stworzyć i utrzymywać.

Dodatkowe konwencje przyjęte przez przykładowy kod dokumentacji i aplikacje przykładowe można znaleźć w podstawach ASP.NET Core.

Razor składnia

Składniki używają składni Razor. Składniki intensywnie wykorzystują dwie cechy: Razordyrektywy i atrybuty dyrektyw. Są to zastrzeżone słowa kluczowe poprzedzone prefiksem @, które występują w znacznikach Razor:

  • Dyrektywy: Zmiana sposobu kompilowania znaczników składników lub funkcji. Na przykład @page dyrektywa określa routowalny składnik z szablonem trasy, który może być osiągnięty bezpośrednio przez żądanie użytkownika w przeglądarce pod określonym adresem URL.

    Zgodnie z konwencją dyrektywy składnika w górnej części definicji składnika (.razor pliku) są umieszczane w spójnej kolejności. W przypadku powtarzających się dyrektyw dyrektywy są umieszczane alfabetycznie według przestrzeni nazw lub typu, z wyjątkiem @using dyrektyw, które mają specjalne porządkowanie drugiego poziomu.

    Poniższa kolejność jest przyjmowana przez Blazor przykładowe aplikacje i dokumentację. Składniki dostarczane przez Blazor szablon projektu mogą różnić się od poniższej kolejności i używać innego formatu. Na przykład Blazor składniki struktury Identity zawierają puste wiersze między blokami @using dyrektyw i blokami @inject dyrektyw. Możesz używać niestandardowego schematu porządkowania i formatowania we własnych aplikacjach.

    Dokumentacja i przykładowa aplikacja Razor - kolejność dyrektyw:

    • @page
    • @rendermode (.NET 8 lub nowszy)
    • @using
      • System przestrzenie nazw (kolejność alfabetyczna)
      • Microsoft przestrzenie nazw (kolejność alfabetyczna)
      • Przestrzenie nazw interfejsu API innych firm (kolejność alfabetyczna)
      • Przestrzenie nazw aplikacji (kolejność alfabetyczna)
    • Inne dyrektywy (kolejność alfabetyczna)

    Uwaga

    Tryb renderowania jest stosowany tylko w Blazor Web Apps i obejmuje tryby, które określają interakcyjność użytkownika z renderowanego składnika. Aby uzyskać więcej informacji, zobacz tryby renderowania ASP.NET CoreBlazor.

    W dyrektywach nie są wyświetlane żadne puste wiersze. Jeden pusty wiersz pojawia się między dyrektywami a pierwszą linią Razor znaczników.

    Przykład:

    @page "/doctor-who-episodes/{season:int}"
    @rendermode InteractiveWebAssembly
    @using System.Globalization
    @using System.Text.Json
    @using Microsoft.AspNetCore.Localization
    @using Mandrill
    @using BlazorSample.Components.Layout
    @attribute [Authorize]
    @implements IAsyncDisposable
    @inject IJSRuntime JS
    @inject ILogger<DoctorWhoEpisodes> Logger
    
    <PageTitle>Doctor Who Episode List</PageTitle>
    
    ...
    
  • Atrybuty dyrektywy: zmień sposób kompilowania elementu składnika lub funkcji.

    Przykład:

    <input @bind="episodeId" />
    

    Możesz prefiksować wartości atrybutów dyrektywy z symbolem at (@) dla wyrażeń innych niż jawne Razor (@bind="@episodeId"), ale nie zalecamy ich, a dokumenty nie przyjmują podejścia w przykładach.

Dyrektywy i atrybuty dyrektyw używane w składnikach są wyjaśnione szerzej w tym artykule i innych artykułach w zestawie dokumentacji Blazor. Aby uzyskać ogólne informacje o składni Razor, zobacz referencje składni dla platformy ASP.NET Core.

Nazwa składnika, nazwa klasy i przestrzeń nazw

Nazwa składnika musi zaczynać się wielką literą:

Obsługiwane:ProductDetail.razor

Nieobsługiwane:productDetail.razor

Typowe konwencje nazewnictwa Blazor używane w dokumentacji Blazor obejmują:

  • Ścieżki plików i nazwy plików używają formatu Pascal case i pojawiają się przed przedstawieniem przykładów kodu. Jeśli ścieżka jest obecna, wskazuje typową lokalizację folderu. Na przykład Components/Pages/ProductDetail.razor wskazuje, że składnik ProductDetail ma nazwę pliku ProductDetail.razor i znajduje się w folderze Pages w folderze Components aplikacji.
  • Ścieżki plików składników dla routowalnych składników odpowiadają ich adresom URL w formacie kebab case, z myślnikami pojawiającymi się między wyrazami w szablonie trasy składnika. Na przykład składnik ProductDetail z szablonem trasy /product-detail (@page "/product-detail") jest żądany w przeglądarce pod względnym adresem URL /product-detail.

†Pascal case (znany też jako upper camel case) to konwencja nazewnictwa bez spacji i interpunkcji, w której każdy wyraz, łącznie z pierwszym, zaczyna się wielką literą.
{Przypadek Kebab to konwencja nazewnictwa bez spacji i znaków interpunkcyjnych, która używa małych liter i kreski między wyrazami.

Składniki są zwykłymi klasami języka C# i można je umieszczać w dowolnym miejscu w projekcie. Składniki tworzące strony internetowe zazwyczaj znajdują się w folderze Components/Pages. Składniki niezwiązane ze stronami są często umieszczane w folderze Components lub w folderze niestandardowym dodanym do projektu.

Zazwyczaj przestrzeń nazw składnika jest oparta na głównej przestrzeni nazw aplikacji i lokalizacji składnika (folderu) w aplikacji. Jeśli główna przestrzeń nazw aplikacji to BlazorSample, a składnik Counter znajduje się w folderze Components/Pages:

  • Przestrzeń nazw składnika Counter to BlazorSample.Components.Pages.
  • W pełni kwalifikowana nazwa typu składnika to BlazorSample.Components.Pages.Counter.

W przypadku niestandardowych folderów zawierających składniki dodaj dyrektywę @using do składnika nadrzędnego lub do pliku _Imports.razor aplikacji. W poniższym przykładzie są udostępniane składniki w folderze AdminComponents:

@using BlazorSample.AdminComponents

Uwaga

Dyrektywy @using w pliku _Imports.razor są stosowane tylko do plików Razor (.razor), a nie do plików C# (.cs).

Instrukcje aliasowe using są obsługiwane. W poniższym przykładzie publiczna WeatherForecast klasa GridRendering komponentu jest udostępniana jako WeatherForecast w innym miejscu komponentu w aplikacji:

@using WeatherForecast = Components.Pages.GridRendering.WeatherForecast

Do składników można też odwoływać się przy użyciu ich w pełni kwalifikowanych nazw, co nie wymaga dyrektywy @using. Poniższy przykład bezpośrednio odwołuje się do składnika ProductDetail w folderze AdminComponents/Pages aplikacji:

<BlazorSample.AdminComponents.Pages.ProductDetail />

Przestrzeń nazw składnika tworzona w aparacie Razor jest oparta na następujących elementach (w kolejności priorytetów):

  • Dyrektywa @namespace w znaczniku pliku Razor (na przykład @namespace BlazorSample.CustomNamespace).
  • Element RootNamespace projektu w pliku projektu (na przykład <RootNamespace>BlazorSample</RootNamespace>).
  • Przestrzeń nazw projektu i ścieżka z katalogu głównego projektu do składnika. Na przykład struktura przypisuje przestrzeń nazw projektu {PROJECT NAMESPACE}/Components/Pages/Home.razor do przestrzeni nazw BlazorSample.Components.Pages składnika Home. {PROJECT NAMESPACE} to przestrzeń nazw projektu. Składniki przestrzegają reguł wiązania nazw języka C#. W tym przykładzie komponent Home włączony jest w zakres wszystkich komponentów:
    • W tym samym folderze (Components/Pages).
    • Składniki w katalogu głównym projektu, które nie określają wyraźnie innej przestrzeni nazw.

Nie są obsługiwane następujące elementy:

  • Kwalifikacja global::.
  • Częściowo kwalifikowane nazwy. Na przykład nie można dodać elementu @using BlazorSample.Components do składnika, a następnie odwołać się do składnika NavMenu w folderze Components/Layout aplikacji (Components/Layout/NavMenu.razor) za pomocą elementu <Layout.NavMenu></Layout.NavMenu>.

Nazwa składnika musi zaczynać się wielką literą:

Obsługiwane:ProductDetail.razor

Nieobsługiwane:productDetail.razor

Typowe konwencje nazewnictwa Blazor używane w dokumentacji Blazor obejmują:

  • Ścieżki plików i nazwy plików używają zapisu Pascal† i pojawiają się przed wyświetleniem przykładów kodu. Jeśli ścieżka jest obecna, wskazuje typową lokalizację folderu. Na przykład Pages/ProductDetail.razor wskazuje, że składnik ProductDetail ma nazwę pliku ProductDetail.razor i znajduje się w folderze Pages aplikacji.
  • Ścieżki plików składników dla składników routowalnych odpowiadają ich adresom URL w formacie "kebab-case", z łącznikami pojawiającymi się między słowami w szablonie trasy składnika. Na przykład składnik ProductDetail z szablonem trasy /product-detail (@page "/product-detail") jest żądany w przeglądarce pod względnym adresem URL /product-detail.

†Pascal case (także zwany wielbłądzim stylem pisania) to konwencja nazewnictwa bez spacji i interpunkcji, w której pierwszy znak każdego wyrazu, łącznie z pierwszym, jest pisany wielką literą.
{Przypadek Kebab to konwencja nazewnictwa bez spacji i znaków interpunkcyjnych, która używa małych liter i kreski między wyrazami.

Składniki są zwykłymi klasami języka C# i można je umieszczać w dowolnym miejscu w projekcie. Składniki tworzące strony internetowe zazwyczaj znajdują się w folderze Pages. Składniki niezwiązane ze stronami są często umieszczane w folderze Shared lub w folderze niestandardowym dodanym do projektu.

Zazwyczaj przestrzeń nazw składnika jest oparta na głównej przestrzeni nazw aplikacji i lokalizacji składnika (folderu) w aplikacji. Jeśli główna przestrzeń nazw aplikacji to BlazorSample, a składnik Counter znajduje się w folderze Pages:

  • Przestrzeń nazw składnika Counter to BlazorSample.Pages.
  • W pełni kwalifikowana nazwa typu składnika to BlazorSample.Pages.Counter.

W przypadku niestandardowych folderów zawierających składniki dodaj dyrektywę @using do składnika nadrzędnego lub do pliku _Imports.razor aplikacji. W poniższym przykładzie są udostępniane składniki w folderze AdminComponents:

@using BlazorSample.AdminComponents

Uwaga

Dyrektywy @using w pliku _Imports.razor są stosowane tylko do plików Razor (.razor), a nie do plików C# (.cs).

Obsługiwane są instrukcje z aliasami using. W poniższym przykładzie publiczna WeatherForecast klasa komponentu GridRendering jest udostępniana jako WeatherForecast w innym obszarze aplikacji.

@using WeatherForecast = Pages.GridRendering.WeatherForecast

Do składników można też odwoływać się przy użyciu ich w pełni kwalifikowanych nazw, co nie wymaga dyrektywy @using. Poniższy przykład bezpośrednio odwołuje się do składnika ProductDetail w folderze Components aplikacji:

<BlazorSample.Components.ProductDetail />

Przestrzeń nazw składnika tworzona w aparacie Razor jest oparta na następujących elementach (w kolejności priorytetów):

  • Dyrektywa @namespace w znacznikach pliku Razor (na przykład @namespace BlazorSample.CustomNamespace).
  • Element RootNamespace projektu w pliku projektu (na przykład <RootNamespace>BlazorSample</RootNamespace>).
  • Przestrzeń nazw projektu i ścieżka z katalogu głównego projektu do składnika. Na przykład struktura przypisuje {PROJECT NAMESPACE}/Pages/Index.razor przestrzeń nazw BlazorSample do przestrzeni nazw BlazorSample.Pages dla składnika Index. {PROJECT NAMESPACE} to przestrzeń nazw projektu. Składniki przestrzegają reguł wiązania nazw języka C#. W tym przykładzie do komponentu Index wszystkie komponenty są w zakresie:
    • W tym samym folderze (Pages).
    • Komponenty w katalogu głównym projektu, które nie określają wyraźnie innej przestrzeni nazw.

Nie są obsługiwane następujące elementy:

  • Kwalifikacja global::.
  • Nazwy częściowo kwalifikowane Na przykład nie można dodać elementu @using BlazorSample do składnika, a następnie odwołać się do składnika NavMenu w folderze Shared aplikacji (Shared/NavMenu.razor) za pomocą elementu <Shared.NavMenu></Shared.NavMenu>.

Obsługa klas częściowych

Składniki są generowane jako klasy częściowe języka C# i są tworzone przy użyciu jednej z następujących metod:

  • Pojedynczy plik zawiera kod w języku C# zdefiniowany w jednym lub kilku blokach @code, znaczniki HTML i znaczniki Razor. Szablony projektów Blazor definiują swoje składniki, używając podejścia jednoplikowego.
  • Kod HTML i znaczniki Razor są umieszczane w pliku Razor (.razor). Kod języka C# jest umieszczany w pliku zaplecza zdefiniowanym jako klasa częściowa (.cs).

Uwaga

Arkusz stylów składnika, który definiuje style specyficzne dla składnika, jest osobnym plikiem (.css). Izolacja CSS jest opisana w dalszej części w sekcji Izolacja CSS w ASP.NET Core Blazor.

W poniższym przykładzie pokazano domyślny składnik Counter z blokiem @code w aplikacji wygenerowanej z szablonu projektu platformy Blazor. Znaczniki i kod języka C# znajdują się w tym samym pliku. Jest to najczęstsze podejście stosowane do tworzenia składników.

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++;
}
@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++;
}
@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++;
    }
}
@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++;
    }
}
@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++;
    }
}
@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++;
    }
}

Poniższy Counter składnik dzieli prezentację HTML i Razor znaczniki od kodu C# używając pliku code-behind z częściową klasą. Oddzielenie znaczników od kodu w języku C# jest preferowane przez niektóre organizacje i deweloperów, aby lepiej dostosować strukturę kodu komponentów do ich preferowanego sposobu pracy. Na przykład ekspert w zakresie interfejsu użytkownika organizacji może pracować nad warstwą prezentacji niezależnie od innego dewelopera pracującego nad logiką języka C# składnika. Takie podejście jest również przydatne podczas pracy z automatycznie generowanym kodem lub generatorami źródłowymi. Aby uzyskać więcej informacji, zobacz Klasy częściowe i metody (Przewodnik programowania w języku C#).

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>
@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>
@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>
@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>
@page "/counter-partial-class"

<h1>Counter</h1>

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

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

<h1>Counter</h1>

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

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

CounterPartialClass.razor.cs:

namespace BlazorSample.Components.Pages;

public partial class CounterPartialClass
{
    private int currentCount = 0;

    private void IncrementCount() => currentCount++;
}
namespace BlazorSample.Components.Pages;

public partial class CounterPartialClass
{
    private int currentCount = 0;

    private void IncrementCount() => currentCount++;
}
namespace BlazorSample.Pages;

public partial class CounterPartialClass
{
    private int currentCount = 0;

    private void IncrementCount()
    {
        currentCount++;
    }
}
namespace BlazorSample.Pages
{
    public partial class CounterPartialClass
    {
        private int currentCount = 0;

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

Dyrektywy @using w pliku _Imports.razor są stosowane tylko do plików Razor (.razor), a nie do plików C# (.cs). Dodaj przestrzenie nazw do pliku klasy częściowej zgodnie z potrzebami.

Typowe przestrzenie nazw używane przez składniki:

using System.Net.Http;
using System.Net.Http.Json;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Components.Authorization;
using Microsoft.AspNetCore.Components.Forms;
using Microsoft.AspNetCore.Components.Routing;
using Microsoft.AspNetCore.Components.Sections
using Microsoft.AspNetCore.Components.Web;
using static Microsoft.AspNetCore.Components.Web.RenderMode;
using Microsoft.AspNetCore.Components.Web.Virtualization;
using Microsoft.JSInterop;

Typowe przestrzenie nazw obejmują też przestrzeń nazw aplikacji i przestrzeń nazw odpowiadającą folderowi Components aplikacji:

using BlazorSample;
using BlazorSample.Components;

Można również uwzględnić dodatkowe foldery, takie jak Layout folder:

using BlazorSample.Components.Layout;
using System.Net.Http;
using System.Net.Http.Json;
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;

Typowe przestrzenie nazw obejmują też przestrzeń nazw aplikacji i przestrzeń nazw odpowiadającą folderowi Shared aplikacji:

using BlazorSample;
using BlazorSample.Shared;
using System.Net.Http;
using Microsoft.AspNetCore.Components.Forms;
using Microsoft.AspNetCore.Components.Routing;
using Microsoft.AspNetCore.Components.Web;
using Microsoft.JSInterop;

Typowe przestrzenie nazw obejmują też przestrzeń nazw aplikacji i przestrzeń nazw odpowiadającą folderowi Shared aplikacji:

using BlazorSample;
using BlazorSample.Shared;

Określanie klasy bazowej

Dyrektywa @inherits służy do określania klasy bazowej dla składnika. W przeciwieństwie do używania klas częściowych, które dzielą tylko znaczniki z logiki języka C#, użycie klasy bazowej umożliwia dziedziczenie kodu C# do użycia w grupie składników, które współużytkujące właściwości i metody klasy bazowej. Używanie klas bazowych zmniejsza nadmiarowość kodu w aplikacjach i jest przydatne podczas dostarczania kodu podstawowego z bibliotek klas do wielu aplikacji. Aby uzyskać więcej informacji, zobacz Dziedziczenie w językach C# i .NET.

W poniższym przykładzie klasa bazowa BlazorRocksBase1 pochodzi z klasy ComponentBase.

BlazorRocks1.razor:

@page "/blazor-rocks-1"
@inherits BlazorRocksBase1

<PageTitle>Blazor Rocks!</PageTitle>

<h1>Blazor Rocks! Example 1</h1>

<p>
    @BlazorRocksText
</p>
@page "/blazor-rocks-1"
@inherits BlazorRocksBase1

<PageTitle>Blazor Rocks!</PageTitle>

<h1>Blazor Rocks! Example 1</h1>

<p>
    @BlazorRocksText
</p>
@page "/blazor-rocks-1"
@inherits BlazorRocksBase1

<PageTitle>Blazor Rocks!</PageTitle>

<h1>Blazor Rocks! Example 1</h1>

<p>
    @BlazorRocksText
</p>
@page "/blazor-rocks-1"
@inherits BlazorRocksBase1

<PageTitle>Blazor Rocks!</PageTitle>

<h1>Blazor Rocks! Example 1</h1>

<p>
    @BlazorRocksText
</p>
@page "/blazor-rocks-1"
@inherits BlazorRocksBase1

<h1>Blazor Rocks! Example 1</h1>

<p>
    @BlazorRocksText
</p>
@page "/blazor-rocks-1"
@inherits BlazorRocksBase1

<h1>Blazor Rocks! Example 1</h1>

<p>
    @BlazorRocksText
</p>

BlazorRocksBase1.cs:

using Microsoft.AspNetCore.Components;

namespace BlazorSample;

public class BlazorRocksBase1 : ComponentBase
{
    public string BlazorRocksText { get; set; } = "Blazor rocks the browser!";
}
using Microsoft.AspNetCore.Components;

namespace BlazorSample;

public class BlazorRocksBase1 : ComponentBase
{
    public string BlazorRocksText { get; set; } = "Blazor rocks the browser!";
}
using Microsoft.AspNetCore.Components;

namespace BlazorSample;

public class BlazorRocksBase1 : ComponentBase
{
    public string BlazorRocksText { get; set; } =
        "Blazor rocks the browser!";
}
using Microsoft.AspNetCore.Components;

namespace BlazorSample;

public class BlazorRocksBase1 : ComponentBase
{
    public string BlazorRocksText { get; set; } =
        "Blazor rocks the browser!";
}
using Microsoft.AspNetCore.Components;

namespace BlazorSample;

public class BlazorRocksBase1 : ComponentBase
{
    public string BlazorRocksText { get; set; } =
        "Blazor rocks the browser!";
}
using Microsoft.AspNetCore.Components;

namespace BlazorSample;

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

Trasowanie

Routing na platformie Blazor jest realizowany poprzez podanie szablonu trasy do każdego osiągalnego składnika aplikacji za pomocą dyrektywy @page. Podczas kompilacji pliku Razor z dyrektywą @page generowana klasa otrzymuje parametr RouteAttribute określający szablon trasy. W czasie wykonywania router wyszukuje klasy składników z elementem RouteAttribute i renderuje dowolny składnik, który ma szablon trasy zgodny z żądanym adresem URL.

Poniższy składnik HelloWorld używa szablonu trasy /hello-world, a renderowana strona internetowa składnika jest dostępna pod względnym adresem URL /hello-world.

HelloWorld.razor:

@page "/hello-world"

<PageTitle>Hello World!</PageTitle>

<h1>Hello World!</h1>
@page "/hello-world"

<PageTitle>Hello World!</PageTitle>

<h1>Hello World!</h1>
@page "/hello-world"

<h1>Hello World!</h1>
@page "/hello-world"

<h1>Hello World!</h1>
@page "/hello-world"

<h1>Hello World!</h1>
@page "/hello-world"

<h1>Hello World!</h1>

Powyższy składnik jest ładowany w przeglądarce pod adresem /hello-world niezależnie od tego, czy dodasz go do nawigacji interfejsu użytkownika aplikacji. Opcjonalnie składniki można dodawać do elementu NavMenu, aby link do danego składnika był widoczny w nawigacji opartej na interfejsie użytkownika aplikacji.

Dla poprzedniego HelloWorld składnika można dodać NavLink składnik do NavMenu składnika. Aby uzyskać więcej informacji, w tym opisy składników NavLink i NavMenu, zobacz Routing i nawigacja na platformie ASP.NET Core Blazor.

Znaczniki

Interfejs użytkownika składnika definiuje się przy użyciu składni Razor, która obejmuje znaczniki Razor oraz kod w językach C# i HTML. Podczas kompilowania aplikacji klienckiej znaczniki HTML i logika renderowania w języku C# są konwertowane na klasę składnika. Nazwa wygenerowanej klasy jest zgodna z nazwą pliku.

Elementy członkowskie klasy składnika definiuje się w jednym lub większej liczbie bloków @code. W blokach @code stan składnika jest określany i przetwarzany za pomocą języka C#:

  • Inicjalizatory właściwości i pól.
  • Wartości parametrów pochodzące z argumentów przekazywanych przez komponenty nadrzędne oraz z parametrów trasy.
  • Metody obsługi zdarzeń użytkownika, zdarzeń cyklu życia i niestandardowej logiki składników.

Elementy członkowskie składnika są używane w logice renderowania za pomocą wyrażeń języka C# zaczynających się symbolem @. Na przykład pole języka C# jest renderowane przez dodanie prefiksu @ do nazwy pola. Następujący składnik Markup analizuje i renderuje elementy:

  • headingFontStyle dla wartości właściwości CSS font-style elementu nagłówka.
  • headingText dla zawartości elementu nagłówka.

Markup.razor:

@page "/markup"

<PageTitle>Markup</PageTitle>

<h1>Markup Example</h1>

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

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

<PageTitle>Markup</PageTitle>

<h1>Markup Example</h1>

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

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

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

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

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

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

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

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

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

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

Uwaga

Przykłady w dokumentacji Blazor określają modyfikator dostępu private dla prywatnych członków. Zakresem prywatnych elementów członkowskich jest klasa składnika. Jednak język C# przyjmuje modyfikator dostępu private, gdy nie jest podany żaden modyfikator dostępu, więc jawne oznaczanie członków modyfikatorem „private” w Twoim własnym kodzie jest opcjonalne. Aby uzyskać więcej informacji o modyfikatorach dostępu, zobacz Modyfikatory dostępu (Przewodnik po programowaniu w języku C#).

Struktura Blazor przetwarza składnik wewnętrznie jako drzewo renderowania, które stanowi połączenie modelu DOM składnika i Kaskadowego Modelu Obiektów Arkusza Stylów (CSSOM) składnika. Po wstępnym renderowaniu składnika jego drzewo renderowania jest generowane ponownie w odpowiedzi na zdarzenia. Platforma Blazor porównuje nowe drzewo renderowania z poprzednim i stosuje wszelkie modyfikacje modelu DOM przeglądarki na potrzeby wyświetlania. Aby uzyskać więcej informacji, zobacz Renderowanie składników platformy ASP.NET Core Razor.

W składni dla struktur sterujących, dyrektyw i atrybutów dyrektyw w języku C# są używane małe litery (przykłady: @if, @code, @bind). Nazwy właściwości zaczynają się wielką literą (przykład: @Body dla elementu LayoutComponentBase.Body).

Metody asynchroniczne (async) nie obsługują zwracania void

Framework Blazor nie śledzi metod asynchronicznych zwracających void (async). Dlatego wyjątki nie są przechwytywane, jeśli zwracany jest void. Należy zawsze zwracać element Task z metod asynchronicznych.

Zagnieżdżone komponenty

Składniki mogą zawierać inne składniki przez zadeklarowanie ich przy użyciu składni języka HTML. Znaczniki dotyczące używania składników wyglądają jak tag HTML, w którym nazwa tagu jest typem składnika.

Rozważmy następujący składnik Heading, który może być używany przez inne składniki w celu wyświetlania nagłówka.

Heading.razor:

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

@code {
    private string headingFontStyle = "italic";
}
<h1 style="font-style:@headingFontStyle">Heading Example</h1>

@code {
    private string headingFontStyle = "italic";
}
<h1 style="font-style:@headingFontStyle">Heading Example</h1>

@code {
    private string headingFontStyle = "italic";
}
<h1 style="font-style:@headingFontStyle">Heading Example</h1>

@code {
    private string headingFontStyle = "italic";
}
<h1 style="font-style:@headingFontStyle">Heading Example</h1>

@code {
    private string headingFontStyle = "italic";
}
<h1 style="font-style:@headingFontStyle">Heading Example</h1>

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

Poniższe znaczniki w składniku HeadingExample renderują powyższy składnik Heading w lokalizacji, w której występuje tag <Heading />.

HeadingExample.razor:

@page "/heading-example"

<PageTitle>Heading</PageTitle>

<h1>Heading Example</h1>

<Heading />
@page "/heading-example"

<PageTitle>Heading</PageTitle>

<h1>Heading Example</h1>

<Heading />
@page "/heading-example"

<Heading />
@page "/heading-example"

<Heading />
@page "/heading-example"

<Heading />
@page "/heading-example"

<Heading />

Jeśli składnik zawiera element HTML zaczynający się wielką literą, która nie jest zgodna z żadną nazwą składnika w tej samej przestrzeni nazw, zostanie wyemitowane ostrzeżenie wskazujące, że element ma nieoczekiwaną nazwę. Dodanie dyrektywy @using dla przestrzeni nazw składnika spowoduje, że składnik stanie się dostępny, a ostrzeżenie zniknie. Aby uzyskać więcej informacji, zobacz sekcję Nazwa składnika, Nazwa klasy i Przestrzeń nazw .

Przykładowy składnik Heading przedstawiony w tej sekcji nie ma dyrektywy @page, więc składnik Heading nie jest bezpośrednio dostępny dla użytkownika za pośrednictwem żądania w przeglądarce. Jednak każdy składnik z dyrektywą @page może być zagnieżdżony w innym składniku. Jeśli składnik Heading byłby bezpośrednio dostępny poprzez dołączenie @page "/heading" na początku jego pliku Razor, to zostałby renderowany dla żądań przeglądarki zarówno pod adresem /heading, jak i /heading-example.

Parametry składników

Parametry składników przekazują dane do składników i są definiowane za pomocą publicznych właściwości języka C# w klasie składnika z atrybutem [Parameter].

W następującym składniku ParameterChild parametry składnika obejmują:

  • Wbudowane typy referencyjne.

  • Typ referencyjny zdefiniowany przez użytkownika (PanelBody), służący do przekazania treści karty Bootstrap do Body.

    PanelBody.cs:

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

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">
        <p>@Body.Text</p>
        @if (Count is not null)
        {
            <p>The count is @Count.</p>
        }
    </div>
</div>

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

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

    [Parameter]
    public int? Count { get; set; }
}
<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">
        <p>@Body.Text</p>
        @if (Count is not null)
        {
            <p>The count is @Count.</p>
        }
    </div>
</div>

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

    [Parameter]
    public PanelBody Body { get; set; } =
        new()
        {
            Text = "Card content set by child.",
            Style = "normal"
        };
    
    [Parameter]
    public int? Count { get; set; }
}
<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">
        <p>@Body.Text</p>
        @if (Count is not null)
        {
            <p>The count is @Count.</p>
        }
    </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"
        };

    [Parameter]
    public int? Count { get; set; }
}
<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">
        <p>@Body.Text</p>
        @if (Count is not null)
        {
            <p>The count is @Count.</p>
        }
    </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"
        };

    [Parameter]
    public int? Count { get; set; }
}
<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">
        <p>@Body.Text</p>
        @if (Count is not null)
        {
            <p>The count is @Count.</p>
        }
    </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"
        };

    [Parameter]
    public int? Count { get; set; }
}
<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">
        <p>@Body.Text</p>
        @if (Count is not null)
        {
            <p>The count is @Count.</p>
        }
    </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"
        };

    [Parameter]
    public int? Count { get; set; }
}

Ostrzeżenie

Obsługiwane jest podawanie wartości początkowych dla parametrów składników, ale nie należy tworzyć składników zapisujących wartości w swoich własnych parametrach po pierwszym renderowaniu składnika. Aby uzyskać więcej informacji, zobacz Unikanie zastępowania parametrów w programie ASP.NET Core Blazor.

Parametry składnika ParameterChild można ustawić za pomocą argumentów w tagu HTML, który renderuje wystąpienie składnika ParameterChild. Następujący składnik nadrzędny renderuje dwa składniki ParameterChild:

  • Pierwszy składnik ParameterChild jest renderowany bez podawania argumentów parametrów.
  • Drugi składnik ParameterChild odbiera wartości dla Title i Body ze składnika nadrzędnego, który używa jawnego wyrażenia języka C# w celu ustawienia wartości właściwości PanelBody.

Parameter1.razor:

@page "/parameter-1"

<PageTitle>Parameter 1</PageTitle>

<h1>Parameter Example 1</h1>

<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" })" />

Parameter1.razor:

@page "/parameter-1"

<PageTitle>Parameter 1</PageTitle>

<h1>Parameter Example 1</h1>

<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" })" />

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" })" />

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" })" />

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" })" />

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" })" />

Poniższy znacznik HTML, renderowany przez składnik nadrzędny, ilustruje domyślne wartości składnika ParameterChild, gdy składnik nadrzędny nie dostarcza wartości parametrów składnika. Kiedy składnik nadrzędny udostępnia wartości parametrów składników, zastępują one domyślne wartości składnika ParameterChild.

Uwaga

Dla przejrzystości większość renderowanych klas stylów CSS i niektóre elementy nie są pokazane w poniższym renderowanym markupu HTML. Główną koncepcją pokazaną w poniższym przykładzie jest to, że komponent nadrzędny przypisuje wartości do komponentu podrzędnego, korzystając z jego parametrów.

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

<div>Set By Child</div>
<div style="font-style:normal">
    <p>Card content set by child.</p>
</div>

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

<div>Set by Parent</div>
<div style="font-style:italic">
    <p>Set by parent.</p>
</div>

Przypisz pole, właściwość lub wynik metody w języku C# do parametru składnika jako wartość atrybutu HTML. Wartość atrybutu może być zwykle dowolnym wyrażeniem języka C#, które jest zgodne z typem parametru. Wartość atrybutu może opcjonalnie rozpoczynać się od zastrzeżonego Razor symbolu@, ale nie jest to wymagane.

Jeśli parametr składnika jest ciągiem typu, wartość atrybutu jest zamiast tego traktowana jako literał ciągu języka C#. Jeśli zamiast tego chcesz określić wyrażenie języka C#, użyj prefiksu @ .

Poniższy składnik nadrzędny wyświetla cztery wystąpienia poprzedniego składnika ParameterChild i ustawia ich Title wartości parametrów na:

Piąte wystąpienie składnika ParameterChild ustawia również parametr Count. Zwróć uwagę, że parametr o typie stringwymaga przedrostka @, aby upewnić się, że wyrażenie nie jest traktowane jako literał ciągu. Jednak Count jest nullowalną liczbą całkowitą (System.Int32), więc Count może przyjąć wartość count bez prefiksu @. Możesz ustanowić alternatywną konwencję nazewnictwa kodu, która wymaga od deweloperów w organizacji zawsze dodawać przedrostek @. Tak czy inaczej, zalecamy jedynie zastosowanie spójnego podejścia do sposobu przekazywania parametrów komponentów w Razor markup.

Cudzysłowy wokół wartości atrybutów parametrów są w większości przypadków opcjonalne, zgodnie ze specyfikacją HTML5. Na przykład jest obsługiwana składnia Value=this zamiast Value="this". Zalecamy jednak używanie cudzysłowów, ponieważ łatwiej jest je zapamiętać i są one powszechnie używane w technologiach opartych na sieci Web.

W całej dokumentacji, przykłady kodu:

  • Zawsze są używane cudzysłowy. Przykład: Value="this".
  • Nie używaj prefiksu @ z niemianownikowymi, chyba że jest to wymagane. Przykład: Count="count", gdzie count jest zmienną typu liczba. Count="@count" jest prawidłowym podejściem stylistycznym, ale dokumentacja i przykłady nie przyjmują konwencji.
  • Zawsze unikaj używania @ dla literałów poza wyrażeniami Razor. Przykład: IsFixed="true". Obejmuje to słowa kluczowe (na przykład this) i null, ale możesz użyć ich, jeśli chcesz. Na przykład instrukcja IsFixed="@true" jest nietypowa, ale jest obsługiwana.

Parameter2.razor:

@page "/parameter-2"

<PageTitle>Parameter 2</PageTitle>

<h1>Parameter Example 2</h1>

<ParameterChild Title="@title" />

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

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

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

<ParameterChild Title="String literal title" Count="count" />

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

    private string GetTitle() => "From Parent method";

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

Parameter2.razor:

@page "/parameter-2"

<PageTitle>Parameter 2</PageTitle>

<h1>Parameter Example 2</h1>

<ParameterChild Title="@title" />

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

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

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

<ParameterChild Title="String literal title" Count="count" />

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

    private string GetTitle() => "From Parent method";

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

ParameterParent2.razor:

@page "/parameter-parent-2"

<ParameterChild Title="@title" />

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

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

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

<ParameterChild Title="String literal title" Count="count" />

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

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

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

ParameterParent2.razor:

@page "/parameter-parent-2"

<ParameterChild Title="@title" />

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

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

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

<ParameterChild Title="String literal title" Count="count" />

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

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

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

ParameterParent2.razor:

@page "/parameter-parent-2"

<ParameterChild Title="@title" />

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

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

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

<ParameterChild Title="String literal title" Count="count" />

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

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

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

Uwaga

Podczas przypisywania elementu składowego języka C# do parametru składnika, nie poprzedzaj atrybutu HTML parametru za pomocą @.

Correct (Title jest parametrem ciągu, Count jest parametrem typu liczbowego):

<ParameterChild Title="@title" Count="count" />
<ParameterChild Title="@title" Count="@count" />

Odpowiedź nieprawidłowa:

<ParameterChild @Title="@title" @Count="count" />
<ParameterChild @Title="@title" @Count="@count" />

W przeciwieństwie do stron Razor (.cshtml), platforma Blazor nie może wykonywać prac asynchronicznych w wyrażeniu Razor podczas renderowania składnika. Wynika to z tego, że platforma Blazor jest przeznaczona do renderowania interakcyjnych interfejsów użytkownika. W interakcyjnym interfejsie użytkownika ekran musi zawsze coś wyświetlać, więc blokowanie przepływu renderowania nie ma sensu. Zamiast tego działania asynchroniczne są wykonywane podczas jednego z asynchronicznych zdarzeń cyklu życia. Po każdym asynchronicznym zdarzeniu cyklu życia składnik może być renderowany ponownie. Następująca składnia Razornie jest obsługiwana:

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

Kod w poprzednim przykładzie generuje błąd kompilatora podczas kompilowania aplikacji:

Operatora „await” można używać tylko w metodzie asynchronicznej. Należy rozważyć oznaczenie tej metody modyfikatorem „async” i zmianę jej zwracanego typu na „Task”.

Aby uzyskać wartość parametru Title w poprzednim przykładzie w sposób asynchroniczny, składnik może skorzystać z zdarzenia cyklu życia OnInitializedAsync, jak pokazano w poniższym przykładzie:

<ParameterChild Title="@title" />

@code {
    private string? title;
    
    protected override async Task OnInitializedAsync()
    {
        title = await ...;
    }
}

Aby uzyskać więcej informacji, zobacz Cykl życia składników platformy ASP.NET Core Razor.

Użycie jawnego wyrażenia Razor w celu połączenia tekstu z wynikiem wyrażenia dla przypisania do parametru nie jest obsługiwane. Poniższy przykład przedstawia próbę połączenia tekstu „Set by ” z wartością właściwości obiektu. Składnia ta jest obsługiwana na stronie Razor (.cshtml), ale nie jest poprawna do przypisania parametru Title komponentu potomnego. Następująca składnia Razornie jest obsługiwana:

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

Kod w poprzednim przykładzie generuje błąd kompilatora podczas kompilowania aplikacji:

Atrybuty składników nie obsługują zawartości złożonej (mieszania języka C# i znaczników).

Aby umożliwić przypisanie wartości złożonej, użyj metody, pola lub właściwości. W poniższym przykładzie następuje połączenie tekstu „Set by ” z wartością właściwości obiektu w metodzie C# GetTitle.

Parameter3.razor:

@page "/parameter-3"

<PageTitle>Parameter 3</PageTitle>

<h1>Parameter Example 3</h1>

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

@code {
    private PanelData panelData = new();

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

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

Parameter3.razor:

@page "/parameter-3"

<PageTitle>Parameter 3</PageTitle>

<h1>Parameter Example 3</h1>

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

@code {
    private PanelData panelData = new();

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

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

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";
    }
}

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";
    }
}

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";
    }
}

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";
    }
}

Aby uzyskać więcej informacji, zobacz Razor odniesienie składni dla ASP.NET Core.

Ostrzeżenie

Obsługiwane jest podawanie wartości początkowych dla parametrów składników, ale nie należy tworzyć składników zapisujących wartości w swoich własnych parametrach po pierwszym renderowaniu składnika. Aby uzyskać więcej informacji, zobacz Unikanie zastępowania parametrów w programie ASP.NET Core Blazor.

Parametry składników powinny być zadeklarowane jako właściwości automatyczne, co oznacza, że nie powinny zawierać niestandardowej logiki w swoich metodach dostępu get i set. Na przykład następująca właściwość StartData jest właściwością automatyczną:

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

Nie umieszczaj logiki niestandardowej w akcesorach get i set, ponieważ parametry komponentów są przeznaczone wyłącznie do używania jako kanał dla komponentu nadrzędnego na potrzeby przepływu informacji do komponentu podrzędnego. Jeśli akcesor set właściwości składnika podrzędnego zawiera logikę wywołującą ponowne renderowanie składnika nadrzędnego, powstaje nieskończona pętla renderowania.

Aby przekształcić otrzymaną wartość parametru:

  • Pozostaw właściwość parametru jako właściwość automatyczną, aby reprezentować dostarczone dane pierwotne.
  • Utwórz inną właściwość lub metodę, aby dostarczyć przekształcone dane na podstawie właściwości parametru.

Nadpisz OnParametersSetAsync, aby przekształcać otrzymany parametr przy każdym otrzymaniu nowych danych.

Zapisywanie wartości początkowej w parametrze składnika jest obsługiwane, ponieważ przypisania wartości początkowych nie kolidują z automatycznym renderowaniem składnika Blazor. Następujące przypisanie bieżącej lokalnej wartości DateTime za pomocą właściwości DateTime.Now do elementu StartData jest poprawną składnią w składniku:

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

Po początkowym przypisaniu właściwości DateTime.Nownie przypisuj wartości do elementu StartData w kodzie dewelopera. Aby uzyskać więcej informacji, zobacz Unikanie zastępowania parametrów w programie ASP.NET Core Blazor.

Zastosuj atrybut [EditorRequired], aby określić wymagany parametr składnika. Jeśli wartość parametru nie zostanie podana, edytory lub narzędzia do kompilacji mogą wyświetlać ostrzeżenia dla użytkownika. Ten atrybut jest poprawny tylko dla właściwości również oznaczonych atrybutem [Parameter]. Atrybut EditorRequiredAttribute jest wymuszany w czasie projektowania i podczas kompilowania aplikacji. Ten atrybut nie jest wymuszany w czasie wykonywania i nie gwarantuje wartości parametru niebędącej wartością null.

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

Obsługiwane są też jednowierszowe listy atrybutów:

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

Nie używaj required modyfikatora ani init akcesora we właściwościach parametru składnika. Składniki są zwykle instancjonowane i przypisywane są im wartości parametrów przy użyciu odbicia, które omija gwarancje, jakie init i required mają zapewniać. Zamiast tego użyj atrybutu [EditorRequired] , aby określić wymagany parametr składnika.

Nie używaj akcesora init na właściwościach parametrów składnika, ponieważ ustawianie wartości parametrów składnika za pomocą ParameterView.SetParameterProperties z zastosowaniem odbicia omija ograniczenie dotyczące inicjalizacji tylko przy inicjatorze. Użyj atrybutu [EditorRequired] , aby określić wymagany parametr składnika.

Nie używaj akcesora we właściwościach parametrów składnika, ponieważ ustawianie wartości parametrów składnika przy użyciu odbicia omija ograniczenie ustawiania tylko podczas inicjalizacji.

Tuples (dokumentacja interfejsu API) jest obsługiwana dla parametrów składników i typów RenderFragment. W poniższym przykładzie parametru składnika są przekazywane trzy wartości w elemencie Tuple:

RenderTupleChild.razor:

<div class="card w-50" style="margin-bottom:15px">
    <div class="card-header font-weight-bold">Tuple 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 (int, string, bool)? Data { get; set; }
}

RenderTupleParent.razor:

@page "/render-tuple-parent"

<PageTitle>Render Tuple Parent</PageTitle>

<h1>Render Tuple Parent Example</h1>

<RenderTupleChild Data="data" />

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

Nazwane krotki są obsługiwane, co pokazano w poniższym przykładzie:

NamedTupleChild.razor:

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

@code {
    [Parameter]
    public (int TheInteger, string TheString, bool TheBoolean)? Data { get; set; }
}

NamedTuples.razor:

@page "/named-tuples"

<PageTitle>Named Tuples</PageTitle>

<h1>Named Tuples Example</h1>

<NamedTupleChild Data="data" />

@code {
    private (int TheInteger, string TheString, bool TheBoolean) data = 
        new(999, "I aim to misbehave.", true);
}

©2005 Universal Pictures: Serenity (Nathan Fillion)

Tuples (dokumentacja interfejsu API) jest obsługiwana dla parametrów komponentów i typów RenderFragment. W poniższym przykładzie parametru składnika są przekazywane trzy wartości w elemencie Tuple:

RenderTupleChild.razor:

<div class="card w-50" style="margin-bottom:15px">
    <div class="card-header font-weight-bold">Tuple 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 (int, string, bool)? Data { get; set; }
}

RenderTupleParent.razor:

@page "/render-tuple-parent"

<PageTitle>Render Tuple Parent</PageTitle>

<h1>Render Tuple Parent Example</h1>

<RenderTupleChild Data="data" />

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

Nazwane krotki są obsługiwane, jak pokazano w poniższym przykładzie:

NamedTupleChild.razor:

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

@code {
    [Parameter]
    public (int TheInteger, string TheString, bool TheBoolean)? Data { get; set; }
}

NamedTuples.razor:

@page "/named-tuples"

<PageTitle>Named Tuples</PageTitle>

<h1>Named Tuples Example</h1>

<NamedTupleChild Data="data" />

@code {
    private (int TheInteger, string TheString, bool TheBoolean) data = 
        new(999, "I aim to misbehave.", true);
}

©2005 Universal Pictures: Serenity (Nathan Fillion)

Tuples (dokumentacja interfejsu API) jest obsługiwana dla parametrów elementów i typów RenderFragment. W poniższym przykładzie parametru składnika są przekazywane trzy wartości w elemencie Tuple:

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 (int, string, bool)? Data { get; set; }
}

RenderTupleParent.razor:

@page "/render-tuple-parent"

<h1>Render Tuple Parent</h1>

<RenderTupleChild Data="data" />

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

Nazwane krotki są obsługiwane, jak pokazano w poniższym przykładzie:

RenderNamedTupleChild.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?.TheInteger</li>
            <li>String: @Data?.TheString</li>
            <li>Boolean: @Data?.TheBoolean</li>
        </ul>
    </div>
</div>

@code {
    [Parameter]
    public (int TheInteger, string TheString, bool TheBoolean)? Data { get; set; }
}

RenderNamedTupleParent.razor:

@page "/render-named-tuple-parent"

<h1>Render Named Tuple Parent</h1>

<RenderNamedTupleChild Data="data" />

@code {
    private (int TheInteger, string TheString, bool TheBoolean) data = 
        new(999, "I aim to misbehave.", true);
}

©2005 Universal Pictures: Serenity (Nathan Fillion)

Parametry tras

Składniki mogą określać parametry trasy w szablonie trasy w dyrektywie @page. RouterBlazor używa parametrów trasy do wypełniania odpowiadających parametrów komponentu.

RouteParameter1.razor:

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

<PageTitle>Route Parameter 1</PageTitle>

<h1>Route Parameter Example 1</h1>

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

@code {
    [Parameter]
    public string? Text { get; set; }
}
@page "/route-parameter-1/{text}"

<PageTitle>Route Parameter 1</PageTitle>

<h1>Route Parameter Example 1</h1>

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

@code {
    [Parameter]
    public string? Text { get; set; }
}
@page "/route-parameter-1/{text}"

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

@code {
    [Parameter]
    public string? Text { get; set; }
}
@page "/route-parameter-1/{text}"

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

@code {
    [Parameter]
    public string? Text { get; set; }
}
@page "/route-parameter-1/{text}"

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

@code {
    [Parameter]
    public string Text { get; set; }
}
@page "/route-parameter-1/{text}"

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

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

Aby uzyskać więcej informacji, zobacz sekcję dotyczącą parametrów trasy w routingu i nawigacji ASP.NET CoreBlazor. Opcjonalne parametry trasy są również obsługiwane i omówione w tej samej sekcji. Aby uzyskać informacje na temat parametrów trasy typu catch-all ({*pageRoute}), które przechwytują ścieżki poprzez wiele granic folderów, zobacz sekcję Parametry trasy catch-all w ASP.NET Core Blazor routingu i nawigacji.

Aby uzyskać więcej informacji, zobacz sekcję Parametry trasy w routingu i nawigacji ASP.NET CoreBlazor. Opcjonalne parametry trasy nie są obsługiwane, dlatego wymagane są dwie @page dyrektywy (zobacz sekcję Parametry trasy, aby uzyskać więcej informacji). Aby uzyskać informacje na temat parametrów trasy typu catch-all ({*pageRoute}), które przechwytują ścieżki przekraczające granice wielu folderów, zobacz sekcję Parametry trasy catch-all w ASP.NET Core Blazor routingu i nawigacji.

Ostrzeżenie

Dzięki kompresji, która jest domyślnie włączona, unikaj tworzenia bezpiecznych (uwierzytelnionych/autoryzowanych) interaktywnych składników po stronie serwera, które renderuje dane z niezaufanych źródeł. Niezaufane źródła obejmują parametry trasy, ciągi zapytania, dane z JS międzyoperacyjności i inne źródła danych, które zewnętrzny użytkownik może kontrolować (bazy danych, usługi zewnętrzne). Aby uzyskać więcej informacji, zobacz zgodność z ASP.NET CoreBlazorSignalR wskazówki oraz Wytyczne dotyczące łagodzenia zagrożeń dla ASP.NET Core interaktywnym renderowaniem po stronie serweraBlazor.

Renderowanie fragmentów zawartości podrzędnej

Składniki mogą ustawiać zawartość innych składników. Składnik przypisujący podaje zawartość między tagiem otwierającym i tagiem zamykającym składnika podrzędnego.

W poniższym przykładzie składnik RenderFragmentChild ma parametr składnika ChildContent, który reprezentuje segment interfejsu użytkownika do renderowania jako element RenderFragment. Położenie elementu ChildContent w znacznikach składnika Razor określa miejsce, w którym renderowana jest zawartość w końcowym HTML.

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; }
}
<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; }
}
<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; }
}
<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; }
}
<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; }
}
<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; }
}

Ważne

Właściwość otrzymująca zawartość RenderFragment musi według konwencji mieć nazwę ChildContent.

Wywołania zwrotne zdarzeń nie są obsługiwane dla elementu RenderFragment.

Poniższa część zapewnia zawartość do renderowania komponentu RenderFragmentChild, umieszczając ją w tagach otwierających i zamykających komponentu podrzędnego.

RenderFragments.razor:

@page "/render-fragments"

<PageTitle>Render Fragments</PageTitle>

<h1>Render Fragments Example</h1>

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

RenderFragments.razor:

@page "/render-fragments"

<PageTitle>Render Fragments</PageTitle>

<h1>Render Fragments Example</h1>

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

RenderFragmentParent.razor:

@page "/render-fragment-parent"

<h1>Render child content</h1>

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

RenderFragmentParent.razor:

@page "/render-fragment-parent"

<h1>Render child content</h1>

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

RenderFragmentParent.razor:

@page "/render-fragment-parent"

<h1>Render child content</h1>

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

RenderFragmentParent.razor:

@page "/render-fragment-parent"

<h1>Render child content</h1>

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

Fragmenty renderowania są używane do renderowania zawartości podrzędnej w aplikacjach platformy Blazor i zostały opisane wraz z przykładami w następujących artykułach i sekcjach artykułów:

Uwaga

Wbudowane komponenty Blazor frameworku używają tej samej konwencji parametrów komponentu ChildContent do ustawiania ich zawartości. Aby zobaczyć składniki, które ustawiają zawartość podrzędną, możesz wyszukać nazwę właściwości parametru składnika ChildContent w dokumentacji interfejsu API (filtrowanie interfejsu API przy użyciu terminu wyszukiwania „ChildContent”).

Fragmenty renderowania dla logiki renderowania do wielokrotnego użytku

Składniki podrzędne można wyodrębnić w celu ponownego używania logiki renderowania. W bloku @code dowolnego składnika zdefiniuj element RenderFragment i renderuj fragment z dowolnego miejsca tyle razy, ile jest to potrzebne:

@RenderWelcomeInfo

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

@RenderWelcomeInfo

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

Aby uzyskać więcej informacji, zobacz Ponowne używanie logiki renderowania.

Zmienne pętli z parametrami składników i zawartością podrzędną

Renderowanie komponentów wewnątrz for pętli wymaga lokalnej zmiennej indeksu, jeśli zmienna pętli inkrementacyjnej jest używana przez parametry komponentu lub RenderFragment treść potomną.

Rozważmy następujący RenderFragmentChild2 składnik, który ma zarówno parametr składnika (Id), jak i fragment renderowania do wyświetlania zawartości podrzędnej (ChildContent).

RenderFragmentChild2.razor:

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

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

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

Podczas renderowania składnika RenderFragmentChild2 w składniku nadrzędnym, użyj lokalnej zmiennej indeksu (ct w poniższym przykładzie) zamiast zmiennej pętli (c) podczas przypisywania wartości parametru do składnika i dostarczania zawartości składnika podrzędnego:

@for (int c = 1; c < 4; c++)
{
    var ct = c;

    <RenderFragmentChild2 Id="@($"Child{ct}")">
        Count: @ct
    </RenderFragmentChild2>
}

Alternatywnie, użyj pętli foreach z Enumerable.Range zamiast pętli for.

@foreach (var c in Enumerable.Range(1, 3))
{
    <RenderFragmentChild2 Id="@($"Child{c}")">
        Count: @c
    </RenderFragmentChild2>
}

Przechwytywanie odwołań do składników

Odwołania do komponentów umożliwiają odwoływanie się do instancji komponentów w celu wydawania poleceń. Aby przechwycić odwołanie do komponentu:

  • Dodaj atrybut @ref do składnika podrzędnego.
  • Zdefiniuj pole o tym samym typie co komponent podrzędny.

Gdy składnik jest renderowany, pole jest wypełniane wystąpieniem składnika. Następnie można wywoływać metody platformy .NET w tym wystąpieniu.

Rozważmy następujący składnik ReferenceChild, który rejestruje komunikat, gdy jest wywoływana jego metoda ChildMethod.

ReferenceChild.razor:

@inject ILogger<ReferenceChild> Logger

@if (value > 0)
{
    <p>
        <code>value</code>: @value
    </p>
}

@code {
    private int value;

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

        this.value = value;
        StateHasChanged();
    }
}
@inject ILogger<ReferenceChild> Logger

@if (value > 0)
{
    <p>
        <code>value</code>: @value
    </p>
}

@code {
    private int value;

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

        this.value = value;
        StateHasChanged();
    }
}
@using Microsoft.Extensions.Logging
@inject ILogger<ReferenceChild> Logger

@code {
    public void ChildMethod(int value)
    {
        Logger.LogInformation("Received {Value} in ChildMethod", value);
    }
}
@using Microsoft.Extensions.Logging
@inject ILogger<ReferenceChild> Logger

@code {
    public void ChildMethod(int value)
    {
        Logger.LogInformation("Received {Value} in ChildMethod", value);
    }
}
@using Microsoft.Extensions.Logging
@inject ILogger<ReferenceChild> Logger

@code {
    public void ChildMethod(int value)
    {
        Logger.LogInformation("Received {Value} in ChildMethod", value);
    }
}
@using Microsoft.Extensions.Logging
@inject ILogger<ReferenceChild> Logger

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

Odwołanie do składnika zostanie wypełnione dopiero wtedy, gdy nastąpi renderowanie składnika, a jego dane wyjściowe będą zawierać element składnika ReferenceChild. Do czasu renderowania składnika nie ma do czego się odwoływać. Nie należy próbować bezpośrednio wywoływać metody składnika z procedury zdarzenia (na przykład @onclick="childComponent!.ChildMethod(5)"), ponieważ zmienna referencyjna może nie być przypisana w momencie przypisania zdarzenia kliknięcia.

Aby manipulować odwołaniami do składników po zakończeniu renderowania danego składnika, używaj metod OnAfterRender lub OnAfterRenderAsync.

W poniższym przykładzie użyto poprzedniego ReferenceChild składnika.

ReferenceParent.razor:

@page "/reference-parent"

<div>
    <button @onclick="@(() => childComponent1!.ChildMethod(5))">
        Call <code>ReferenceChild.ChildMethod</code> (first instance) 
        with an argument of 5
    </button>

    <ReferenceChild @ref="childComponent1" />
</div>

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

    <ReferenceChild @ref="childComponent2" />
</div>

@code {
    private ReferenceChild? childComponent1;
    private ReferenceChild? childComponent2;

    private void CallChildMethod() => childComponent2!.ChildMethod(5);
}
@page "/reference-parent"

<div>
    <button @onclick="@(() => childComponent1!.ChildMethod(5))">
        Call <code>ReferenceChild.ChildMethod</code> (first instance) 
        with an argument of 5
    </button>

    <ReferenceChild @ref="childComponent1" />
</div>

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

    <ReferenceChild @ref="childComponent2" />
</div>

@code {
    private ReferenceChild? childComponent1;
    private ReferenceChild? childComponent2;

    private void CallChildMethod() => childComponent2!.ChildMethod(5);
}
@page "/reference-parent"

<div>
    <button @onclick="@(() => childComponent1!.ChildMethod(5))">
        Call <code>ReferenceChild.ChildMethod</code> (first instance) 
        with an argument of 5
    </button>

    <ReferenceChild @ref="childComponent1" />
</div>

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

    <ReferenceChild @ref="childComponent2" />
</div>

@code {
    private ReferenceChild? childComponent1;
    private ReferenceChild? childComponent2;

    private void CallChildMethod()
    {
        childComponent2!.ChildMethod(5);
    }
}
@page "/reference-parent"

<div>
    <button @onclick="@(() => childComponent1!.ChildMethod(5))">
        Call <code>ReferenceChild.ChildMethod</code> (first instance) 
        with an argument of 5
    </button>

    <ReferenceChild @ref="childComponent1" />
</div>

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

    <ReferenceChild @ref="childComponent2" />
</div>

@code {
    private ReferenceChild? childComponent1;
    private ReferenceChild? childComponent2;

    private void CallChildMethod()
    {
        childComponent2!.ChildMethod(5);
    }
}
@page "/reference-parent"

<div>
    <button @onclick="@(() => childComponent1!.ChildMethod(5))">
        Call <code>ReferenceChild.ChildMethod</code> (first instance) 
        with an argument of 5
    </button>

    <ReferenceChild @ref="childComponent1" />
</div>

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

    <ReferenceChild @ref="childComponent2" />
</div>

@code {
    private ReferenceChild childComponent1;
    private ReferenceChild childComponent2;

    private void CallChildMethod()
    {
        childComponent2!.ChildMethod(5);
    }
}
@page "/reference-parent"

<div>
    <button @onclick="@(() => childComponent1!.ChildMethod(5))">
        Call <code>ReferenceChild.ChildMethod</code> (first instance) 
        with an argument of 5
    </button>

    <ReferenceChild @ref="childComponent1" />
</div>

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

    <ReferenceChild @ref="childComponent2" />
</div>

@code {
    private ReferenceChild childComponent1;
    private ReferenceChild childComponent2;

    private void CallChildMethod()
    {
        childComponent2!.ChildMethod(5);
    }
}

Przechwytywanie odwołań do składników używa podobnej składni jak przechwytywanie odwołań do elementów, ale nie jest funkcją współdziałającą z językiem JavaScript. Odwołania do komponentów nie są przekazywane do kodu języka JavaScript. Odwołania do składników są używane tylko w kodzie platformy .NET.

Ważne

Nie należy używać odwołań do komponentów w celu modyfikowania stanu komponentów podrzędnych. Zamiast tego należy używać normalnych deklaracyjnych parametrów składników w celu przekazania danych do składników podrzędnych. Używanie parametrów składników spowoduje, że składniki podrzędne będą automatycznie renderowane ponownie w odpowiednim czasie. Aby uzyskać więcej informacji, zobacz sekcję Parametry składników i artykuł Wiązanie danych na platformie ASP.NET Core Blazor.

Stosowanie atrybutu

Atrybuty można stosować do składników za pomocą dyrektywy @attribute. W poniższym przykładzie stosuje się atrybut [Authorize] do klasy składnika:

@page "/"
@attribute [Authorize]

Warunkowe atrybuty elementu HTML i właściwości MODELU DOM

Blazor przyjmuje następujące ogólne zachowania:

  • W przypadku atrybutów HTML Blazor ustawia lub usuwa atrybut warunkowo na podstawie wartości .NET. Jeśli wartość platformy .NET to false lub null, atrybut nie jest ustawiony lub jest usuwany, jeśli został wcześniej ustawiony.
  • W przypadku właściwości DOM, takich jak checked lub value, Blazor ustawia właściwość DOM na podstawie wartości .NET. Jeśli wartość platformy .NET to false lub null, właściwość DOM zostanie zresetowana do wartości domyślnej.

Atrybuty składni Razor, które odpowiadają atrybutom HTML i właściwościom DOM, pozostają nieudokumentowane, ponieważ stanowią one szczegół implementacji frameworku, który może ulec zmianie bez powiadomienia.

Ostrzeżenie

Niektóre atrybuty HTML, takie jak aria-pressed, muszą mieć wartość ciągu "true" lub "false". Ponieważ wymagają wartości ciągu, a nie wartości logicznej, musisz użyć .NET string zamiast bool dla ich wartości. Jest to wymóg ustalony przez API DOM przeglądarki.

Nieprzetworzony kod HTML

Ciągi tekstowe są zwykle renderowane przy użyciu węzłów tekstowych DOM, co oznacza, że wszelkie znaczniki, które mogą zawierać, są ignorowane i traktowane jako dosłowny tekst. Aby renderować nieprzetworzony kod HTML, umieść zawartość HTML wewnątrz wartości MarkupString. Wartość jest analizowana jako kod HTML lub SVG i wstawiana w modelu DOM.

Ostrzeżenie

Renderowanie nieprzetworzonego kodu HTML utworzonego z jakiegokolwiek niezaufanego źródła jest zagrożeniem dla bezpieczeństwa i zawsze należy tego unikać.

W poniższym przykładzie pokazano użycie typu MarkupString w celu dodania bloku statycznej zawartości HTML do renderowanych danych wyjściowych składnika.

MarkupStrings.razor:

@page "/markup-strings"

<PageTitle>Markup Strings</PageTitle>

<h1>Markup Strings Example</h1>

@((MarkupString)myMarkup)

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

MarkupStrings.razor:

@page "/markup-strings"

<PageTitle>Markup Strings</PageTitle>

<h1>Markup Strings Example</h1>

@((MarkupString)myMarkup)

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

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>";
}

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>";
}

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>";
}

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 szablony

Fragmenty renderowania można definiować, używając składni szablonu Razor aby zdefiniować fragment interfejsu użytkownika. Szablony Razor stosują następujący format:

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

Poniższy przykład przedstawia sposób określania wartości RenderFragment i RenderFragment<TValue> oraz renderowania szablonów bezpośrednio w składniku. Fragmenty renderowania można też przekazywać jako argumenty do komponentów szablonowych.

RazorTemplate.razor:

@page "/razor-template"

<PageTitle>Razor Template</PageTitle>

<h1>Razor Template Example</h1>

@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; }
    }
}
@page "/razor-template"

<PageTitle>Razor Template</PageTitle>

<h1>Razor Template Example</h1>

@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; }
    }
}
@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; }
    }
}
@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; }
    }
}
@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; }
    }
}
@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; }
    }
}

Renderowane dane wyjściowe powyższego kodu:

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

Zasoby statyczne

Platforma Blazor stosuje konwencję platformy ASP.NET Core dla zasobów statycznych. Zasoby statyczne znajdują się w folderze web root (wwwroot) projektu lub w folderach wewnątrz folderu wwwroot.

Używaj ścieżki określonej względem ścieżki bazowej (/), aby odwoływać się do katalogu głównego sieci Web w przypadku zasobu statycznego. W poniższym przykładzie plik logo.png znajduje się fizycznie w folderze {PROJECT ROOT}/wwwroot/images. {PROJECT ROOT} to główny katalog projektu aplikacji.

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

Składniki nie obsługują notacji z tyldą i ukośnikiem (~/).

Aby uzyskać informacje o ustawianiu ścieżki bazowej aplikacji, zobacz Hostowanie i wdrażanie platformy ASP.NET Core Blazor.

Pomocnicy tagów nie są obsługiwani w składnikach

Tag Helpers nie są obsługiwani w składnikach. Aby zapewnić funkcjonalność podobną do pomocników tagów na platformie Blazor, utwórz składnik o takiej samej funkcjonalności jak pomocnik tagów i użyj zamiast niego tego składnika.

Obrazy w formacie skalowalnej grafiki wektorowej (SVG)

Platforma Blazor renderuje kod HTML, więc obrazy obsługiwane przez przeglądarkę, w tym obrazy Scalable Vector Graphics (SVG) (.svg), są obsługiwane przy użyciu tagu <img>:

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

Podobnie, obrazy SVG są obsługiwane w regułach CSS pliku arkusza stylów (.css):

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

Platforma Blazor obsługuje element <foreignObject> na potrzeby wyświetlania dowolnego kodu HTML wewnątrz obrazu SVG. Ten znacznik może reprezentować dowolny kod HTML, element RenderFragment lub komponent Razor.

Poniższy przykład przedstawia:

  • Wyświetlanie elementu string (@message).
  • Wiązanie dwukierunkowe z elementem <input> i polem value.
  • Składnik Robot.
<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;
}

Zachowanie renderowania odstępów

O ile dyrektywa @preservewhitespace nie zostanie użyta z wartością true, dodatkowe spacje zostaną usunięte, jeśli:

  • Występują na początku lub na końcu elementu.
  • Występują na początku lub na końcu wewnątrz parametru RenderFragment/RenderFragment<TValue> (np. zawartość podrzędna przekazywana do innego składnika).
  • Poprzedzają blok kodu języka C# lub następują po takim bloku, na przykład @if lub @foreach.

Usuwanie odstępów może wpływać na renderowane dane wyjściowe w przypadku używania reguły CSS, takiej jak white-space: pre. Aby wyłączyć tę optymalizację wydajności i zachować odstępy, wykonaj jedną z następujących czynności:

  • Dodaj dyrektywę @preservewhitespace true na początku pliku Razor (.razor), aby zastosować preferencję do konkretnego składnika.
  • Dodaj dyrektywę @preservewhitespace true wewnątrz pliku _Imports.razor, aby zastosować preferencję do podkatalogu lub całego projektu.

W większości przypadków nie są wymagane żadne czynności, ponieważ aplikacje zazwyczaj nadal działają normalnie (ale szybciej). Jeśli usuwanie odstępów powoduje problem z renderowaniem konkretnego składnika, użyj w nim dyrektywy @preservewhitespace true, aby wyłączyć tę optymalizację.

Odstępy są zachowywane w kodzie źródłowym składnika. Tekst zawierający tylko odstępy jest renderowany w modelu DOM przeglądarki nawet wtedy, gdy nie ma żadnego efektu wizualnego.

Weź pod uwagę następujący znacznik składnika:

<ul>
    @foreach (var item in Items)
    {
        <li>
            @item.Text
        </li>
    }
</ul>

W powyższym przykładzie renderowane są następujące niepotrzebne odstępy:

  • Poza blokiem kodu @foreach.
  • Wokół elementu <li>.
  • Wokół wyjścia @item.Text.

Lista 100 elementów powoduje utworzenie ponad 400 obszarów pustej przestrzeni. Żadne z dodatkowych odstępów nie wpływają wizualnie na renderowane dane wyjściowe.

Podczas renderowania statycznego kodu HTML dla składników odstępy wewnątrz tagów nie są zachowywane. Na przykład możesz wyświetlić renderowane dane wyjściowe następującego tagu <img> w pliku składnika Razor (.razor).

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

Odstępy nie są zachowywane w powyższych znacznikach:

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

Składnik główny

Składnik główny Razor komponent (główny komponent) jest pierwszym składnikiem ładowanym w dowolnej hierarchii składników stworzonej przez aplikację.

W aplikacji utworzonej na podstawie szablonu projektu Blazor Web App, składnik App (App.razor) jest określony jako domyślny element główny za pomocą parametru typu zadeklarowanego dla wywołania MapRazorComponents<TRootComponent> w pliku po stronie serwera Program. W poniższym przykładzie pokazano użycie App składnika jako składnika głównego, który jest domyślnym ustawieniem aplikacji utworzonej na podstawie szablonu Blazor projektu:

app.MapRazorComponents<App>();

Uwaga

Tworzenie interaktywnego składnika głównego, takiego jak App, nie jest obsługiwane.

W aplikacji utworzonej Blazor Server na podstawie szablonu App projektu składnik (App.razor) jest określony jako domyślny składnik główny przy Pages/_Host.cshtml użyciu pomocnika tagów składników:

<component type="typeof(App)" render-mode="ServerPrerendered" />

W aplikacji utworzonej na podstawie szablonu projektu Blazor WebAssembly, składnik App (App.razor) jest określony jako domyślny główny składnik w pliku Program.

builder.RootComponents.Add<App>("#app");

W poprzednim kodzie selektor CSS #app wskazuje, że komponent App jest określony dla <div> w wwwroot/index.html z id równym app:

<div id="app">...</app>

Aplikacje MVC i Razor Pages mogą również używać Component Tag Helper do rejestrowania statycznie renderowanych Blazor WebAssembly komponentów głównych:

<component type="typeof(App)" render-mode="WebAssemblyPrerendered" />

Statycznie renderowane składniki można dodawać tylko do aplikacji. Nie można ich później usunąć ani zaktualizować.

Aby uzyskać więcej informacji, zobacz następujące zasoby: