From 495cdada15febd18f65497c368c782a975faa9b5 Mon Sep 17 00:00:00 2001 From: guardrex <1622880+guardrex@users.noreply.github.com> Date: Tue, 8 Nov 2022 06:15:47 -0600 Subject: [PATCH] Blazor .NET 7 release work --- aspnetcore/blazor/advanced-scenarios.md | 30 +- aspnetcore/blazor/blazor-server-ef-core.md | 114 +- aspnetcore/blazor/call-web-api.md | 86 +- .../blazor/components/built-in-components.md | 36 +- .../cascading-values-and-parameters.md | 166 +- .../blazor/components/class-libraries.md | 2 +- .../blazor/components/control-head-content.md | 55 +- aspnetcore/blazor/components/css-isolation.md | 2 +- aspnetcore/blazor/components/data-binding.md | 408 +- .../blazor/components/dynamiccomponent.md | 34 +- .../blazor/components/event-handling.md | 492 +-- aspnetcore/blazor/components/index.md | 3616 ++++++++--------- aspnetcore/blazor/components/layouts.md | 90 +- aspnetcore/blazor/components/lifecycle.md | 138 +- .../prerendering-and-integration.md | 1724 ++++---- aspnetcore/blazor/components/rendering.md | 50 +- .../blazor/components/templated-components.md | 52 +- .../blazor/components/virtualization.md | 312 +- aspnetcore/blazor/debug.md | 150 +- aspnetcore/blazor/file-downloads.md | 22 +- aspnetcore/blazor/file-uploads.md | 114 +- .../blazor/forms-and-input-components.md | 1592 ++++---- .../blazor/fundamentals/configuration.md | 2 +- .../fundamentals/dependency-injection.md | 460 +-- .../blazor/fundamentals/environments.md | 96 +- .../blazor/fundamentals/handle-errors.md | 1010 ++--- aspnetcore/blazor/fundamentals/index.md | 2 +- aspnetcore/blazor/fundamentals/logging.md | 970 ++--- aspnetcore/blazor/fundamentals/routing.md | 1276 +++--- aspnetcore/blazor/fundamentals/signalr.md | 256 +- aspnetcore/blazor/fundamentals/startup.md | 142 +- .../blazor/fundamentals/static-files.md | 2 +- .../blazor/globalization-localization.md | 430 +- .../host-and-deploy/configure-linker.md | 2 +- .../host-and-deploy/configure-trimmer.md | 2 +- aspnetcore/blazor/host-and-deploy/index.md | 2 +- .../multiple-hosted-webassembly.md | 2 +- aspnetcore/blazor/host-and-deploy/server.md | 490 +-- .../webassembly-deployment-layout.md | 2 +- .../blazor/host-and-deploy/webassembly.md | 760 ++-- aspnetcore/blazor/hosting-models.md | 216 +- aspnetcore/blazor/hybrid/class-libraries.md | 10 +- aspnetcore/blazor/hybrid/developer-tools.md | 2 +- aspnetcore/blazor/hybrid/index.md | 2 +- .../blazor/hybrid/reuse-razor-components.md | 2 +- aspnetcore/blazor/hybrid/routing.md | 162 +- aspnetcore/blazor/hybrid/security/index.md | 2 +- .../security/security-considerations.md | 2 +- aspnetcore/blazor/hybrid/static-files.md | 2 +- aspnetcore/blazor/hybrid/tutorials/index.md | 2 +- aspnetcore/blazor/hybrid/tutorials/maui.md | 2 +- .../blazor/hybrid/tutorials/windows-forms.md | 2 +- aspnetcore/blazor/hybrid/tutorials/wpf.md | 2 +- aspnetcore/blazor/images.md | 16 +- aspnetcore/blazor/index.md | 2 +- .../call-dotnet-from-javascript.md | 1536 +++---- .../call-javascript-from-dotnet.md | 1058 ++--- .../import-export-interop.md | 2 +- .../javascript-interoperability/index.md | 122 +- aspnetcore/blazor/performance.md | 316 +- aspnetcore/blazor/progressive-web-app.md | 367 +- aspnetcore/blazor/project-structure.md | 106 +- .../security/content-security-policy.md | 76 +- aspnetcore/blazor/security/index.md | 212 +- .../security/server/additional-scenarios.md | 2 +- aspnetcore/blazor/security/server/index.md | 38 +- .../security/server/threat-mitigation.md | 2 +- .../webassembly/additional-scenarios.md | 370 +- ...azure-active-directory-groups-and-roles.md | 28 +- .../blazor/security/webassembly/graph-api.md | 2 +- .../hosted-with-azure-active-directory-b2c.md | 128 +- .../hosted-with-azure-active-directory.md | 100 +- .../hosted-with-identity-server.md | 736 ++-- .../blazor/security/webassembly/index.md | 108 +- .../standalone-with-authentication-library.md | 18 +- ...ndalone-with-azure-active-directory-b2c.md | 70 +- .../standalone-with-azure-active-directory.md | 42 +- .../standalone-with-microsoft-accounts.md | 42 +- aspnetcore/blazor/state-management.md | 461 ++- aspnetcore/blazor/supported-platforms.md | 30 +- aspnetcore/blazor/test.md | 2 +- aspnetcore/blazor/tooling.md | 556 +-- .../blazor/tutorials/build-a-blazor-app.md | 98 +- aspnetcore/blazor/tutorials/index.md | 2 +- aspnetcore/blazor/tutorials/signalr-blazor.md | 296 +- .../webassembly-lazy-load-assemblies.md | 2 +- .../blazor/webassembly-native-dependencies.md | 2 +- 87 files changed, 11695 insertions(+), 10852 deletions(-) diff --git a/aspnetcore/blazor/advanced-scenarios.md b/aspnetcore/blazor/advanced-scenarios.md index bdefb0851015..ebcb4dd5f7ba 100644 --- a/aspnetcore/blazor/advanced-scenarios.md +++ b/aspnetcore/blazor/advanced-scenarios.md @@ -5,7 +5,7 @@ description: Learn how to incorporate manual logic for building Blazor render tr monikerRange: '>= aspnetcore-3.1' ms.author: riande ms.custom: mvc -ms.date: 11/09/2021 +ms.date: 11/08/2022 uid: blazor/advanced-scenarios --- # ASP.NET Core Blazor advanced scenarios (render tree construction) @@ -15,7 +15,7 @@ This article describes the advanced scenario for building Blazor render trees ma > [!WARNING] > Use of to create components is an *advanced scenario*. A malformed component (for example, an unclosed markup tag) can result in undefined behavior. Undefined behavior includes broken content rendering, loss of app features, and ***compromised security***. -:::moniker range=">= aspnetcore-6.0 < aspnetcore-7.0" +:::moniker range=">= aspnetcore-7.0" ## Manually build a render tree (`RenderTreeBuilder`) @@ -25,7 +25,7 @@ Consider the following `PetDetails` component, which can be manually rendered in `Shared/PetDetails.razor`: -:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Shared/advanced-scenarios/PetDetails.razor"::: +:::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Shared/advanced-scenarios/PetDetails.razor"::: In the following `BuiltContent` component, the loop in the `CreateComponent` method generates three `PetDetails` components. @@ -33,7 +33,7 @@ In methods wi `Pages/BuiltContent.razor`: -:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Pages/advanced-scenarios/BuiltContent.razor" highlight="6,16-24,28"::: +:::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Pages/advanced-scenarios/BuiltContent.razor" highlight="6,16-24,28"::: > [!WARNING] > The types in allow processing of the *results* of rendering operations. These are internal details of the Blazor framework implementation. These types should be considered *unstable* and subject to change in future releases. @@ -130,7 +130,7 @@ This is a trivial example. In more realistic cases with complex and deeply neste :::moniker-end -:::moniker range=">= aspnetcore-5.0 < aspnetcore-6.0" +:::moniker range=">= aspnetcore-6.0 < aspnetcore-7.0" ## Manually build a render tree (`RenderTreeBuilder`) @@ -140,7 +140,7 @@ Consider the following `PetDetails` component, which can be manually rendered in `Shared/PetDetails.razor`: -:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Shared/advanced-scenarios/PetDetails.razor"::: +:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Shared/advanced-scenarios/PetDetails.razor"::: In the following `BuiltContent` component, the loop in the `CreateComponent` method generates three `PetDetails` components. @@ -148,7 +148,7 @@ In methods wi `Pages/BuiltContent.razor`: -:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Pages/advanced-scenarios/BuiltContent.razor" highlight="6,16-24,28"::: +:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Pages/advanced-scenarios/BuiltContent.razor" highlight="6,16-24,28"::: > [!WARNING] > The types in allow processing of the *results* of rendering operations. These are internal details of the Blazor framework implementation. These types should be considered *unstable* and subject to change in future releases. @@ -213,7 +213,7 @@ if (someFlag) builder.AddContent(seq++, "Second"); ``` -The first output is seen in the following table. +The first output is reflected in the following table. | Sequence | Type | Data | | :------: | --------- | :----: | @@ -245,7 +245,7 @@ This is a trivial example. In more realistic cases with complex and deeply neste :::moniker-end -:::moniker range="< aspnetcore-5.0" +:::moniker range=">= aspnetcore-5.0 < aspnetcore-6.0" ## Manually build a render tree (`RenderTreeBuilder`) @@ -255,7 +255,7 @@ Consider the following `PetDetails` component, which can be manually rendered in `Shared/PetDetails.razor`: -:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Shared/advanced-scenarios/PetDetails.razor"::: +:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Shared/advanced-scenarios/PetDetails.razor"::: In the following `BuiltContent` component, the loop in the `CreateComponent` method generates three `PetDetails` components. @@ -263,7 +263,7 @@ In methods wi `Pages/BuiltContent.razor`: -:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Pages/advanced-scenarios/BuiltContent.razor" highlight="6,16-24,28"::: +:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Pages/advanced-scenarios/BuiltContent.razor" highlight="6,16-24,28"::: > [!WARNING] > The types in allow processing of the *results* of rendering operations. These are internal details of the Blazor framework implementation. These types should be considered *unstable* and subject to change in future releases. @@ -360,7 +360,7 @@ This is a trivial example. In more realistic cases with complex and deeply neste :::moniker-end -:::moniker range=">= aspnetcore-7.0" +:::moniker range="< aspnetcore-5.0" ## Manually build a render tree (`RenderTreeBuilder`) @@ -370,7 +370,7 @@ Consider the following `PetDetails` component, which can be manually rendered in `Shared/PetDetails.razor`: -:::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Shared/advanced-scenarios/PetDetails.razor"::: +:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Shared/advanced-scenarios/PetDetails.razor"::: In the following `BuiltContent` component, the loop in the `CreateComponent` method generates three `PetDetails` components. @@ -378,7 +378,7 @@ In methods wi `Pages/BuiltContent.razor`: -:::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Pages/advanced-scenarios/BuiltContent.razor" highlight="6,16-24,28"::: +:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Pages/advanced-scenarios/BuiltContent.razor" highlight="6,16-24,28"::: > [!WARNING] > The types in allow processing of the *results* of rendering operations. These are internal details of the Blazor framework implementation. These types should be considered *unstable* and subject to change in future releases. @@ -443,7 +443,7 @@ if (someFlag) builder.AddContent(seq++, "Second"); ``` -The first output is reflected in the following table. +The first output is seen in the following table. | Sequence | Type | Data | | :------: | --------- | :----: | diff --git a/aspnetcore/blazor/blazor-server-ef-core.md b/aspnetcore/blazor/blazor-server-ef-core.md index a780247c7a7e..da7325e7cb3d 100644 --- a/aspnetcore/blazor/blazor-server-ef-core.md +++ b/aspnetcore/blazor/blazor-server-ef-core.md @@ -5,14 +5,14 @@ description: Learn how to use Entity Framework Core (EF Core) in Blazor Server a monikerRange: '>= aspnetcore-3.1' ms.author: jeliknes ms.custom: mvc -ms.date: 11/09/2021 +ms.date: 11/08/2022 uid: blazor/blazor-server-ef-core --- # ASP.NET Core Blazor Server with Entity Framework Core (EF Core) This article explains how to use [Entity Framework Core (EF Core)](/ef/core/) in Blazor Server apps. -:::moniker range=">= aspnetcore-6.0 < aspnetcore-7.0" +:::moniker range=">= aspnetcore-7.0" Blazor Server is a stateful app framework. The app maintains an ongoing connection to the server, and the user's state is held in the server's memory in a *circuit*. One example of user state is data held in [dependency injection (DI)](xref:fundamentals/dependency-injection) service instances that are scoped to the circuit. The unique application model that Blazor Server provides requires a special approach to use Entity Framework Core. @@ -27,7 +27,7 @@ The sample app was built as a reference for Blazor Server apps that use EF Core. The sample uses a local [SQLite](https://www.sqlite.org/index.html) database so that it can be used on any platform. The sample also configures database logging to show the SQL queries that are generated. This is configured in `appsettings.Development.json`: -:::code language="json" source="~/../blazor-samples/6.0/BlazorServerEFCoreSample/BlazorServerDbContextExample/appsettings.Development.json" highlight="8"::: +:::code language="json" source="~/../blazor-samples/7.0/BlazorServerEFCoreSample/appsettings.Development.json" highlight="8"::: The grid, add, and view components use the "context-per-operation" pattern, where a context is created for each operation. The edit component uses the "context-per-component" pattern, where a context is created for each component. @@ -80,8 +80,6 @@ The following recommendations are designed to provide a consistent approach to u * For longer-lived operations that take advantage of EF Core's [change tracking](/ef/core/querying/tracking) or [concurrency control](/ef/core/saving/concurrency), [scope the context to the lifetime of the component](#scope-to-the-component-lifetime). -## New `DbContext` instances - The fastest way to create a new instance is by using `new` to create a new instance. However, there are scenarios that require resolving additional dependencies: * Using [`DbContextOptions`](/ef/core/miscellaneous/configuring-dbcontext#configuring-dbcontextoptions) to configure the context. @@ -91,11 +89,11 @@ The recommended approach to create a new ` is injected into the component: +In `Pages/Index.razor` of the [sample app](https://github.com/dotnet/blazor-samples/blob/main/7.0/BlazorServerEFCoreSample/BlazorServerDbContextExample/Pages/Index.razor), `IDbContextFactory` is injected into the component: ```razor @inject IDbContextFactory DbFactory @@ -103,10 +101,10 @@ In `Pages/Index.razor` of the [sample app](https://github.com/dotnet/blazor-samp A `DbContext` is created using the factory (`DbFactory`) to delete a contact in the `DeleteContactAsync` method: -:::code language="csharp" source="~/../blazor-samples/6.0/BlazorServerEFCoreSample/BlazorServerDbContextExample/Pages/Index.razor" id="snippet1"::: +:::code language="csharp" source="~/../blazor-samples/7.0/BlazorServerEFCoreSample/Pages/Index.razor" id="snippet1"::: > [!NOTE] -> `Filters` is an injected `IContactFilters`, and `Wrapper` is a [component reference](xref:blazor/components/index#capture-references-to-components) to the `GridWrapper` component. See the `Index` component (`Pages/Index.razor`) in the [sample app](https://github.com/dotnet/blazor-samples/blob/main/6.0/BlazorServerEFCoreSample/BlazorServerDbContextExample/Pages/Index.razor). +> `Filters` is an injected `IContactFilters`, and `Wrapper` is a [component reference](xref:blazor/components/index#capture-references-to-components) to the `GridWrapper` component. See the `Index` component (`Pages/Index.razor`) in the [sample app](https://github.com/dotnet/blazor-samples/blob/main/6.0/BlazorServerEFCoreSample/Pages/Index.razor). ## Scope to the component lifetime @@ -120,11 +118,11 @@ You can use the factory to create a context and track it for the lifetime of the The sample app ensures the context is disposed when the component is disposed: -:::code language="csharp" source="~/../blazor-samples/6.0/BlazorServerEFCoreSample/BlazorServerDbContextExample/Pages/EditContact.razor" id="snippet1"::: +:::code language="csharp" source="~/../blazor-samples/7.0/BlazorServerEFCoreSample/Pages/EditContact.razor" id="snippet1"::: Finally, [`OnInitializedAsync`](xref:blazor/components/lifecycle) is overridden to create a new context. In the sample app, [`OnInitializedAsync`](xref:blazor/components/lifecycle) loads the contact in the same method: -:::code language="csharp" source="~/../blazor-samples/6.0/BlazorServerEFCoreSample/BlazorServerDbContextExample/Pages/EditContact.razor" id="snippet2"::: +:::code language="csharp" source="~/../blazor-samples/7.0/BlazorServerEFCoreSample/Pages/EditContact.razor" id="snippet2"::: ## Enable sensitive data logging @@ -150,7 +148,7 @@ We recommend only enabling [!NOTE] -> Some of the code examples in this topic require namespaces and services that aren't shown. To inspect the fully working code, including the required [`@using`](xref:mvc/views/razor#using) and [`@inject`](xref:mvc/views/razor#inject) directives for Razor examples, see the [sample app](https://github.com/dotnet/AspNetCore.Docs/tree/main/aspnetcore/blazor/samples/). +> Some of the code examples in this topic require namespaces and services that aren't shown. To inspect the fully working code, including the required [`@using`](xref:mvc/views/razor#using) and [`@inject`](xref:mvc/views/razor#inject) directives for Razor examples, see the [sample app](https://github.com/dotnet/blazor-samples). ## Database access @@ -229,11 +227,11 @@ The recommended approach to create a new ` is injected into the component: +In `Pages/Index.razor` of the [sample app](https://github.com/dotnet/blazor-samples/blob/main/6.0/BlazorServerEFCoreSample/BlazorServerDbContextExample/Pages/Index.razor), `IDbContextFactory` is injected into the component: ```razor @inject IDbContextFactory DbFactory @@ -241,10 +239,10 @@ In `Pages/Index.razor` of the [sample app](https://github.com/dotnet/blazor-samp A `DbContext` is created using the factory (`DbFactory`) to delete a contact in the `DeleteContactAsync` method: -:::code language="csharp" source="~/../blazor-samples/5.0/BlazorServerEFCoreSample/BlazorServerDbContextExample/Pages/Index.razor" id="snippet1"::: +:::code language="csharp" source="~/../blazor-samples/6.0/BlazorServerEFCoreSample/BlazorServerDbContextExample/Pages/Index.razor" id="snippet1"::: > [!NOTE] -> `Filters` is an injected `IContactFilters`, and `Wrapper` is a [component reference](xref:blazor/components/index#capture-references-to-components) to the `GridWrapper` component. See the `Index` component (`Pages/Index.razor`) in the [sample app](https://github.com/dotnet/blazor-samples/blob/main/5.0/BlazorServerEFCoreSample/BlazorServerDbContextExample/Pages/Index.razor). +> `Filters` is an injected `IContactFilters`, and `Wrapper` is a [component reference](xref:blazor/components/index#capture-references-to-components) to the `GridWrapper` component. See the `Index` component (`Pages/Index.razor`) in the [sample app](https://github.com/dotnet/blazor-samples/blob/main/6.0/BlazorServerEFCoreSample/BlazorServerDbContextExample/Pages/Index.razor). ## Scope to the component lifetime @@ -258,11 +256,11 @@ You can use the factory to create a context and track it for the lifetime of the The sample app ensures the context is disposed when the component is disposed: -:::code language="csharp" source="~/../blazor-samples/5.0/BlazorServerEFCoreSample/BlazorServerDbContextExample/Pages/EditContact.razor" id="snippet1"::: +:::code language="csharp" source="~/../blazor-samples/6.0/BlazorServerEFCoreSample/BlazorServerDbContextExample/Pages/EditContact.razor" id="snippet1"::: Finally, [`OnInitializedAsync`](xref:blazor/components/lifecycle) is overridden to create a new context. In the sample app, [`OnInitializedAsync`](xref:blazor/components/lifecycle) loads the contact in the same method: -:::code language="csharp" source="~/../blazor-samples/5.0/BlazorServerEFCoreSample/BlazorServerDbContextExample/Pages/EditContact.razor" id="snippet2"::: +:::code language="csharp" source="~/../blazor-samples/6.0/BlazorServerEFCoreSample/BlazorServerDbContextExample/Pages/EditContact.razor" id="snippet2"::: ## Enable sensitive data logging @@ -288,7 +286,7 @@ We recommend only enabling as the means to [configure database access](/ef/core/miscellaneous/configuring-dbcontext) and act as a [*unit of work*](https://martinfowler.com/eaaCatalog/unitOfWork.html). EF Core provides the extension for ASP.NET Core apps that registers the context as a *scoped* service by default. In Blazor Server apps, this can be problematic because the instance is shared across components within the user's circuit. isn't thread safe and isn't designed for concurrent use. The existing lifetimes are inappropriate for these reasons: +EF Core relies on a as the means to [configure database access](/ef/core/miscellaneous/configuring-dbcontext) and act as a [*unit of work*](https://martinfowler.com/eaaCatalog/unitOfWork.html). EF Core provides the extension for ASP.NET Core apps that registers the context as a *scoped* service by default. In Blazor Server apps, scoped service registrations can be problematic because the instance is shared across components within the user's circuit. isn't thread safe and isn't designed for concurrent use. The existing lifetimes are inappropriate for these reasons: * **Singleton** shares state across all users of the app and leads to inappropriate concurrent use. * **Scoped** (the default) poses a similar issue between components for the same user. @@ -363,22 +361,15 @@ The fastest way to create a new i * Using [`DbContextOptions`](/ef/core/miscellaneous/configuring-dbcontext#configuring-dbcontextoptions) to configure the context. * Using a connection string per , such as when you use [ASP.NET Core's Identity model](xref:security/authentication/customize_identity_model). For more information, see [Multi-tenancy (EF Core documentation)](/ef/core/miscellaneous/multitenancy). -The recommended approach to create a new with dependencies is to use a factory. The sample app implements its own factory in `Data/DbContextFactory.cs`. - -:::code language="csharp" source="~/../blazor-samples/3.1/BlazorServerEFCoreSample/BlazorServerDbContextExample/Data/DbContextFactory.cs"::: - -In the preceding factory: - -* satisfies any dependencies via the service provider. -* `IDbContextFactory` is available in EF Core ASP.NET Core 5.0 or later, so the interface is [implemented in the sample app for ASP.NET Core 3.x](https://github.com/dotnet/blazor-samples/blob/main/3.1/BlazorServerEFCoreSample/BlazorServerDbContextExample/Data/IDbContextFactory.cs). +The recommended approach to create a new with dependencies is to use a factory. EF Core 5.0 or later provides a built-in factory for creating new contexts. -The following example configures [SQLite](https://www.sqlite.org/index.html) and enables data logging. The code uses an extension method to configure the database factory for DI and provide default options: +The following example configures [SQLite](https://www.sqlite.org/index.html) and enables data logging. The code uses an extension method (`AddDbContextFactory`) to configure the database factory for DI and provide default options: -:::code language="csharp" source="~/../blazor-samples/3.1/BlazorServerEFCoreSample/BlazorServerDbContextExample/Startup.cs" id="snippet1"::: +:::code language="csharp" source="~/../blazor-samples/5.0/BlazorServerEFCoreSample/BlazorServerDbContextExample/Startup.cs" id="snippet1"::: The factory is injected into components and used to create new `DbContext` instances. -In `Pages/Index.razor` of the [sample app](https://github.com/dotnet/blazor-samples/blob/main/3.1/BlazorServerEFCoreSample/BlazorServerDbContextExample/Pages/Index.razor), `IDbContextFactory` is injected into the component: +In `Pages/Index.razor` of the [sample app](https://github.com/dotnet/blazor-samples/blob/main/5.0/BlazorServerEFCoreSample/BlazorServerDbContextExample/Pages/Index.razor), `IDbContextFactory` is injected into the component: ```razor @inject IDbContextFactory DbFactory @@ -386,10 +377,10 @@ In `Pages/Index.razor` of the [sample app](https://github.com/dotnet/blazor-samp A `DbContext` is created using the factory (`DbFactory`) to delete a contact in the `DeleteContactAsync` method: -:::code language="csharp" source="~/../blazor-samples/3.1/BlazorServerEFCoreSample/BlazorServerDbContextExample/Pages/Index.razor" id="snippet1"::: +:::code language="csharp" source="~/../blazor-samples/5.0/BlazorServerEFCoreSample/BlazorServerDbContextExample/Pages/Index.razor" id="snippet1"::: > [!NOTE] -> `Filters` is an injected `IContactFilters`, and `Wrapper` is a [component reference](xref:blazor/components/index#capture-references-to-components) to the `GridWrapper` component. See the `Index` component (`Pages/Index.razor`) in the [sample app](https://github.com/dotnet/blazor-samples/blob/main/3.1/BlazorServerEFCoreSample/BlazorServerDbContextExample/Pages/Index.razor). +> `Filters` is an injected `IContactFilters`, and `Wrapper` is a [component reference](xref:blazor/components/index#capture-references-to-components) to the `GridWrapper` component. See the `Index` component (`Pages/Index.razor`) in the [sample app](https://github.com/dotnet/blazor-samples/blob/main/5.0/BlazorServerEFCoreSample/BlazorServerDbContextExample/Pages/Index.razor). ## Scope to the component lifetime @@ -403,16 +394,11 @@ You can use the factory to create a context and track it for the lifetime of the The sample app ensures the context is disposed when the component is disposed: -:::code language="csharp" source="~/../blazor-samples/3.1/BlazorServerEFCoreSample/BlazorServerDbContextExample/Pages/EditContact.razor" id="snippet1"::: +:::code language="csharp" source="~/../blazor-samples/5.0/BlazorServerEFCoreSample/BlazorServerDbContextExample/Pages/EditContact.razor" id="snippet1"::: Finally, [`OnInitializedAsync`](xref:blazor/components/lifecycle) is overridden to create a new context. In the sample app, [`OnInitializedAsync`](xref:blazor/components/lifecycle) loads the contact in the same method: -:::code language="csharp" source="~/../blazor-samples/3.1/BlazorServerEFCoreSample/BlazorServerDbContextExample/Pages/EditContact.razor" id="snippet2"::: - -In the preceding example: - -* When `Busy` is set to `true`, asynchronous operations may begin. When `Busy` is set back to `false`, asynchronous operations should be finished. -* Place additional error handling logic in a `catch` block. +:::code language="csharp" source="~/../blazor-samples/5.0/BlazorServerEFCoreSample/BlazorServerDbContextExample/Pages/EditContact.razor" id="snippet2"::: ## Enable sensitive data logging @@ -438,7 +424,7 @@ We recommend only enabling [!NOTE] -> Some of the code examples in this topic require namespaces and services that aren't shown. To inspect the fully working code, including the required [`@using`](xref:mvc/views/razor#using) and [`@inject`](xref:mvc/views/razor#inject) directives for Razor examples, see the [sample app](https://github.com/dotnet/blazor-samples). +> Some of the code examples in this topic require namespaces and services that aren't shown. To inspect the fully working code, including the required [`@using`](xref:mvc/views/razor#using) and [`@inject`](xref:mvc/views/razor#inject) directives for Razor examples, see the [sample app](https://github.com/dotnet/AspNetCore.Docs/tree/main/aspnetcore/blazor/samples/). ## Database access -EF Core relies on a as the means to [configure database access](/ef/core/miscellaneous/configuring-dbcontext) and act as a [*unit of work*](https://martinfowler.com/eaaCatalog/unitOfWork.html). EF Core provides the extension for ASP.NET Core apps that registers the context as a *scoped* service by default. In Blazor Server apps, scoped service registrations can be problematic because the instance is shared across components within the user's circuit. isn't thread safe and isn't designed for concurrent use. The existing lifetimes are inappropriate for these reasons: +EF Core relies on a as the means to [configure database access](/ef/core/miscellaneous/configuring-dbcontext) and act as a [*unit of work*](https://martinfowler.com/eaaCatalog/unitOfWork.html). EF Core provides the extension for ASP.NET Core apps that registers the context as a *scoped* service by default. In Blazor Server apps, this can be problematic because the instance is shared across components within the user's circuit. isn't thread safe and isn't designed for concurrent use. The existing lifetimes are inappropriate for these reasons: * **Singleton** shares state across all users of the app and leads to inappropriate concurrent use. * **Scoped** (the default) poses a similar issue between components for the same user. @@ -506,20 +492,29 @@ The following recommendations are designed to provide a consistent approach to u * For longer-lived operations that take advantage of EF Core's [change tracking](/ef/core/querying/tracking) or [concurrency control](/ef/core/saving/concurrency), [scope the context to the lifetime of the component](#scope-to-the-component-lifetime). +## New `DbContext` instances + The fastest way to create a new instance is by using `new` to create a new instance. However, there are scenarios that require resolving additional dependencies: * Using [`DbContextOptions`](/ef/core/miscellaneous/configuring-dbcontext#configuring-dbcontextoptions) to configure the context. * Using a connection string per , such as when you use [ASP.NET Core's Identity model](xref:security/authentication/customize_identity_model). For more information, see [Multi-tenancy (EF Core documentation)](/ef/core/miscellaneous/multitenancy). -The recommended approach to create a new with dependencies is to use a factory. EF Core 5.0 or later provides a built-in factory for creating new contexts. +The recommended approach to create a new with dependencies is to use a factory. The sample app implements its own factory in `Data/DbContextFactory.cs`. -The following example configures [SQLite](https://www.sqlite.org/index.html) and enables data logging. The code uses an extension method (`AddDbContextFactory`) to configure the database factory for DI and provide default options: +:::code language="csharp" source="~/../blazor-samples/3.1/BlazorServerEFCoreSample/BlazorServerDbContextExample/Data/DbContextFactory.cs"::: -:::code language="csharp" source="~/../blazor-samples/7.0/BlazorServerEFCoreSample/Program.cs" id="snippet1"::: +In the preceding factory: + +* satisfies any dependencies via the service provider. +* `IDbContextFactory` is available in EF Core ASP.NET Core 5.0 or later, so the interface is [implemented in the sample app for ASP.NET Core 3.x](https://github.com/dotnet/blazor-samples/blob/main/3.1/BlazorServerEFCoreSample/BlazorServerDbContextExample/Data/IDbContextFactory.cs). + +The following example configures [SQLite](https://www.sqlite.org/index.html) and enables data logging. The code uses an extension method to configure the database factory for DI and provide default options: + +:::code language="csharp" source="~/../blazor-samples/3.1/BlazorServerEFCoreSample/BlazorServerDbContextExample/Startup.cs" id="snippet1"::: The factory is injected into components and used to create new `DbContext` instances. -In `Pages/Index.razor` of the [sample app](https://github.com/dotnet/blazor-samples/blob/main/7.0/BlazorServerEFCoreSample/BlazorServerDbContextExample/Pages/Index.razor), `IDbContextFactory` is injected into the component: +In `Pages/Index.razor` of the [sample app](https://github.com/dotnet/blazor-samples/blob/main/3.1/BlazorServerEFCoreSample/BlazorServerDbContextExample/Pages/Index.razor), `IDbContextFactory` is injected into the component: ```razor @inject IDbContextFactory DbFactory @@ -527,10 +522,10 @@ In `Pages/Index.razor` of the [sample app](https://github.com/dotnet/blazor-samp A `DbContext` is created using the factory (`DbFactory`) to delete a contact in the `DeleteContactAsync` method: -:::code language="csharp" source="~/../blazor-samples/7.0/BlazorServerEFCoreSample/Pages/Index.razor" id="snippet1"::: +:::code language="csharp" source="~/../blazor-samples/3.1/BlazorServerEFCoreSample/BlazorServerDbContextExample/Pages/Index.razor" id="snippet1"::: > [!NOTE] -> `Filters` is an injected `IContactFilters`, and `Wrapper` is a [component reference](xref:blazor/components/index#capture-references-to-components) to the `GridWrapper` component. See the `Index` component (`Pages/Index.razor`) in the [sample app](https://github.com/dotnet/blazor-samples/blob/main/6.0/BlazorServerEFCoreSample/Pages/Index.razor). +> `Filters` is an injected `IContactFilters`, and `Wrapper` is a [component reference](xref:blazor/components/index#capture-references-to-components) to the `GridWrapper` component. See the `Index` component (`Pages/Index.razor`) in the [sample app](https://github.com/dotnet/blazor-samples/blob/main/3.1/BlazorServerEFCoreSample/BlazorServerDbContextExample/Pages/Index.razor). ## Scope to the component lifetime @@ -544,11 +539,16 @@ You can use the factory to create a context and track it for the lifetime of the The sample app ensures the context is disposed when the component is disposed: -:::code language="csharp" source="~/../blazor-samples/7.0/BlazorServerEFCoreSample/Pages/EditContact.razor" id="snippet1"::: +:::code language="csharp" source="~/../blazor-samples/3.1/BlazorServerEFCoreSample/BlazorServerDbContextExample/Pages/EditContact.razor" id="snippet1"::: Finally, [`OnInitializedAsync`](xref:blazor/components/lifecycle) is overridden to create a new context. In the sample app, [`OnInitializedAsync`](xref:blazor/components/lifecycle) loads the contact in the same method: -:::code language="csharp" source="~/../blazor-samples/7.0/BlazorServerEFCoreSample/Pages/EditContact.razor" id="snippet2"::: +:::code language="csharp" source="~/../blazor-samples/3.1/BlazorServerEFCoreSample/BlazorServerDbContextExample/Pages/EditContact.razor" id="snippet2"::: + +In the preceding example: + +* When `Busy` is set to `true`, asynchronous operations may begin. When `Busy` is set back to `false`, asynchronous operations should be finished. +* Place additional error handling logic in a `catch` block. ## Enable sensitive data logging diff --git a/aspnetcore/blazor/call-web-api.md b/aspnetcore/blazor/call-web-api.md index 669ffceb69e9..7a139f13ce7f 100644 --- a/aspnetcore/blazor/call-web-api.md +++ b/aspnetcore/blazor/call-web-api.md @@ -5,7 +5,7 @@ description: Learn how to call a web API from Blazor apps. monikerRange: '>= aspnetcore-3.1' ms.author: riande ms.custom: mvc -ms.date: 11/09/2021 +ms.date: 11/08/2022 uid: blazor/call-web-api zone_pivot_groups: blazor-hosting-models --- @@ -13,7 +13,7 @@ zone_pivot_groups: blazor-hosting-models This article describes how to call a web API from a Blazor app. -:::moniker range=">= aspnetcore-6.0 < aspnetcore-7.0" +:::moniker range=">= aspnetcore-7.0" :::zone pivot="webassembly" @@ -379,7 +379,7 @@ else `Pages/TodoRequest.razor`: -:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Pages/call-web-api/TodoRequest.razor"::: +:::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Pages/call-web-api/TodoRequest.razor"::: Blazor WebAssembly's implementation of uses [Fetch API](https://developer.mozilla.org/docs/Web/API/fetch). Fetch API allows the configuration of several [request-specific options](https://developer.mozilla.org/docs/Web/API/fetch#Parameters). Options can be configured with extension methods shown in the following table. @@ -421,7 +421,7 @@ By default, ASP.NET Core apps use ports 5000 (HTTP) and 5001 (HTTPS). To run bot `Pages/CallWebAPI.razor`: -:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Pages/call-web-api/CallWebAPI.razor"::: +:::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Pages/call-web-api/CallWebAPI.razor"::: ## Handle errors @@ -640,7 +640,7 @@ Various network tools are publicly available for testing web API backend apps di :::moniker-end -:::moniker range=">= aspnetcore-5.0 < aspnetcore-6.0" +:::moniker range=">= aspnetcore-6.0 < aspnetcore-7.0" :::zone pivot="webassembly" @@ -668,7 +668,7 @@ Use the following `TodoItem` class with this article's examples if you build the public class TodoItem { public long Id { get; set; } - public string Name { get; set; } + public string? Name { get; set; } public bool IsComplete { get; set; } } ``` @@ -739,7 +739,7 @@ else } @code { - private TodoItem[] todoItems; + private TodoItem[]? todoItems; protected override async Task OnInitializedAsync() => todoItems = await Http.GetFromJsonAsync("api/TodoItems"); @@ -762,7 +762,7 @@ In the following component code, `newItemName` is provided by a bound element of @code { - private string newItemName; + private string? newItemName; private async Task AddItem() { @@ -782,7 +782,7 @@ var content = await response.Content.ReadFromJsonAsync(); sends an HTTP PUT request with JSON-encoded content. -In the following component code, `editItem` values for `Name` and `IsCompleted` are provided by bound elements of the component. The item's `Id` is set when the item is selected in another part of the UI (not shown) and `EditItem` is called. The `SaveItem` method is triggered by selecting the ` @code { - private string id; + private string? id; private TodoItem editItem = new TodoItem(); private void EditItem(long id) @@ -891,7 +891,7 @@ else } @code { - private WeatherForecast[] forecasts; + private WeatherForecast[]? forecasts; protected override async Task OnInitializedAsync() { @@ -991,7 +991,7 @@ else } @code { - private WeatherForecast[] forecasts; + private WeatherForecast[]? forecasts; protected override async Task OnInitializedAsync() { @@ -1006,7 +1006,7 @@ else `Pages/TodoRequest.razor`: -:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Pages/call-web-api/TodoRequest.razor"::: +:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Pages/call-web-api/TodoRequest.razor"::: Blazor WebAssembly's implementation of uses [Fetch API](https://developer.mozilla.org/docs/Web/API/fetch). Fetch API allows the configuration of several [request-specific options](https://developer.mozilla.org/docs/Web/API/fetch#Parameters). Options can be configured with extension methods shown in the following table. @@ -1033,7 +1033,7 @@ For more information on Fetch API options, see [MDN web docs: WindowOrWorkerGlob The following example calls a web API. The example requires a running web API based on the sample app described by the article. This example makes requests to the web API at `https://localhost:10000/api/TodoItems`. If a different web API address is used, update the `ServiceEndpoint` constant value in the component's `@code` block. -The following example makes a [cross-origin resource sharing (CORS)](xref:security/cors) request from `http://localhost:5000` or `https://localhost:5001` to the web API. Add the following CORS middleware configuration to the web API's service's `Startup.Configure` method: +The following example makes a [cross-origin resource sharing (CORS)](xref:security/cors) request from `http://localhost:5000` or `https://localhost:5001` to the web API. Add the following CORS middleware configuration to the web API's service's `Program.cs` file: ```csharp app.UseCors(policy => @@ -1048,7 +1048,7 @@ By default, ASP.NET Core apps use ports 5000 (HTTP) and 5001 (HTTPS). To run bot `Pages/CallWebAPI.razor`: -:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Pages/call-web-api/CallWebAPI.razor"::: +:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Pages/call-web-api/CallWebAPI.razor"::: ## Handle errors @@ -1096,8 +1096,8 @@ else

@code { - private WeatherForecast[] forecasts; - private string exceptionMessage; + private WeatherForecast[]? forecasts; + private string? exceptionMessage; protected override async Task OnInitializedAsync() { @@ -1143,10 +1143,10 @@ For more information, see . A Blazor Server app doesn't include an service by default. Provide an to the app using the [`HttpClient` factory infrastructure](xref:fundamentals/http-requests). -In `Startup.ConfigureServices` of `Startup.cs`: +In `Program.cs`: ```csharp -services.AddHttpClient(); +builder.Services.AddHttpClient(); ``` The following Blazor Server Razor component makes a request to a web API for GitHub branches similar to the *Basic Usage* example in the article. @@ -1243,8 +1243,6 @@ Various network tools are publicly available for testing web API backend apps di [`HttpClientTest` assets in the `dotnet/aspnetcore` GitHub repository](https://github.com/dotnet/aspnetcore/tree/main/src/Components/test/testassets/BasicTestApp/HttpClientTest) -[!INCLUDE[](~/includes/aspnetcore-repo-ref-source-links.md)] - ## Additional resources :::zone pivot="webassembly" @@ -1269,7 +1267,7 @@ Various network tools are publicly available for testing web API backend apps di :::moniker-end -:::moniker range="< aspnetcore-5.0" +:::moniker range=">= aspnetcore-5.0 < aspnetcore-6.0" :::zone pivot="webassembly" @@ -1635,7 +1633,7 @@ else `Pages/TodoRequest.razor`: -:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Pages/call-web-api/TodoRequest.razor"::: +:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Pages/call-web-api/TodoRequest.razor"::: Blazor WebAssembly's implementation of uses [Fetch API](https://developer.mozilla.org/docs/Web/API/fetch). Fetch API allows the configuration of several [request-specific options](https://developer.mozilla.org/docs/Web/API/fetch#Parameters). Options can be configured with extension methods shown in the following table. @@ -1673,11 +1671,11 @@ app.UseCors(policy => Adjust the domains and ports of `WithOrigins` as needed for the Blazor app. For more information, see . -By default, ASP.NET Core apps use ports 5000 (HTTP) and 5001 (HTTPS). To run both apps on the same machine at the same time for testing, use a different port for the web API app (for example, port 10000). For more information on setting the port, see . +By default, ASP.NET Core apps use ports 5000 (HTTP) and 5001 (HTTPS). To run both apps on the same machine at the same time for testing, use a different port for the web API app (for example, port 10000). For more information on setting the port, see . `Pages/CallWebAPI.razor`: -:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Pages/call-web-api/CallWebAPI.razor"::: +:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Pages/call-web-api/CallWebAPI.razor"::: ## Handle errors @@ -1891,14 +1889,14 @@ Various network tools are publicly available for testing web API backend apps di * * * -* [Kestrel HTTPS endpoint configuration](xref:fundamentals/servers/kestrel#endpoint-configuration) +* [Kestrel HTTPS endpoint configuration](xref:fundamentals/servers/kestrel/endpoints) * [Cross Origin Resource Sharing (CORS) at W3C](https://www.w3.org/TR/cors/) :::zone-end :::moniker-end -:::moniker range=">= aspnetcore-7.0" +:::moniker range="< aspnetcore-5.0" :::zone pivot="webassembly" @@ -1926,7 +1924,7 @@ Use the following `TodoItem` class with this article's examples if you build the public class TodoItem { public long Id { get; set; } - public string? Name { get; set; } + public string Name { get; set; } public bool IsComplete { get; set; } } ``` @@ -1997,7 +1995,7 @@ else } @code { - private TodoItem[]? todoItems; + private TodoItem[] todoItems; protected override async Task OnInitializedAsync() => todoItems = await Http.GetFromJsonAsync("api/TodoItems"); @@ -2020,7 +2018,7 @@ In the following component code, `newItemName` is provided by a bound element of @code { - private string? newItemName; + private string newItemName; private async Task AddItem() { @@ -2040,7 +2038,7 @@ var content = await response.Content.ReadFromJsonAsync(); sends an HTTP PUT request with JSON-encoded content. -In the following component code, `editItem` values for `Name` and `IsCompleted` are provided by bound elements of the component. The item's `Id` is set when the item is selected in another part of the UI (not shown) and `EditItem` is called. The `SaveItem` method is triggered by selecting the ` @code { - private string? id; + private string id; private TodoItem editItem = new TodoItem(); private void EditItem(long id) @@ -2149,7 +2147,7 @@ else } @code { - private WeatherForecast[]? forecasts; + private WeatherForecast[] forecasts; protected override async Task OnInitializedAsync() { @@ -2249,7 +2247,7 @@ else } @code { - private WeatherForecast[]? forecasts; + private WeatherForecast[] forecasts; protected override async Task OnInitializedAsync() { @@ -2264,7 +2262,7 @@ else `Pages/TodoRequest.razor`: -:::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Pages/call-web-api/TodoRequest.razor"::: +:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Pages/call-web-api/TodoRequest.razor"::: Blazor WebAssembly's implementation of uses [Fetch API](https://developer.mozilla.org/docs/Web/API/fetch). Fetch API allows the configuration of several [request-specific options](https://developer.mozilla.org/docs/Web/API/fetch#Parameters). Options can be configured with extension methods shown in the following table. @@ -2291,7 +2289,7 @@ For more information on Fetch API options, see [MDN web docs: WindowOrWorkerGlob The following example calls a web API. The example requires a running web API based on the sample app described by the article. This example makes requests to the web API at `https://localhost:10000/api/TodoItems`. If a different web API address is used, update the `ServiceEndpoint` constant value in the component's `@code` block. -The following example makes a [cross-origin resource sharing (CORS)](xref:security/cors) request from `http://localhost:5000` or `https://localhost:5001` to the web API. Add the following CORS middleware configuration to the web API's service's `Program.cs` file: +The following example makes a [cross-origin resource sharing (CORS)](xref:security/cors) request from `http://localhost:5000` or `https://localhost:5001` to the web API. Add the following CORS middleware configuration to the web API's service's `Startup.Configure` method: ```csharp app.UseCors(policy => @@ -2302,11 +2300,11 @@ app.UseCors(policy => Adjust the domains and ports of `WithOrigins` as needed for the Blazor app. For more information, see . -By default, ASP.NET Core apps use ports 5000 (HTTP) and 5001 (HTTPS). To run both apps on the same machine at the same time for testing, use a different port for the web API app (for example, port 10000). For more information on setting the port, see . +By default, ASP.NET Core apps use ports 5000 (HTTP) and 5001 (HTTPS). To run both apps on the same machine at the same time for testing, use a different port for the web API app (for example, port 10000). For more information on setting the port, see . `Pages/CallWebAPI.razor`: -:::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Pages/call-web-api/CallWebAPI.razor"::: +:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Pages/call-web-api/CallWebAPI.razor"::: ## Handle errors @@ -2354,8 +2352,8 @@ else

@code { - private WeatherForecast[]? forecasts; - private string? exceptionMessage; + private WeatherForecast[] forecasts; + private string exceptionMessage; protected override async Task OnInitializedAsync() { @@ -2401,10 +2399,10 @@ For more information, see . A Blazor Server app doesn't include an service by default. Provide an to the app using the [`HttpClient` factory infrastructure](xref:fundamentals/http-requests). -In `Program.cs`: +In `Startup.ConfigureServices` of `Startup.cs`: ```csharp -builder.Services.AddHttpClient(); +services.AddHttpClient(); ``` The following Blazor Server Razor component makes a request to a web API for GitHub branches similar to the *Basic Usage* example in the article. @@ -2501,6 +2499,8 @@ Various network tools are publicly available for testing web API backend apps di [`HttpClientTest` assets in the `dotnet/aspnetcore` GitHub repository](https://github.com/dotnet/aspnetcore/tree/main/src/Components/test/testassets/BasicTestApp/HttpClientTest) +[!INCLUDE[](~/includes/aspnetcore-repo-ref-source-links.md)] + ## Additional resources :::zone pivot="webassembly" @@ -2518,7 +2518,7 @@ Various network tools are publicly available for testing web API backend apps di * * * -* [Kestrel HTTPS endpoint configuration](xref:fundamentals/servers/kestrel/endpoints) +* [Kestrel HTTPS endpoint configuration](xref:fundamentals/servers/kestrel#endpoint-configuration) * [Cross Origin Resource Sharing (CORS) at W3C](https://www.w3.org/TR/cors/) :::zone-end diff --git a/aspnetcore/blazor/components/built-in-components.md b/aspnetcore/blazor/components/built-in-components.md index 8aea1a43b7c1..12d55503f159 100644 --- a/aspnetcore/blazor/components/built-in-components.md +++ b/aspnetcore/blazor/components/built-in-components.md @@ -5,14 +5,14 @@ description: Find information on Razor components provided by the Blazor framewo monikerRange: '>= aspnetcore-3.1' ms.author: riande ms.custom: mvc -ms.date: 06/09/2022 +ms.date: 11/08/2022 uid: blazor/components/built-in-components --- # ASP.NET Core built-in Razor components This article lists the Razor components that are provided by the Blazor framework. -:::moniker range=">= aspnetcore-6.0 < aspnetcore-7.0" +:::moniker range=">= aspnetcore-7.0" The following built-in Razor components are provided by the Blazor framework: @@ -39,20 +39,28 @@ The following built-in Razor components are provided by the Blazor framework: * [`NavLink`](xref:blazor/fundamentals/routing#navlink-and-navmenu-components) * [`NavMenu`](xref:blazor/fundamentals/routing#navlink-and-navmenu-components) * [`PageTitle`](xref:blazor/components/control-head-content) +* [`QuickGrid`†](xref:blazor/components/index#quickgrid-component) * [`Router`](xref:blazor/fundamentals/routing#route-templates) * [`RouteView`](xref:blazor/fundamentals/routing#route-templates) * [`Virtualize`](xref:blazor/components/virtualization) +†Currently, the component is prerelease, experimental, unsupported, and not recommended for production apps. + :::moniker-end -:::moniker range=">= aspnetcore-5.0 < aspnetcore-6.0" +:::moniker range=">= aspnetcore-6.0 < aspnetcore-7.0" -The following built-in Razor components are provided by ASP.NET Core: +The following built-in Razor components are provided by the Blazor framework: * [`App`](xref:blazor/project-structure) * [`Authentication`](xref:blazor/security/webassembly/index#authentication-component) * [`AuthorizeView`](xref:blazor/security/index#authorizeview-component) * [`CascadingValue`](xref:blazor/components/cascading-values-and-parameters#cascadingvalue-component) +* [`DynamicComponent`](xref:blazor/components/dynamiccomponent) +* [`ErrorBoundary`](xref:blazor/fundamentals/handle-errors#error-boundaries) +* [`FocusOnNavigate`](xref:blazor/fundamentals/routing#focus-an-element-on-navigation) +* [`HeadContent`](xref:blazor/components/control-head-content) +* [`HeadOutlet`](xref:blazor/components/control-head-content) * [`InputCheckbox`](xref:blazor/forms-and-input-components#built-in-input-components) * [`InputDate`](xref:blazor/forms-and-input-components#built-in-input-components) * [`InputFile`](xref:blazor/file-uploads) @@ -66,15 +74,16 @@ The following built-in Razor components are provided by ASP.NET Core: * [`MainLayout`](xref:blazor/components/layouts#mainlayout-component) * [`NavLink`](xref:blazor/fundamentals/routing#navlink-and-navmenu-components) * [`NavMenu`](xref:blazor/fundamentals/routing#navlink-and-navmenu-components) +* [`PageTitle`](xref:blazor/components/control-head-content) * [`Router`](xref:blazor/fundamentals/routing#route-templates) * [`RouteView`](xref:blazor/fundamentals/routing#route-templates) * [`Virtualize`](xref:blazor/components/virtualization) :::moniker-end -:::moniker range="< aspnetcore-5.0" +:::moniker range=">= aspnetcore-5.0 < aspnetcore-6.0" -The following built-in Razor components are provided by the Blazor framework: +The following built-in Razor components are provided by ASP.NET Core: * [`App`](xref:blazor/project-structure) * [`Authentication`](xref:blazor/security/webassembly/index#authentication-component) @@ -82,6 +91,7 @@ The following built-in Razor components are provided by the Blazor framework: * [`CascadingValue`](xref:blazor/components/cascading-values-and-parameters#cascadingvalue-component) * [`InputCheckbox`](xref:blazor/forms-and-input-components#built-in-input-components) * [`InputDate`](xref:blazor/forms-and-input-components#built-in-input-components) +* [`InputFile`](xref:blazor/file-uploads) * [`InputNumber`](xref:blazor/forms-and-input-components#built-in-input-components) * [`InputRadio`](xref:blazor/forms-and-input-components#built-in-input-components) * [`InputRadioGroup`](xref:blazor/forms-and-input-components#built-in-input-components) @@ -94,10 +104,11 @@ The following built-in Razor components are provided by the Blazor framework: * [`NavMenu`](xref:blazor/fundamentals/routing#navlink-and-navmenu-components) * [`Router`](xref:blazor/fundamentals/routing#route-templates) * [`RouteView`](xref:blazor/fundamentals/routing#route-templates) +* [`Virtualize`](xref:blazor/components/virtualization) :::moniker-end -:::moniker range=">= aspnetcore-7.0" +:::moniker range="< aspnetcore-5.0" The following built-in Razor components are provided by the Blazor framework: @@ -105,14 +116,8 @@ The following built-in Razor components are provided by the Blazor framework: * [`Authentication`](xref:blazor/security/webassembly/index#authentication-component) * [`AuthorizeView`](xref:blazor/security/index#authorizeview-component) * [`CascadingValue`](xref:blazor/components/cascading-values-and-parameters#cascadingvalue-component) -* [`DynamicComponent`](xref:blazor/components/dynamiccomponent) -* [`ErrorBoundary`](xref:blazor/fundamentals/handle-errors#error-boundaries) -* [`FocusOnNavigate`](xref:blazor/fundamentals/routing#focus-an-element-on-navigation) -* [`HeadContent`](xref:blazor/components/control-head-content) -* [`HeadOutlet`](xref:blazor/components/control-head-content) * [`InputCheckbox`](xref:blazor/forms-and-input-components#built-in-input-components) * [`InputDate`](xref:blazor/forms-and-input-components#built-in-input-components) -* [`InputFile`](xref:blazor/file-uploads) * [`InputNumber`](xref:blazor/forms-and-input-components#built-in-input-components) * [`InputRadio`](xref:blazor/forms-and-input-components#built-in-input-components) * [`InputRadioGroup`](xref:blazor/forms-and-input-components#built-in-input-components) @@ -123,12 +128,7 @@ The following built-in Razor components are provided by the Blazor framework: * [`MainLayout`](xref:blazor/components/layouts#mainlayout-component) * [`NavLink`](xref:blazor/fundamentals/routing#navlink-and-navmenu-components) * [`NavMenu`](xref:blazor/fundamentals/routing#navlink-and-navmenu-components) -* [`PageTitle`](xref:blazor/components/control-head-content) -* [`QuickGrid`†](xref:blazor/components/index#quickgrid-component) * [`Router`](xref:blazor/fundamentals/routing#route-templates) * [`RouteView`](xref:blazor/fundamentals/routing#route-templates) -* [`Virtualize`](xref:blazor/components/virtualization) - -†Currently, the component is prerelease, experimental, unsupported, and not recommended for production apps. :::moniker-end diff --git a/aspnetcore/blazor/components/cascading-values-and-parameters.md b/aspnetcore/blazor/components/cascading-values-and-parameters.md index 614e20384d12..170f9adfe97f 100644 --- a/aspnetcore/blazor/components/cascading-values-and-parameters.md +++ b/aspnetcore/blazor/components/cascading-values-and-parameters.md @@ -5,14 +5,14 @@ description: Learn how to flow data from an ancestor Razor component to descende monikerRange: '>= aspnetcore-3.1' ms.author: riande ms.custom: mvc -ms.date: 11/09/2021 +ms.date: 11/08/2022 uid: blazor/components/cascading-values-and-parameters --- # ASP.NET Core Blazor cascading values and parameters This article explains how to flow data from an ancestor Razor component to descendent components. -:::moniker range=">= aspnetcore-6.0 < aspnetcore-7.0" +:::moniker range=">= aspnetcore-7.0" *Cascading values and parameters* provide a convenient way to flow data down a component hierarchy from an ancestor component to any number of descendent components. Unlike [Component parameters](xref:blazor/components/index#component-parameters), cascading values and parameters don't require an attribute assignment for each descendent component where the data is consumed. Cascading values and parameters also allow components to coordinate with each other across a component hierarchy. @@ -29,13 +29,13 @@ The following `ThemeInfo` C# class is placed in a folder named `UIThemeClasses` `UIThemeClasses/ThemeInfo.cs`: -:::code language="csharp" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/UIThemeClasses/ThemeInfo.cs"::: +:::code language="csharp" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/UIThemeClasses/ThemeInfo.cs"::: The following [layout component](xref:blazor/components/layouts) specifies theme information (`ThemeInfo`) as a cascading value for all components that make up the layout body of the property. `ButtonClass` is assigned a value of [`btn-success`](https://getbootstrap.com/docs/5.0/components/buttons/), which is a Bootstrap button style. Any descendent component in the component hierarchy can use the `ButtonClass` property through the `ThemeInfo` cascading value. `Shared/MainLayout.razor`: -:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Shared/MainLayout.razor" highlight="2,10-14,19"::: +:::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Shared/MainLayout.razor"::: ## `[CascadingParameter]` attribute @@ -45,7 +45,7 @@ The following component binds the `ThemeInfo` cascading value to a cascading par `Pages/ThemedCounter.razor`: -:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Pages/ThemedCounter.razor"::: +:::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Pages/ThemedCounter.razor"::: Similar to a regular component parameter, components accepting a cascading parameter are rerendered when the cascading value is changed. For instance, configuring a different theme instance causes the `ThemedCounter` component from the [`CascadingValue` component](#cascadingvalue-component) section to rerender: @@ -53,10 +53,14 @@ Similar to a regular component parameter, components accepting a cascading param ```razor
+
+ About +
+ -
+
@Body -
+
@@ -256,7 +260,7 @@ The following `ExampleTabSet` component uses the `TabSet` component, which conta :::moniker-end -:::moniker range=">= aspnetcore-5.0 < aspnetcore-6.0" +:::moniker range=">= aspnetcore-6.0 < aspnetcore-7.0" *Cascading values and parameters* provide a convenient way to flow data down a component hierarchy from an ancestor component to any number of descendent components. Unlike [Component parameters](xref:blazor/components/index#component-parameters), cascading values and parameters don't require an attribute assignment for each descendent component where the data is consumed. Cascading values and parameters also allow components to coordinate with each other across a component hierarchy. @@ -273,13 +277,13 @@ The following `ThemeInfo` C# class is placed in a folder named `UIThemeClasses` `UIThemeClasses/ThemeInfo.cs`: -:::code language="csharp" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/UIThemeClasses/ThemeInfo.cs"::: +:::code language="csharp" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/UIThemeClasses/ThemeInfo.cs"::: The following [layout component](xref:blazor/components/layouts) specifies theme information (`ThemeInfo`) as a cascading value for all components that make up the layout body of the property. `ButtonClass` is assigned a value of [`btn-success`](https://getbootstrap.com/docs/5.0/components/buttons/), which is a Bootstrap button style. Any descendent component in the component hierarchy can use the `ButtonClass` property through the `ThemeInfo` cascading value. `Shared/MainLayout.razor`: -:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Shared/MainLayout.razor" highlight="2,10-14,19"::: +:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Shared/MainLayout.razor" highlight="2,10-14,19"::: ## `[CascadingParameter]` attribute @@ -289,7 +293,33 @@ The following component binds the `ThemeInfo` cascading value to a cascading par `Pages/ThemedCounter.razor`: -:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Pages/ThemedCounter.razor" highlight="2,15-17,23-24"::: +:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Pages/ThemedCounter.razor"::: + +Similar to a regular component parameter, components accepting a cascading parameter are rerendered when the cascading value is changed. For instance, configuring a different theme instance causes the `ThemedCounter` component from the [`CascadingValue` component](#cascadingvalue-component) section to rerender: + +`Shared/MainLayout.razor`: + +```razor +
+ +
+ @Body +
+
+ +
+ +@code { + private ThemeInfo theme = new() { ButtonClass = "btn-success" }; + + private void ChangeToDarkTheme() + { + theme = new() { ButtonClass = "btn-darkmode-success" }; + } +} +``` + + can be used to indicate that a cascading parameter doesn't change after initialization. ## Cascade multiple values @@ -305,26 +335,22 @@ In the following example, two [`CascadingValue`](xref:Microsoft.AspNetCore.Compo @code { - private CascadingType parentCascadeParameter1; + private CascadingType? parentCascadeParameter1; [Parameter] - public CascadingType ParentCascadeParameter2 { get; set; } - - ... + public CascadingType? ParentCascadeParameter2 { get; set; } } ``` In a descendant component, the cascaded parameters receive their cascaded values from the ancestor component by : ```razor -... - @code { [CascadingParameter(Name = "CascadeParam1")] - protected CascadingType ChildCascadeParameter1 { get; set; } - + protected CascadingType? ChildCascadeParameter1 { get; set; } + [CascadingParameter(Name = "CascadeParam2")] - protected CascadingType ChildCascadeParameter2 { get; set; } + protected CascadingType? ChildCascadeParameter2 { get; set; } } ``` @@ -379,13 +405,13 @@ Child `Tab` components aren't explicitly passed as parameters to the `TabSet`. I @code { [Parameter] - public RenderFragment ChildContent { get; set; } + public RenderFragment? ChildContent { get; set; } - public ITab ActiveTab { get; private set; } + public ITab? ActiveTab { get; private set; } public void AddTab(ITab tab) { - if (ActiveTab == null) + if (ActiveTab is null) { SetActiveTab(tab); } @@ -418,25 +444,25 @@ Descendent `Tab` components capture the containing `TabSet` as a cascading param @code { [CascadingParameter] - public TabSet ContainerTabSet { get; set; } + public TabSet? ContainerTabSet { get; set; } [Parameter] - public string Title { get; set; } + public string? Title { get; set; } [Parameter] - public RenderFragment ChildContent { get; set; } + public RenderFragment? ChildContent { get; set; } - private string TitleCssClass => - ContainerTabSet.ActiveTab == this ? "active" : null; + private string? TitleCssClass => + ContainerTabSet?.ActiveTab == this ? "active" : null; protected override void OnInitialized() { - ContainerTabSet.AddTab(this); + ContainerTabSet?.AddTab(this); } private void ActivateTab() { - ContainerTabSet.SetActiveTab(this); + ContainerTabSet?.SetActiveTab(this); } } ``` @@ -478,7 +504,7 @@ The following `ExampleTabSet` component uses the `TabSet` component, which conta :::moniker-end -:::moniker range="< aspnetcore-5.0" +:::moniker range=">= aspnetcore-5.0 < aspnetcore-6.0" *Cascading values and parameters* provide a convenient way to flow data down a component hierarchy from an ancestor component to any number of descendent components. Unlike [Component parameters](xref:blazor/components/index#component-parameters), cascading values and parameters don't require an attribute assignment for each descendent component where the data is consumed. Cascading values and parameters also allow components to coordinate with each other across a component hierarchy. @@ -495,13 +521,13 @@ The following `ThemeInfo` C# class is placed in a folder named `UIThemeClasses` `UIThemeClasses/ThemeInfo.cs`: -:::code language="csharp" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/UIThemeClasses/ThemeInfo.cs"::: +:::code language="csharp" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/UIThemeClasses/ThemeInfo.cs"::: The following [layout component](xref:blazor/components/layouts) specifies theme information (`ThemeInfo`) as a cascading value for all components that make up the layout body of the property. `ButtonClass` is assigned a value of [`btn-success`](https://getbootstrap.com/docs/5.0/components/buttons/), which is a Bootstrap button style. Any descendent component in the component hierarchy can use the `ButtonClass` property through the `ThemeInfo` cascading value. `Shared/MainLayout.razor`: -:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Shared/MainLayout.razor" highlight="2,9-13,17"::: +:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Shared/MainLayout.razor" highlight="2,10-14,19"::: ## `[CascadingParameter]` attribute @@ -511,7 +537,7 @@ The following component binds the `ThemeInfo` cascading value to a cascading par `Pages/ThemedCounter.razor`: -:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Pages/ThemedCounter.razor" highlight="2,15-17,23-24"::: +:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Pages/ThemedCounter.razor" highlight="2,15-17,23-24"::: ## Cascade multiple values @@ -700,7 +726,7 @@ The following `ExampleTabSet` component uses the `TabSet` component, which conta :::moniker-end -:::moniker range=">= aspnetcore-7.0" +:::moniker range="< aspnetcore-5.0" *Cascading values and parameters* provide a convenient way to flow data down a component hierarchy from an ancestor component to any number of descendent components. Unlike [Component parameters](xref:blazor/components/index#component-parameters), cascading values and parameters don't require an attribute assignment for each descendent component where the data is consumed. Cascading values and parameters also allow components to coordinate with each other across a component hierarchy. @@ -717,13 +743,13 @@ The following `ThemeInfo` C# class is placed in a folder named `UIThemeClasses` `UIThemeClasses/ThemeInfo.cs`: -:::code language="csharp" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/UIThemeClasses/ThemeInfo.cs"::: +:::code language="csharp" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/UIThemeClasses/ThemeInfo.cs"::: The following [layout component](xref:blazor/components/layouts) specifies theme information (`ThemeInfo`) as a cascading value for all components that make up the layout body of the property. `ButtonClass` is assigned a value of [`btn-success`](https://getbootstrap.com/docs/5.0/components/buttons/), which is a Bootstrap button style. Any descendent component in the component hierarchy can use the `ButtonClass` property through the `ThemeInfo` cascading value. `Shared/MainLayout.razor`: -:::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Shared/MainLayout.razor"::: +:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Shared/MainLayout.razor" highlight="2,9-13,17"::: ## `[CascadingParameter]` attribute @@ -733,37 +759,7 @@ The following component binds the `ThemeInfo` cascading value to a cascading par `Pages/ThemedCounter.razor`: -:::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Pages/ThemedCounter.razor"::: - -Similar to a regular component parameter, components accepting a cascading parameter are rerendered when the cascading value is changed. For instance, configuring a different theme instance causes the `ThemedCounter` component from the [`CascadingValue` component](#cascadingvalue-component) section to rerender: - -`Shared/MainLayout.razor`: - -```razor -
-
- About -
- - -
- @Body -
-
- -
- -@code { - private ThemeInfo theme = new() { ButtonClass = "btn-success" }; - - private void ChangeToDarkTheme() - { - theme = new() { ButtonClass = "btn-darkmode-success" }; - } -} -``` - - can be used to indicate that a cascading parameter doesn't change after initialization. +:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Pages/ThemedCounter.razor" highlight="2,15-17,23-24"::: ## Cascade multiple values @@ -779,22 +775,26 @@ In the following example, two [`CascadingValue`](xref:Microsoft.AspNetCore.Compo @code { - private CascadingType? parentCascadeParameter1; + private CascadingType parentCascadeParameter1; [Parameter] - public CascadingType? ParentCascadeParameter2 { get; set; } + public CascadingType ParentCascadeParameter2 { get; set; } + + ... } ``` In a descendant component, the cascaded parameters receive their cascaded values from the ancestor component by : ```razor +... + @code { [CascadingParameter(Name = "CascadeParam1")] - protected CascadingType? ChildCascadeParameter1 { get; set; } - + protected CascadingType ChildCascadeParameter1 { get; set; } + [CascadingParameter(Name = "CascadeParam2")] - protected CascadingType? ChildCascadeParameter2 { get; set; } + protected CascadingType ChildCascadeParameter2 { get; set; } } ``` @@ -849,13 +849,13 @@ Child `Tab` components aren't explicitly passed as parameters to the `TabSet`. I @code { [Parameter] - public RenderFragment? ChildContent { get; set; } + public RenderFragment ChildContent { get; set; } - public ITab? ActiveTab { get; private set; } + public ITab ActiveTab { get; private set; } public void AddTab(ITab tab) { - if (ActiveTab is null) + if (ActiveTab == null) { SetActiveTab(tab); } @@ -888,25 +888,25 @@ Descendent `Tab` components capture the containing `TabSet` as a cascading param @code { [CascadingParameter] - public TabSet? ContainerTabSet { get; set; } + public TabSet ContainerTabSet { get; set; } [Parameter] - public string? Title { get; set; } + public string Title { get; set; } [Parameter] - public RenderFragment? ChildContent { get; set; } + public RenderFragment ChildContent { get; set; } - private string? TitleCssClass => - ContainerTabSet?.ActiveTab == this ? "active" : null; + private string TitleCssClass => + ContainerTabSet.ActiveTab == this ? "active" : null; protected override void OnInitialized() { - ContainerTabSet?.AddTab(this); + ContainerTabSet.AddTab(this); } private void ActivateTab() { - ContainerTabSet?.SetActiveTab(this); + ContainerTabSet.SetActiveTab(this); } } ``` diff --git a/aspnetcore/blazor/components/class-libraries.md b/aspnetcore/blazor/components/class-libraries.md index e8f0f9ffffa1..549e7740f39b 100644 --- a/aspnetcore/blazor/components/class-libraries.md +++ b/aspnetcore/blazor/components/class-libraries.md @@ -5,7 +5,7 @@ description: Discover how components can be included in Blazor apps from an exte monikerRange: '>= aspnetcore-3.1' ms.author: riande ms.custom: mvc -ms.date: 11/09/2021 +ms.date: 11/08/2022 uid: blazor/components/class-libraries --- # Consume ASP.NET Core Razor components from a Razor class library (RCL) diff --git a/aspnetcore/blazor/components/control-head-content.md b/aspnetcore/blazor/components/control-head-content.md index a18d8661c616..512adb199f05 100644 --- a/aspnetcore/blazor/components/control-head-content.md +++ b/aspnetcore/blazor/components/control-head-content.md @@ -5,14 +5,14 @@ description: Learn how to control head content in Blazor apps, including how to monikerRange: '>= aspnetcore-6.0' ms.author: riande ms.custom: mvc -ms.date: 11/09/2021 +ms.date: 11/08/2022 uid: blazor/components/control-head-content --- # Control `` content in ASP.NET Core Blazor apps Razor components can modify the HTML `` element content of a page, including setting the page's title (`` element) and modifying metadata (`<meta>` elements). -:::moniker range="< aspnetcore-7.0" +:::moniker range=">= aspnetcore-7.0" ## Control `<head>` content in a Razor component @@ -24,27 +24,7 @@ The following example sets the page's title and description using Razor. `Pages/ControlHeadContent.razor`: -:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Pages/control-head-content/ControlHeadContent.razor" highlight="13,15-17"::: - -## Control `<head>` content during prerendering - -*This section applies to prerendered Blazor WebAssembly apps and Blazor Server apps.* - -When [Razor components are prerendered](xref:blazor/components/prerendering-and-integration), the use of a layout page (`_Layout.cshtml`) is required to control `<head>` content with the <xref:Microsoft.AspNetCore.Components.Web.PageTitle> and <xref:Microsoft.AspNetCore.Components.Web.HeadContent> components. The reason for this requirement is that components that control `<head>` content must be rendered before the layout with the <xref:Microsoft.AspNetCore.Components.Web.HeadOutlet> component. **This order of rendering is required to control head content.** - -If the shared `_Layout.cshtml` file doesn't have a [Component Tag Helper](xref:mvc/views/tag-helpers/builtin-th/component-tag-helper) for a <xref:Microsoft.AspNetCore.Components.Web.HeadOutlet> component, add it to the `<head>` elements. - -In a **required**, shared `_Layout.cshtml` file of a Blazor Server app or Razor Pages/MVC app that embeds components into pages or views: - -```cshtml -<component type="typeof(HeadOutlet)" render-mode="ServerPrerendered" /> -``` - -In a **required**, shared `_Layout.cshtml` file of a prerendered hosted Blazor WebAssembly app: - -```cshtml -<component type="typeof(HeadOutlet)" render-mode="WebAssemblyPrerendered" /> -``` +:::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Pages/control-head-content/ControlHeadContent.razor" highlight="13,15-17"::: ## `HeadOutlet` component @@ -58,7 +38,7 @@ builder.RootComponents.Add<HeadOutlet>("head::after"); When the [`::after` pseudo-selector](https://developer.mozilla.org/docs/Web/CSS/::after) is specified, the contents of the root component are appended to the existing head contents instead of replacing the content. This allows the app to retain static head content in `wwwroot/index.html` without having to repeat the content in the app's Razor components. -In Blazor Server apps created from the Blazor Server project template, a [Component Tag Helper](xref:mvc/views/tag-helpers/builtin-th/component-tag-helper) renders `<head>` content for the <xref:Microsoft.AspNetCore.Components.Web.HeadOutlet> component in `Pages/_Layout.cshtml`: +In Blazor Server apps created from the Blazor Server project template, a [Component Tag Helper](xref:mvc/views/tag-helpers/builtin-th/component-tag-helper) renders `<head>` content for the <xref:Microsoft.AspNetCore.Components.Web.HeadOutlet> component in `Pages/_Host.cshtml`: ```cshtml <head> @@ -91,7 +71,7 @@ Mozilla MDN Web Docs documentation: :::moniker-end -:::moniker range=">= aspnetcore-7.0" +:::moniker range="< aspnetcore-7.0" ## Control `<head>` content in a Razor component @@ -103,7 +83,27 @@ The following example sets the page's title and description using Razor. `Pages/ControlHeadContent.razor`: -:::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Pages/control-head-content/ControlHeadContent.razor" highlight="13,15-17"::: +:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Pages/control-head-content/ControlHeadContent.razor" highlight="13,15-17"::: + +## Control `<head>` content during prerendering + +*This section applies to prerendered Blazor WebAssembly apps and Blazor Server apps.* + +When [Razor components are prerendered](xref:blazor/components/prerendering-and-integration), the use of a layout page (`_Layout.cshtml`) is required to control `<head>` content with the <xref:Microsoft.AspNetCore.Components.Web.PageTitle> and <xref:Microsoft.AspNetCore.Components.Web.HeadContent> components. The reason for this requirement is that components that control `<head>` content must be rendered before the layout with the <xref:Microsoft.AspNetCore.Components.Web.HeadOutlet> component. **This order of rendering is required to control head content.** + +If the shared `_Layout.cshtml` file doesn't have a [Component Tag Helper](xref:mvc/views/tag-helpers/builtin-th/component-tag-helper) for a <xref:Microsoft.AspNetCore.Components.Web.HeadOutlet> component, add it to the `<head>` elements. + +In a **required**, shared `_Layout.cshtml` file of a Blazor Server app or Razor Pages/MVC app that embeds components into pages or views: + +```cshtml +<component type="typeof(HeadOutlet)" render-mode="ServerPrerendered" /> +``` + +In a **required**, shared `_Layout.cshtml` file of a prerendered hosted Blazor WebAssembly app: + +```cshtml +<component type="typeof(HeadOutlet)" render-mode="WebAssemblyPrerendered" /> +``` ## `HeadOutlet` component @@ -117,7 +117,7 @@ builder.RootComponents.Add<HeadOutlet>("head::after"); When the [`::after` pseudo-selector](https://developer.mozilla.org/docs/Web/CSS/::after) is specified, the contents of the root component are appended to the existing head contents instead of replacing the content. This allows the app to retain static head content in `wwwroot/index.html` without having to repeat the content in the app's Razor components. -In Blazor Server apps created from the Blazor Server project template, a [Component Tag Helper](xref:mvc/views/tag-helpers/builtin-th/component-tag-helper) renders `<head>` content for the <xref:Microsoft.AspNetCore.Components.Web.HeadOutlet> component in `Pages/_Host.cshtml`: +In Blazor Server apps created from the Blazor Server project template, a [Component Tag Helper](xref:mvc/views/tag-helpers/builtin-th/component-tag-helper) renders `<head>` content for the <xref:Microsoft.AspNetCore.Components.Web.HeadOutlet> component in `Pages/_Layout.cshtml`: ```cshtml <head> @@ -148,5 +148,4 @@ Mozilla MDN Web Docs documentation: * [`<title>`: The Document Title element](https://developer.mozilla.org/docs/Web/HTML/Element/title) * [`<meta>`: The metadata element](https://developer.mozilla.org/docs/Web/HTML/Element/meta) - :::moniker-end diff --git a/aspnetcore/blazor/components/css-isolation.md b/aspnetcore/blazor/components/css-isolation.md index 4df31c7ff1e9..368da218b4cc 100644 --- a/aspnetcore/blazor/components/css-isolation.md +++ b/aspnetcore/blazor/components/css-isolation.md @@ -5,7 +5,7 @@ description: Learn how CSS isolation scopes CSS to Razor components, which can s monikerRange: '>= aspnetcore-5.0' ms.author: riande ms.custom: mvc -ms.date: 11/09/2021 +ms.date: 11/08/2022 uid: blazor/components/css-isolation --- # ASP.NET Core Blazor CSS isolation diff --git a/aspnetcore/blazor/components/data-binding.md b/aspnetcore/blazor/components/data-binding.md index e6becc6a9aad..a0162911faed 100644 --- a/aspnetcore/blazor/components/data-binding.md +++ b/aspnetcore/blazor/components/data-binding.md @@ -5,14 +5,14 @@ description: Learn about data binding features for Razor components and Document monikerRange: '>= aspnetcore-3.1' ms.author: riande ms.custom: mvc -ms.date: 11/09/2021 +ms.date: 11/08/2022 uid: blazor/components/data-binding --- # ASP.NET Core Blazor data binding This article explains data binding features for Razor components and Document Object Model (DOM) elements in Blazor apps. -:::moniker range=">= aspnetcore-6.0 < aspnetcore-7.0" +:::moniker range=">= aspnetcore-7.0" Razor components provide data binding features with the [`@bind`](xref:mvc/views/razor#bind) Razor directive attribute with a field, property, or Razor expression value. @@ -25,7 +25,7 @@ When an `<input>` element loses focus, its bound field or property is updated. `Pages/Bind.razor`: -:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Pages/data-binding/Bind.razor" highlight="4,8"::: +:::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Pages/data-binding/Bind.razor" highlight="4,8"::: The text box is updated in the UI only when the component is rendered, not in response to changing the field's or property's value. Since components render themselves after event handler code executes, field and property updates are usually reflected in the UI immediately after an event handler is triggered. @@ -33,7 +33,7 @@ As a demonstration of how data binding composes in HTML, the following example b `Pages/BindTheory.razor`: -:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Pages/data-binding/BindTheory.razor" highlight="12-14"::: +:::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Pages/data-binding/BindTheory.razor" highlight="12-14"::: When the `BindTheory` component is rendered, the `value` of the HTML demonstration `<input>` element comes from the `InputValue` property. When the user enters a value in the text box and changes element focus, the `onchange` event is fired and the `InputValue` property is set to the changed value. In reality, code execution is more complex because [`@bind`](xref:mvc/views/razor#bind) handles cases where type conversions are performed. In general, [`@bind`](xref:mvc/views/razor#bind) associates the current value of an expression with a `value` attribute and handles changes using the registered handler. @@ -41,12 +41,59 @@ Bind a property or field on other [Document Object Model (DOM)](https://develope `Page/BindEvent.razor`: -:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Pages/data-binding/BindEvent.razor" highlight="4"::: +:::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Pages/data-binding/BindEvent.razor" highlight="4"::: + +To execute asynchronous logic after binding, use `@bind:after="{EVENT}"` with a DOM event for the `{EVENT}` placeholder. An assigned C# method isn't executed until the bound value is assigned synchronously. + +In the following example: + +* The `<input>` element's `value` is bound to the value of `searchText` synchronously. +* After each keystroke (`onchange` event) in the field, the `PerformSearch` method executes asynchronously. +* `PerformSearch` calls a service with an asynchronous method (`FetchAsync`) to return search results. + +```razor +@inject ISearchService SearchService + +<input @bind="searchText" @bind:after="PerformSearch" /> + +@code { + private string? searchText; + private string[]? searchResult; + + private async Task PerformSearch() + { + searchResult = await SearchService.FetchAsync(searchText); + } +} +``` + +Components support two-way data binding by defining a pair of parameters: + +* `@bind:get`: Specifies the value to bind. +* `@bind:set`: Specifies a callback for when the value changes. + +The `@bind:get` and `@bind:set` modifiers are always used together. + +Example: + +```razor +<input @bind:get="Value" @bind:set="ValueChanged" /> + +@code { + [Parameter] + public string? Value { get; set; } + + [Parameter] + public EventCallback<string> ValueChanged { get; set; } +} +``` + +For another example use of `@bind:get` and `@bind:set`, see the [Bind across more than two components](#bind-across-more-than-two-components) section later in this article. Razor attribute binding is case sensitive: -* `@bind` and `@bind:event` are valid. -* `@Bind`/`@Bind:Event` (capital letters `B` and `E`) or `@BIND`/`@BIND:EVENT` (all capital letters) **are invalid**. +* `@bind`, `@bind:event`, and `@bind:after` are valid. +* `@Bind`/`@bind:Event`/`@bind:aftEr` (capital letters) or `@BIND`/`@BIND:EVENT`/`@BIND:AFTER` (all capital letters) **are invalid**. ## Multiple option selection with `<select>` elements @@ -131,7 +178,7 @@ Consider the following component, where an `<input>` element is bound to an `int `Pages/UnparsableValues.razor`: -:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Pages/data-binding/UnparsableValues.razor" highlight="4,12"::: +:::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Pages/data-binding/UnparsableValues.razor" highlight="4,12"::: By default, binding applies to the element's `onchange` event. If the user updates the value of the text box's entry to `123.45` and changes the focus, the element's value is reverted to `123` when `onchange` fires. When the value `123.45` is rejected in favor of the original value of `123`, the user understands that their value wasn't accepted. @@ -149,7 +196,7 @@ Data binding works with a single <xref:System.DateTime> format string using `@bi `Pages/DateBinding.razor`: -:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Pages/data-binding/DateBinding.razor" highlight="6"::: +:::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Pages/data-binding/DateBinding.razor" highlight="6"::: In the preceding code, the `<input>` element's field type (`type` attribute) defaults to `text`. @@ -172,7 +219,7 @@ Specifying a format for the `date` field type isn't recommended because Blazor h `Pages/DecimalBinding.razor`: -:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Pages/data-binding/DecimalBinding.razor" highlight="7,21-31"::: +:::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Pages/data-binding/DecimalBinding.razor" highlight="7,21-31"::: ## Binding with component parameters @@ -190,7 +237,7 @@ The following `ChildBind` component has a `Year` component parameter and an <xre `Shared/ChildBind.razor`: -:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Shared/data-binding/ChildBind.razor" highlight="14-15,17-18,22"::: +:::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Shared/data-binding/ChildBind.razor" highlight="14-15,17-18,22"::: For more information on events and <xref:Microsoft.AspNetCore.Components.EventCallback%601>, see the *EventCallback* section of the <xref:blazor/components/event-handling#eventcallback> article. @@ -198,7 +245,22 @@ In the following `Parent1` component, the `year` field is bound to the `Year` pa `Pages/Parent1.razor`: -:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Pages/data-binding/Parent1.razor" highlight="9"::: +:::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Pages/data-binding/Parent1.razor" highlight="9"::: + +Component parameter binding can also trigger `@bind:after` events. In the following example, the `YearUpdated` method executes asynchronously after binding the `Year` component parameter. + +```razor +<ChildBind @bind-Year="year" @bind-Year:after="YearUpdated" /> + +@code { + ... + + private async Task YearUpdated() + { + ... = await ...; + } +} +``` By convention, a property can be bound to a corresponding event handler by including an `@bind-{PROPERTY}:event` attribute assigned to the handler, where the `{PROPERTY}` placeholder is the property. `<ChildBind @bind-Year="year" />` is equivalent to writing: @@ -214,13 +276,13 @@ In a more sophisticated and real-world example, the following `PasswordEntry` co `Shared/PasswordEntry.razor`: -:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Shared/data-binding/PasswordEntry.razor" highlight="7-10,13,23-24,26-27,36-39"::: +:::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Shared/data-binding/PasswordEntry.razor" highlight="7-10,13,23-24,26-27,36-39"::: The `PasswordEntry` component is used in another component, such as the following `PasswordBinding` component example. `Pages/PasswordBinding.razor`: -:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Pages/data-binding/PasswordBinding.razor" highlight="5"::: +:::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Pages/data-binding/PasswordBinding.razor" highlight="5"::: When the `PasswordBinding` component is initially rendered, the `password` value of `Not set` is displayed in the UI. After initial rendering, the value of `password` reflects changes made to the `Password` component parameter value in the `PasswordEntry` component. @@ -231,7 +293,13 @@ Perform checks or trap errors in the handler. The following revised `PasswordEnt `Shared/PasswordEntry.razor`: -:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Shared/data-binding/PasswordEntry2.razor" highlight="35-46"::: +:::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Shared/data-binding/PasswordEntry2.razor" highlight="35-46"::: + +In the following example, the `PasswordUpdated` method executes asynchronously after binding the `Password` component parameter: + +```razor +<PasswordEntry @bind-Password="password" @bind-Password:after="PasswordUpdated" /> +``` ## Bind across more than two components @@ -244,18 +312,20 @@ A common and recommended approach is to only store the underlying data in the pa `Pages/Parent2.razor`: -:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Pages/data-binding/Parent2.razor"::: +:::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Pages/data-binding/Parent2.razor"::: -`Shared/NestedChild.razor`: +In the following `NestedChild` component, the `NestedGrandchild` component: -:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Shared/data-binding/NestedChild.razor"::: +* Assigns the value of `ChildMessage` to `GrandchildMessage` with `@bind:get` syntax. +* Updates `GrandchildMessage` when `ChildMessageChanged` executes with `@bind:set` syntax. -> [!WARNING] -> Generally, avoid creating components that write directly to their own component parameters. The preceding `NestedChild` component makes use of a `BoundValue` property instead of writing directly to its `ChildMessage` parameter. For more information, see <xref:blazor/components/index#overwritten-parameters>. +`Shared/NestedChild.razor`: + +:::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Shared/data-binding/NestedChild.razor"::: `Shared/NestedGrandchild.razor`: -:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Shared/data-binding/NestedGrandchild.razor"::: +:::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Shared/data-binding/NestedGrandchild.razor"::: For an alternative approach suited to sharing data in memory and across components that aren't necessarily nested, see <xref:blazor/state-management>. @@ -270,7 +340,7 @@ For an alternative approach suited to sharing data in memory and across componen :::moniker-end -:::moniker range=">= aspnetcore-5.0 < aspnetcore-6.0" +:::moniker range=">= aspnetcore-6.0 < aspnetcore-7.0" Razor components provide data binding features with the [`@bind`](xref:mvc/views/razor#bind) Razor directive attribute with a field, property, or Razor expression value. @@ -283,7 +353,7 @@ When an `<input>` element loses focus, its bound field or property is updated. `Pages/Bind.razor`: -:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Pages/data-binding/Bind.razor" highlight="4,8"::: +:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Pages/data-binding/Bind.razor" highlight="4,8"::: The text box is updated in the UI only when the component is rendered, not in response to changing the field's or property's value. Since components render themselves after event handler code executes, field and property updates are usually reflected in the UI immediately after an event handler is triggered. @@ -291,7 +361,7 @@ As a demonstration of how data binding composes in HTML, the following example b `Pages/BindTheory.razor`: -:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Pages/data-binding/BindTheory.razor" highlight="12-14"::: +:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Pages/data-binding/BindTheory.razor" highlight="12-14"::: When the `BindTheory` component is rendered, the `value` of the HTML demonstration `<input>` element comes from the `InputValue` property. When the user enters a value in the text box and changes element focus, the `onchange` event is fired and the `InputValue` property is set to the changed value. In reality, code execution is more complex because [`@bind`](xref:mvc/views/razor#bind) handles cases where type conversions are performed. In general, [`@bind`](xref:mvc/views/razor#bind) associates the current value of an expression with a `value` attribute and handles changes using the registered handler. @@ -299,13 +369,74 @@ Bind a property or field on other [Document Object Model (DOM)](https://develope `Page/BindEvent.razor`: -:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Pages/data-binding/BindEvent.razor" highlight="4"::: +:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Pages/data-binding/BindEvent.razor" highlight="4"::: Razor attribute binding is case sensitive: * `@bind` and `@bind:event` are valid. * `@Bind`/`@Bind:Event` (capital letters `B` and `E`) or `@BIND`/`@BIND:EVENT` (all capital letters) **are invalid**. +## Multiple option selection with `<select>` elements + +Binding supports [`multiple`](https://developer.mozilla.org/docs/Web/HTML/Attributes/multiple) option selection with `<select>` elements. The [`@onchange`](xref:mvc/views/razor#onevent) event provides an array of the selected elements via [event arguments (`ChangeEventArgs`)](xref:blazor/components/event-handling#event-arguments). The value must be bound to an array type. + +`Pages/BindMultipleInput.razor`: + +```razor +@page "/bind-multiple-input" + +<h1>Bind Multiple <code>input</code>Example</h1> + +<p> + <label> + Select one or more cars: + <select @onchange="SelectedCarsChanged" multiple> + <option value="audi">Audi</option> + <option value="jeep">Jeep</option> + <option value="opel">Opel</option> + <option value="saab">Saab</option> + <option value="volvo">Volvo</option> + </select> + </label> +</p> + +<p> + Selected Cars: @string.Join(", ", SelectedCars) +</p> + +<p> + <label> + Select one or more cities: + <select @bind="SelectedCities" multiple> + <option value="bal">Baltimore</option> + <option value="la">Los Angeles</option> + <option value="pdx">Portland</option> + <option value="sf">San Francisco</option> + <option value="sea">Seattle</option> + </select> + </label> +</p> + +<span> + Selected Cities: @string.Join(", ", SelectedCities) +</span> + +@code { + public string[] SelectedCars { get; set; } = new string[] { }; + public string[] SelectedCities { get; set; } = new[] { "bal", "sea" }; + + void SelectedCarsChanged(ChangeEventArgs e) + { + if (e.Value is not null) + { + SelectedCars = (string[])e.Value; + } + } +} +``` + +For information on how empty strings and `null` values are handled in data binding, see the [Binding `<select>` element options to C# object `null` values](#binding-select-element-options-to-c-object-null-values) section. + ## Binding `<select>` element options to C# object `null` values There's no sensible way to represent a `<select>` element option value as a C# object `null` value, because: @@ -328,7 +459,7 @@ Consider the following component, where an `<input>` element is bound to an `int `Pages/UnparsableValues.razor`: -:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Pages/data-binding/UnparsableValues.razor" highlight="4,12"::: +:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Pages/data-binding/UnparsableValues.razor" highlight="4,12"::: By default, binding applies to the element's `onchange` event. If the user updates the value of the text box's entry to `123.45` and changes the focus, the element's value is reverted to `123` when `onchange` fires. When the value `123.45` is rejected in favor of the original value of `123`, the user understands that their value wasn't accepted. @@ -346,7 +477,7 @@ Data binding works with a single <xref:System.DateTime> format string using `@bi `Pages/DateBinding.razor`: -:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Pages/data-binding/DateBinding.razor" highlight="6"::: +:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Pages/data-binding/DateBinding.razor" highlight="6"::: In the preceding code, the `<input>` element's field type (`type` attribute) defaults to `text`. @@ -369,7 +500,7 @@ Specifying a format for the `date` field type isn't recommended because Blazor h `Pages/DecimalBinding.razor`: -:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Pages/data-binding/DecimalBinding.razor" highlight="7,21-31"::: +:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Pages/data-binding/DecimalBinding.razor" highlight="7,21-31"::: ## Binding with component parameters @@ -387,7 +518,7 @@ The following `ChildBind` component has a `Year` component parameter and an <xre `Shared/ChildBind.razor`: -:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Shared/data-binding/ChildBind.razor" highlight="14-15,17-18,22"::: +:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Shared/data-binding/ChildBind.razor" highlight="14-15,17-18,22"::: For more information on events and <xref:Microsoft.AspNetCore.Components.EventCallback%601>, see the *EventCallback* section of the <xref:blazor/components/event-handling#eventcallback> article. @@ -395,7 +526,7 @@ In the following `Parent1` component, the `year` field is bound to the `Year` pa `Pages/Parent1.razor`: -:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Pages/data-binding/Parent1.razor" highlight="9"::: +:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Pages/data-binding/Parent1.razor" highlight="9"::: By convention, a property can be bound to a corresponding event handler by including an `@bind-{PROPERTY}:event` attribute assigned to the handler, where the `{PROPERTY}` placeholder is the property. `<ChildBind @bind-Year="year" />` is equivalent to writing: @@ -411,13 +542,13 @@ In a more sophisticated and real-world example, the following `PasswordEntry` co `Shared/PasswordEntry.razor`: -:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Shared/data-binding/PasswordEntry.razor" highlight="7-10,13,23-24,26-27,36-39"::: +:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Shared/data-binding/PasswordEntry.razor" highlight="7-10,13,23-24,26-27,36-39"::: The `PasswordEntry` component is used in another component, such as the following `PasswordBinding` component example. `Pages/PasswordBinding.razor`: -:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Pages/data-binding/PasswordBinding.razor" highlight="5"::: +:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Pages/data-binding/PasswordBinding.razor" highlight="5"::: When the `PasswordBinding` component is initially rendered, the `password` value of `Not set` is displayed in the UI. After initial rendering, the value of `password` reflects changes made to the `Password` component parameter value in the `PasswordEntry` component. @@ -428,7 +559,7 @@ Perform checks or trap errors in the handler. The following revised `PasswordEnt `Shared/PasswordEntry.razor`: -:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Shared/data-binding/PasswordEntry2.razor" highlight="35-46"::: +:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Shared/data-binding/PasswordEntry2.razor" highlight="35-46"::: ## Bind across more than two components @@ -441,18 +572,18 @@ A common and recommended approach is to only store the underlying data in the pa `Pages/Parent2.razor`: -:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Pages/data-binding/Parent2.razor"::: +:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Pages/data-binding/Parent2.razor"::: `Shared/NestedChild.razor`: -:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Shared/data-binding/NestedChild.razor"::: +:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Shared/data-binding/NestedChild.razor"::: > [!WARNING] > Generally, avoid creating components that write directly to their own component parameters. The preceding `NestedChild` component makes use of a `BoundValue` property instead of writing directly to its `ChildMessage` parameter. For more information, see <xref:blazor/components/index#overwritten-parameters>. `Shared/NestedGrandchild.razor`: -:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Shared/data-binding/NestedGrandchild.razor"::: +:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Shared/data-binding/NestedGrandchild.razor"::: For an alternative approach suited to sharing data in memory and across components that aren't necessarily nested, see <xref:blazor/state-management>. @@ -467,7 +598,7 @@ For an alternative approach suited to sharing data in memory and across componen :::moniker-end -:::moniker range="< aspnetcore-5.0" +:::moniker range=">= aspnetcore-5.0 < aspnetcore-6.0" Razor components provide data binding features with the [`@bind`](xref:mvc/views/razor#bind) Razor directive attribute with a field, property, or Razor expression value. @@ -480,7 +611,7 @@ When an `<input>` element loses focus, its bound field or property is updated. `Pages/Bind.razor`: -:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Pages/data-binding/Bind.razor" highlight="4,8"::: +:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Pages/data-binding/Bind.razor" highlight="4,8"::: The text box is updated in the UI only when the component is rendered, not in response to changing the field's or property's value. Since components render themselves after event handler code executes, field and property updates are usually reflected in the UI immediately after an event handler is triggered. @@ -488,7 +619,7 @@ As a demonstration of how data binding composes in HTML, the following example b `Pages/BindTheory.razor`: -:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Pages/data-binding/BindTheory.razor" highlight="12-14"::: +:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Pages/data-binding/BindTheory.razor" highlight="12-14"::: When the `BindTheory` component is rendered, the `value` of the HTML demonstration `<input>` element comes from the `InputValue` property. When the user enters a value in the text box and changes element focus, the `onchange` event is fired and the `InputValue` property is set to the changed value. In reality, code execution is more complex because [`@bind`](xref:mvc/views/razor#bind) handles cases where type conversions are performed. In general, [`@bind`](xref:mvc/views/razor#bind) associates the current value of an expression with a `value` attribute and handles changes using the registered handler. @@ -496,7 +627,7 @@ Bind a property or field on other [Document Object Model (DOM)](https://develope `Page/BindEvent.razor`: -:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Pages/data-binding/BindEvent.razor" highlight="4"::: +:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Pages/data-binding/BindEvent.razor" highlight="4"::: Razor attribute binding is case sensitive: @@ -515,7 +646,7 @@ The Blazor framework doesn't attempt to suppress the default behavior because it * Creating a chain of special-case workarounds in the framework. * Breaking changes to current framework behavior. -The Blazor framework doesn't automatically handle `null` to empty string conversions when attempting two-way binding to a `<select>`'s value. For more information, see [Fix binding `<select>` to a null value (dotnet/aspnetcore #23221)](https://github.com/dotnet/aspnetcore/pull/23221). +The most plausible `null` equivalent in HTML is an *empty string* `value`. The Blazor framework handles `null` to empty string conversions for two-way binding to a `<select>`'s value. ## Unparsable values @@ -525,7 +656,7 @@ Consider the following component, where an `<input>` element is bound to an `int `Pages/UnparsableValues.razor`: -:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Pages/data-binding/UnparsableValues.razor" highlight="4,12"::: +:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Pages/data-binding/UnparsableValues.razor" highlight="4,12"::: By default, binding applies to the element's `onchange` event. If the user updates the value of the text box's entry to `123.45` and changes the focus, the element's value is reverted to `123` when `onchange` fires. When the value `123.45` is rejected in favor of the original value of `123`, the user understands that their value wasn't accepted. @@ -543,7 +674,7 @@ Data binding works with a single <xref:System.DateTime> format string using `@bi `Pages/DateBinding.razor`: -:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Pages/data-binding/DateBinding.razor" highlight="6"::: +:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Pages/data-binding/DateBinding.razor" highlight="6"::: In the preceding code, the `<input>` element's field type (`type` attribute) defaults to `text`. @@ -566,7 +697,7 @@ Specifying a format for the `date` field type isn't recommended because Blazor h `Pages/DecimalBinding.razor`: -:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Pages/data-binding/DecimalBinding.razor" highlight="7,21-31"::: +:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Pages/data-binding/DecimalBinding.razor" highlight="7,21-31"::: ## Binding with component parameters @@ -584,7 +715,7 @@ The following `ChildBind` component has a `Year` component parameter and an <xre `Shared/ChildBind.razor`: -:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Shared/data-binding/ChildBind.razor" highlight="14-15,17-18,22"::: +:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Shared/data-binding/ChildBind.razor" highlight="14-15,17-18,22"::: For more information on events and <xref:Microsoft.AspNetCore.Components.EventCallback%601>, see the *EventCallback* section of the <xref:blazor/components/event-handling#eventcallback> article. @@ -592,7 +723,7 @@ In the following `Parent1` component, the `year` field is bound to the `Year` pa `Pages/Parent1.razor`: -:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Pages/data-binding/Parent1.razor" highlight="9"::: +:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Pages/data-binding/Parent1.razor" highlight="9"::: By convention, a property can be bound to a corresponding event handler by including an `@bind-{PROPERTY}:event` attribute assigned to the handler, where the `{PROPERTY}` placeholder is the property. `<ChildBind @bind-Year="year" />` is equivalent to writing: @@ -608,13 +739,13 @@ In a more sophisticated and real-world example, the following `PasswordEntry` co `Shared/PasswordEntry.razor`: -:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Shared/data-binding/PasswordEntry.razor" highlight="7-10,13,23-24,26-27,36-39"::: +:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Shared/data-binding/PasswordEntry.razor" highlight="7-10,13,23-24,26-27,36-39"::: The `PasswordEntry` component is used in another component, such as the following `PasswordBinding` component example. `Pages/PasswordBinding.razor`: -:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Pages/data-binding/PasswordBinding.razor" highlight="5"::: +:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Pages/data-binding/PasswordBinding.razor" highlight="5"::: When the `PasswordBinding` component is initially rendered, the `password` value of `Not set` is displayed in the UI. After initial rendering, the value of `password` reflects changes made to the `Password` component parameter value in the `PasswordEntry` component. @@ -625,7 +756,7 @@ Perform checks or trap errors in the handler. The following revised `PasswordEnt `Shared/PasswordEntry.razor`: -:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Shared/data-binding/PasswordEntry2.razor" highlight="35-46"::: +:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Shared/data-binding/PasswordEntry2.razor" highlight="35-46"::: ## Bind across more than two components @@ -638,18 +769,18 @@ A common and recommended approach is to only store the underlying data in the pa `Pages/Parent2.razor`: -:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Pages/data-binding/Parent2.razor"::: +:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Pages/data-binding/Parent2.razor"::: `Shared/NestedChild.razor`: -:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Shared/data-binding/NestedChild.razor"::: +:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Shared/data-binding/NestedChild.razor"::: > [!WARNING] > Generally, avoid creating components that write directly to their own component parameters. The preceding `NestedChild` component makes use of a `BoundValue` property instead of writing directly to its `ChildMessage` parameter. For more information, see <xref:blazor/components/index#overwritten-parameters>. `Shared/NestedGrandchild.razor`: -:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Shared/data-binding/NestedGrandchild.razor"::: +:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Shared/data-binding/NestedGrandchild.razor"::: For an alternative approach suited to sharing data in memory and across components that aren't necessarily nested, see <xref:blazor/state-management>. @@ -664,7 +795,7 @@ For an alternative approach suited to sharing data in memory and across componen :::moniker-end -:::moniker range=">= aspnetcore-7.0" +:::moniker range="< aspnetcore-5.0" Razor components provide data binding features with the [`@bind`](xref:mvc/views/razor#bind) Razor directive attribute with a field, property, or Razor expression value. @@ -677,7 +808,7 @@ When an `<input>` element loses focus, its bound field or property is updated. `Pages/Bind.razor`: -:::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Pages/data-binding/Bind.razor" highlight="4,8"::: +:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Pages/data-binding/Bind.razor" highlight="4,8"::: The text box is updated in the UI only when the component is rendered, not in response to changing the field's or property's value. Since components render themselves after event handler code executes, field and property updates are usually reflected in the UI immediately after an event handler is triggered. @@ -685,7 +816,7 @@ As a demonstration of how data binding composes in HTML, the following example b `Pages/BindTheory.razor`: -:::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Pages/data-binding/BindTheory.razor" highlight="12-14"::: +:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Pages/data-binding/BindTheory.razor" highlight="12-14"::: When the `BindTheory` component is rendered, the `value` of the HTML demonstration `<input>` element comes from the `InputValue` property. When the user enters a value in the text box and changes element focus, the `onchange` event is fired and the `InputValue` property is set to the changed value. In reality, code execution is more complex because [`@bind`](xref:mvc/views/razor#bind) handles cases where type conversions are performed. In general, [`@bind`](xref:mvc/views/razor#bind) associates the current value of an expression with a `value` attribute and handles changes using the registered handler. @@ -693,120 +824,12 @@ Bind a property or field on other [Document Object Model (DOM)](https://develope `Page/BindEvent.razor`: -:::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Pages/data-binding/BindEvent.razor" highlight="4"::: - -To execute asynchronous logic after binding, use `@bind:after="{EVENT}"` with a DOM event for the `{EVENT}` placeholder. An assigned C# method isn't executed until the bound value is assigned synchronously. - -In the following example: - -* The `<input>` element's `value` is bound to the value of `searchText` synchronously. -* After each keystroke (`onchange` event) in the field, the `PerformSearch` method executes asynchronously. -* `PerformSearch` calls a service with an asynchronous method (`FetchAsync`) to return search results. - -```razor -@inject ISearchService SearchService - -<input @bind="searchText" @bind:after="PerformSearch" /> - -@code { - private string? searchText; - private string[]? searchResult; - - private async Task PerformSearch() - { - searchResult = await SearchService.FetchAsync(searchText); - } -} -``` - -Components support two-way data binding by defining a pair of parameters: - -* `@bind:get`: Specifies the value to bind. -* `@bind:set`: Specifies a callback for when the value changes. - -The `@bind:get` and `@bind:set` modifiers are always used together. - -Example: - -```razor -<input @bind:get="Value" @bind:set="ValueChanged" /> - -@code { - [Parameter] - public string? Value { get; set; } - - [Parameter] - public EventCallback<string> ValueChanged { get; set; } -} -``` - -For another example use of `@bind:get` and `@bind:set`, see the [Bind across more than two components](#bind-across-more-than-two-components) section later in this article. +:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Pages/data-binding/BindEvent.razor" highlight="4"::: Razor attribute binding is case sensitive: -* `@bind`, `@bind:event`, and `@bind:after` are valid. -* `@Bind`/`@bind:Event`/`@bind:aftEr` (capital letters) or `@BIND`/`@BIND:EVENT`/`@BIND:AFTER` (all capital letters) **are invalid**. - -## Multiple option selection with `<select>` elements - -Binding supports [`multiple`](https://developer.mozilla.org/docs/Web/HTML/Attributes/multiple) option selection with `<select>` elements. The [`@onchange`](xref:mvc/views/razor#onevent) event provides an array of the selected elements via [event arguments (`ChangeEventArgs`)](xref:blazor/components/event-handling#event-arguments). The value must be bound to an array type. - -`Pages/BindMultipleInput.razor`: - -```razor -@page "/bind-multiple-input" - -<h1>Bind Multiple <code>input</code>Example</h1> - -<p> - <label> - Select one or more cars: - <select @onchange="SelectedCarsChanged" multiple> - <option value="audi">Audi</option> - <option value="jeep">Jeep</option> - <option value="opel">Opel</option> - <option value="saab">Saab</option> - <option value="volvo">Volvo</option> - </select> - </label> -</p> - -<p> - Selected Cars: @string.Join(", ", SelectedCars) -</p> - -<p> - <label> - Select one or more cities: - <select @bind="SelectedCities" multiple> - <option value="bal">Baltimore</option> - <option value="la">Los Angeles</option> - <option value="pdx">Portland</option> - <option value="sf">San Francisco</option> - <option value="sea">Seattle</option> - </select> - </label> -</p> - -<span> - Selected Cities: @string.Join(", ", SelectedCities) -</span> - -@code { - public string[] SelectedCars { get; set; } = new string[] { }; - public string[] SelectedCities { get; set; } = new[] { "bal", "sea" }; - - void SelectedCarsChanged(ChangeEventArgs e) - { - if (e.Value is not null) - { - SelectedCars = (string[])e.Value; - } - } -} -``` - -For information on how empty strings and `null` values are handled in data binding, see the [Binding `<select>` element options to C# object `null` values](#binding-select-element-options-to-c-object-null-values) section. +* `@bind` and `@bind:event` are valid. +* `@Bind`/`@Bind:Event` (capital letters `B` and `E`) or `@BIND`/`@BIND:EVENT` (all capital letters) **are invalid**. ## Binding `<select>` element options to C# object `null` values @@ -820,7 +843,7 @@ The Blazor framework doesn't attempt to suppress the default behavior because it * Creating a chain of special-case workarounds in the framework. * Breaking changes to current framework behavior. -The most plausible `null` equivalent in HTML is an *empty string* `value`. The Blazor framework handles `null` to empty string conversions for two-way binding to a `<select>`'s value. +The Blazor framework doesn't automatically handle `null` to empty string conversions when attempting two-way binding to a `<select>`'s value. For more information, see [Fix binding `<select>` to a null value (dotnet/aspnetcore #23221)](https://github.com/dotnet/aspnetcore/pull/23221). ## Unparsable values @@ -830,7 +853,7 @@ Consider the following component, where an `<input>` element is bound to an `int `Pages/UnparsableValues.razor`: -:::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Pages/data-binding/UnparsableValues.razor" highlight="4,12"::: +:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Pages/data-binding/UnparsableValues.razor" highlight="4,12"::: By default, binding applies to the element's `onchange` event. If the user updates the value of the text box's entry to `123.45` and changes the focus, the element's value is reverted to `123` when `onchange` fires. When the value `123.45` is rejected in favor of the original value of `123`, the user understands that their value wasn't accepted. @@ -848,7 +871,7 @@ Data binding works with a single <xref:System.DateTime> format string using `@bi `Pages/DateBinding.razor`: -:::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Pages/data-binding/DateBinding.razor" highlight="6"::: +:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Pages/data-binding/DateBinding.razor" highlight="6"::: In the preceding code, the `<input>` element's field type (`type` attribute) defaults to `text`. @@ -871,7 +894,7 @@ Specifying a format for the `date` field type isn't recommended because Blazor h `Pages/DecimalBinding.razor`: -:::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Pages/data-binding/DecimalBinding.razor" highlight="7,21-31"::: +:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Pages/data-binding/DecimalBinding.razor" highlight="7,21-31"::: ## Binding with component parameters @@ -889,7 +912,7 @@ The following `ChildBind` component has a `Year` component parameter and an <xre `Shared/ChildBind.razor`: -:::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Shared/data-binding/ChildBind.razor" highlight="14-15,17-18,22"::: +:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Shared/data-binding/ChildBind.razor" highlight="14-15,17-18,22"::: For more information on events and <xref:Microsoft.AspNetCore.Components.EventCallback%601>, see the *EventCallback* section of the <xref:blazor/components/event-handling#eventcallback> article. @@ -897,22 +920,7 @@ In the following `Parent1` component, the `year` field is bound to the `Year` pa `Pages/Parent1.razor`: -:::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Pages/data-binding/Parent1.razor" highlight="9"::: - -Component parameter binding can also trigger `@bind:after` events. In the following example, the `YearUpdated` method executes asynchronously after binding the `Year` component parameter. - -```razor -<ChildBind @bind-Year="year" @bind-Year:after="YearUpdated" /> - -@code { - ... - - private async Task YearUpdated() - { - ... = await ...; - } -} -``` +:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Pages/data-binding/Parent1.razor" highlight="9"::: By convention, a property can be bound to a corresponding event handler by including an `@bind-{PROPERTY}:event` attribute assigned to the handler, where the `{PROPERTY}` placeholder is the property. `<ChildBind @bind-Year="year" />` is equivalent to writing: @@ -928,13 +936,13 @@ In a more sophisticated and real-world example, the following `PasswordEntry` co `Shared/PasswordEntry.razor`: -:::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Shared/data-binding/PasswordEntry.razor" highlight="7-10,13,23-24,26-27,36-39"::: +:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Shared/data-binding/PasswordEntry.razor" highlight="7-10,13,23-24,26-27,36-39"::: The `PasswordEntry` component is used in another component, such as the following `PasswordBinding` component example. `Pages/PasswordBinding.razor`: -:::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Pages/data-binding/PasswordBinding.razor" highlight="5"::: +:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Pages/data-binding/PasswordBinding.razor" highlight="5"::: When the `PasswordBinding` component is initially rendered, the `password` value of `Not set` is displayed in the UI. After initial rendering, the value of `password` reflects changes made to the `Password` component parameter value in the `PasswordEntry` component. @@ -945,13 +953,7 @@ Perform checks or trap errors in the handler. The following revised `PasswordEnt `Shared/PasswordEntry.razor`: -:::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Shared/data-binding/PasswordEntry2.razor" highlight="35-46"::: - -In the following example, the `PasswordUpdated` method executes asynchronously after binding the `Password` component parameter: - -```razor -<PasswordEntry @bind-Password="password" @bind-Password:after="PasswordUpdated" /> -``` +:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Shared/data-binding/PasswordEntry2.razor" highlight="35-46"::: ## Bind across more than two components @@ -964,20 +966,18 @@ A common and recommended approach is to only store the underlying data in the pa `Pages/Parent2.razor`: -:::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Pages/data-binding/Parent2.razor"::: - -In the following `NestedChild` component, the `NestedGrandchild` component: - -* Assigns the value of `ChildMessage` to `GrandchildMessage` with `@bind:get` syntax. -* Updates `GrandchildMessage` when `ChildMessageChanged` executes with `@bind:set` syntax. +:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Pages/data-binding/Parent2.razor"::: `Shared/NestedChild.razor`: -:::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Shared/data-binding/NestedChild.razor"::: +:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Shared/data-binding/NestedChild.razor"::: + +> [!WARNING] +> Generally, avoid creating components that write directly to their own component parameters. The preceding `NestedChild` component makes use of a `BoundValue` property instead of writing directly to its `ChildMessage` parameter. For more information, see <xref:blazor/components/index#overwritten-parameters>. `Shared/NestedGrandchild.razor`: -:::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Shared/data-binding/NestedGrandchild.razor"::: +:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Shared/data-binding/NestedGrandchild.razor"::: For an alternative approach suited to sharing data in memory and across components that aren't necessarily nested, see <xref:blazor/state-management>. diff --git a/aspnetcore/blazor/components/dynamiccomponent.md b/aspnetcore/blazor/components/dynamiccomponent.md index b431332ec490..344571dcff36 100644 --- a/aspnetcore/blazor/components/dynamiccomponent.md +++ b/aspnetcore/blazor/components/dynamiccomponent.md @@ -5,7 +5,7 @@ description: Learn how to use dynamically-rendered Razor components in Blazor ap monikerRange: '>= aspnetcore-6.0' ms.author: riande ms.custom: mvc -ms.date: 11/09/2021 +ms.date: 11/08/2022 uid: blazor/components/dynamiccomponent --- # Dynamically-rendered ASP.NET Core Razor components @@ -14,7 +14,7 @@ By [Dave Brock](https://twitter.com/daveabrock) Use the built-in <xref:Microsoft.AspNetCore.Components.DynamicComponent> component to render components by type. -:::moniker range="< aspnetcore-7.0" +:::moniker range=">= aspnetcore-7.0" A <xref:Microsoft.AspNetCore.Components.DynamicComponent> is useful for rendering components without iterating through possible types or using conditional logic. For example, <xref:Microsoft.AspNetCore.Components.DynamicComponent> can render a component based on a user selection from a dropdown list. @@ -69,23 +69,23 @@ In the following example, a Razor component renders a component based on the use `Shared/RocketLab.razor`: -:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Shared/dynamiccomponent/RocketLab.razor"::: +:::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Shared/dynamiccomponent/RocketLab.razor"::: `Shared/SpaceX.razor`: -:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Shared/dynamiccomponent/SpaceX.razor"::: +:::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Shared/dynamiccomponent/SpaceX.razor"::: `Shared/UnitedLaunchAlliance.razor`: -:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Shared/dynamiccomponent/UnitedLaunchAlliance.razor"::: +:::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Shared/dynamiccomponent/UnitedLaunchAlliance.razor"::: `Shared/VirginGalactic.razor`: -:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Shared/dynamiccomponent/VirginGalactic.razor"::: +:::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Shared/dynamiccomponent/VirginGalactic.razor"::: `Pages/DynamicComponentExample1.razor`: -:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Pages/dynamiccomponent/DynamicComponentExample1.razor"::: +:::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Pages/dynamiccomponent/DynamicComponentExample1.razor"::: In the preceding example: @@ -113,7 +113,7 @@ The following `RocketLabWithWindowSeat` component (`Shared/RocketLabWithWindowSe `Shared/RocketLabWithWindowSeat.razor`: -:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Shared/dynamiccomponent/RocketLabWithWindowSeat.razor" highlight="13-14"::: +:::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Shared/dynamiccomponent/RocketLabWithWindowSeat.razor" highlight="13-14"::: In the following example: @@ -128,7 +128,7 @@ In the following example: `Pages/DynamicComponentExample2.razor`: -:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Pages/dynamiccomponent/DynamicComponentExample2.razor"::: +:::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Pages/dynamiccomponent/DynamicComponentExample2.razor"::: ## Event callbacks (`EventCallback`) @@ -371,7 +371,7 @@ Rocket Lab is a registered trademark of [Rocket Lab USA Inc.](https://www.rocket :::moniker-end -:::moniker range=">= aspnetcore-7.0" +:::moniker range="< aspnetcore-7.0" A <xref:Microsoft.AspNetCore.Components.DynamicComponent> is useful for rendering components without iterating through possible types or using conditional logic. For example, <xref:Microsoft.AspNetCore.Components.DynamicComponent> can render a component based on a user selection from a dropdown list. @@ -426,23 +426,23 @@ In the following example, a Razor component renders a component based on the use `Shared/RocketLab.razor`: -:::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Shared/dynamiccomponent/RocketLab.razor"::: +:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Shared/dynamiccomponent/RocketLab.razor"::: `Shared/SpaceX.razor`: -:::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Shared/dynamiccomponent/SpaceX.razor"::: +:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Shared/dynamiccomponent/SpaceX.razor"::: `Shared/UnitedLaunchAlliance.razor`: -:::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Shared/dynamiccomponent/UnitedLaunchAlliance.razor"::: +:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Shared/dynamiccomponent/UnitedLaunchAlliance.razor"::: `Shared/VirginGalactic.razor`: -:::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Shared/dynamiccomponent/VirginGalactic.razor"::: +:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Shared/dynamiccomponent/VirginGalactic.razor"::: `Pages/DynamicComponentExample1.razor`: -:::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Pages/dynamiccomponent/DynamicComponentExample1.razor"::: +:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Pages/dynamiccomponent/DynamicComponentExample1.razor"::: In the preceding example: @@ -470,7 +470,7 @@ The following `RocketLabWithWindowSeat` component (`Shared/RocketLabWithWindowSe `Shared/RocketLabWithWindowSeat.razor`: -:::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Shared/dynamiccomponent/RocketLabWithWindowSeat.razor" highlight="13-14"::: +:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Shared/dynamiccomponent/RocketLabWithWindowSeat.razor" highlight="13-14"::: In the following example: @@ -485,7 +485,7 @@ In the following example: `Pages/DynamicComponentExample2.razor`: -:::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Pages/dynamiccomponent/DynamicComponentExample2.razor"::: +:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Pages/dynamiccomponent/DynamicComponentExample2.razor"::: ## Event callbacks (`EventCallback`) diff --git a/aspnetcore/blazor/components/event-handling.md b/aspnetcore/blazor/components/event-handling.md index 9309ce238cf6..60e74a243a5b 100644 --- a/aspnetcore/blazor/components/event-handling.md +++ b/aspnetcore/blazor/components/event-handling.md @@ -5,14 +5,14 @@ description: Learn about Blazor's event handling features, including event argum monikerRange: '>= aspnetcore-3.1' ms.author: riande ms.custom: mvc -ms.date: 11/09/2021 +ms.date: 11/08/2022 uid: blazor/components/event-handling --- # ASP.NET Core Blazor event handling This article explains Blazor's event handling features, including event argument types, event callbacks, and managing default browser events. -:::moniker range=">= aspnetcore-6.0 < aspnetcore-7.0" +:::moniker range=">= aspnetcore-7.0" Specify delegate event handlers in Razor component markup with [`@on{DOM EVENT}="{DELEGATE}"`](xref:mvc/views/razor#onevent) Razor syntax: @@ -32,7 +32,7 @@ The following code: `Pages/EventHandlerExample1.razor`: -:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Pages/event-handling/EventHandlerExample1.razor" highlight="10,17,27-30,32-35"::: +:::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Pages/event-handling/EventHandlerExample1.razor" highlight="10,17,27-30,32-35"::: In the following example, `UpdateHeading`: @@ -41,7 +41,7 @@ In the following example, `UpdateHeading`: `Pages/EventHandlerExample2.razor`: -:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Pages/event-handling/EventHandlerExample2.razor" highlight="10,19-24"::: +:::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Pages/event-handling/EventHandlerExample2.razor" highlight="10,19-24"::: ## Event arguments @@ -51,28 +51,32 @@ For events that support an event argument type, specifying an event parameter in `Pages/EventHandlerExample3.razor`: -:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Pages/event-handling/EventHandlerExample3.razor" highlight="17-20"::: +:::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Pages/event-handling/EventHandlerExample3.razor" highlight="17-20"::: Supported <xref:System.EventArgs> are shown in the following table. -| Event | Class | [Document Object Model (DOM)](https://developer.mozilla.org/docs/Web/API/Document_Object_Model/Introduction) events and notes | +| Event | Class | [Document Object Model (DOM)](https://developer.mozilla.org/docs/Web/API/Document_Object_Model/Introduction) notes | | ---------------- | ------ | --- | -| Clipboard | <xref:Microsoft.AspNetCore.Components.Web.ClipboardEventArgs> | `oncut`, `oncopy`, `onpaste` | -| Drag | <xref:Microsoft.AspNetCore.Components.Web.DragEventArgs> | `ondrag`, `ondragstart`, `ondragenter`, `ondragleave`, `ondragover`, `ondrop`, `ondragend`<br><br><xref:Microsoft.AspNetCore.Components.Web.DataTransfer> and <xref:Microsoft.AspNetCore.Components.Web.DataTransferItem> hold dragged item data.<br><br>Implement drag and drop in Blazor apps using [JS interop](xref:blazor/js-interop/call-javascript-from-dotnet) with [HTML Drag and Drop API](https://developer.mozilla.org/docs/Web/API/HTML_Drag_and_Drop_API). | -| Error | <xref:Microsoft.AspNetCore.Components.Web.ErrorEventArgs> | `onerror` | -| Event | <xref:System.EventArgs> | *General*<br>`onactivate`, `onbeforeactivate`, `onbeforedeactivate`, `ondeactivate`, `onfullscreenchange`, `onfullscreenerror`, `onloadeddata`, `onloadedmetadata`, `onpointerlockchange`, `onpointerlockerror`, `onreadystatechange`, `onscroll`<br><br>*Clipboard*<br>`onbeforecut`, `onbeforecopy`, `onbeforepaste`<br><br>*Input*<br>`oninvalid`, `onreset`, `onselect`, `onselectionchange`, `onselectstart`, `onsubmit`<br><br>*Media*<br>`oncanplay`, `oncanplaythrough`, `oncuechange`, `ondurationchange`, `onemptied`, `onended`, `onpause`, `onplay`, `onplaying`, `onratechange`, `onseeked`, `onseeking`, `onstalled`, `onstop`, `onsuspend`, `ontimeupdate`, `ontoggle`, `onvolumechange`, `onwaiting`<br><br><xref:Microsoft.AspNetCore.Components.Web.EventHandlers> holds attributes to configure the mappings between event names and event argument types. | -| Focus | <xref:Microsoft.AspNetCore.Components.Web.FocusEventArgs> | `onfocus`, `onblur`, `onfocusin`, `onfocusout`<br><br>Doesn't include support for `relatedTarget`. | -| Input | <xref:Microsoft.AspNetCore.Components.ChangeEventArgs> | `onchange`, `oninput` | -| Keyboard | <xref:Microsoft.AspNetCore.Components.Web.KeyboardEventArgs> | `onkeydown`, `onkeypress`, `onkeyup` | -| Mouse | <xref:Microsoft.AspNetCore.Components.Web.MouseEventArgs> | `onclick`, `oncontextmenu`, `ondblclick`, `onmousedown`, `onmouseup`, `onmouseover`, `onmousemove`, `onmouseout` | -| Mouse pointer | <xref:Microsoft.AspNetCore.Components.Web.PointerEventArgs> | `onpointerdown`, `onpointerup`, `onpointercancel`, `onpointermove`, `onpointerover`, `onpointerout`, `onpointerenter`, `onpointerleave`, `ongotpointercapture`, `onlostpointercapture` | -| Mouse wheel | <xref:Microsoft.AspNetCore.Components.Web.WheelEventArgs> | `onwheel`, `onmousewheel` | -| Progress | <xref:Microsoft.AspNetCore.Components.Web.ProgressEventArgs> | `onabort`, `onload`, `onloadend`, `onloadstart`, `onprogress`, `ontimeout` | -| Touch | <xref:Microsoft.AspNetCore.Components.Web.TouchEventArgs> | `ontouchstart`, `ontouchend`, `ontouchmove`, `ontouchenter`, `ontouchleave`, `ontouchcancel`<br><br><xref:Microsoft.AspNetCore.Components.Web.TouchPoint> represents a single contact point on a touch-sensitive device. | +| Clipboard | <xref:Microsoft.AspNetCore.Components.Web.ClipboardEventArgs> | | +| Drag | <xref:Microsoft.AspNetCore.Components.Web.DragEventArgs> | <xref:Microsoft.AspNetCore.Components.Web.DataTransfer> and <xref:Microsoft.AspNetCore.Components.Web.DataTransferItem> hold dragged item data.<br><br>Implement drag and drop in Blazor apps using [JS interop](xref:blazor/js-interop/call-javascript-from-dotnet) with [HTML Drag and Drop API](https://developer.mozilla.org/docs/Web/API/HTML_Drag_and_Drop_API). | +| Error | <xref:Microsoft.AspNetCore.Components.Web.ErrorEventArgs> | | +| Event | <xref:System.EventArgs> | <xref:Microsoft.AspNetCore.Components.Web.EventHandlers> holds attributes to configure the mappings between event names and event argument types. | +| Focus | <xref:Microsoft.AspNetCore.Components.Web.FocusEventArgs> | Doesn't include support for `relatedTarget`. | +| Input | <xref:Microsoft.AspNetCore.Components.ChangeEventArgs> | | +| Keyboard | <xref:Microsoft.AspNetCore.Components.Web.KeyboardEventArgs> | | +| Mouse | <xref:Microsoft.AspNetCore.Components.Web.MouseEventArgs> | | +| Mouse pointer | <xref:Microsoft.AspNetCore.Components.Web.PointerEventArgs> | | +| Mouse wheel | <xref:Microsoft.AspNetCore.Components.Web.WheelEventArgs> | | +| Progress | <xref:Microsoft.AspNetCore.Components.Web.ProgressEventArgs> | | +| Touch | <xref:Microsoft.AspNetCore.Components.Web.TouchEventArgs> | <xref:Microsoft.AspNetCore.Components.Web.TouchPoint> represents a single contact point on a touch-sensitive device. | -For more information, see [`EventArgs` classes in the ASP.NET Core reference source (dotnet/aspnetcore `main` branch)](https://github.com/dotnet/aspnetcore/tree/main/src/Components/Web/src/Web). +For more information, see the following resources: -[!INCLUDE[](~/includes/aspnetcore-repo-ref-source-links.md)] +* [`EventArgs` classes in the ASP.NET Core reference source (dotnet/aspnetcore `main` branch)](https://github.com/dotnet/aspnetcore/tree/main/src/Components/Web/src/Web) + + [!INCLUDE[](~/includes/aspnetcore-repo-ref-source-links.md)] + +* <xref:Microsoft.AspNetCore.Components.Web.EventHandlers> holds attributes to configure the mappings between event names and event argument types. ### Custom event arguments @@ -93,7 +97,7 @@ Custom events with custom event arguments are generally enabled with the followi } ``` -1. Register the custom event with the preceding handler in `wwwroot/index.html` (Blazor WebAssembly) or `Pages/_Layout.cshtml` (Blazor Server) immediately after the Blazor `<script>`: +1. Register the custom event with the preceding handler in `wwwroot/index.html` (Blazor WebAssembly) or `Pages/_Host.cshtml` (Blazor Server) immediately after the Blazor `<script>`: ```html <script> @@ -168,9 +172,9 @@ public class CustomPasteEventArgs : EventArgs } ``` -Add JavaScript code to supply data for the <xref:System.EventArgs> subclass. In the `wwwroot/index.html` or `Pages/_Layout.cshtml` file, add the following `<script>` tag and content immediately after the Blazor script. The following example only handles pasting text, but you could use arbitrary JavaScript APIs to deal with users pasting other types of data, such as images. +Add JavaScript code to supply data for the <xref:System.EventArgs> subclass. In the `wwwroot/index.html` or `Pages/_Host.cshtml` file, add the following `<script>` tag and content immediately after the Blazor script. The following example only handles pasting text, but you could use arbitrary JavaScript APIs to deal with users pasting other types of data, such as images. -`wwwroot/index.html` (Blazor WebAssembly) or `Pages/_Layout.cshtml` (Blazor Server) immediately after the Blazor script: +`wwwroot/index.html` (Blazor WebAssembly) or `Pages/_Host.cshtml` (Blazor Server) immediately after the Blazor script: ```html <script> @@ -231,7 +235,7 @@ In a Razor component, attach the custom handler to an element. `Pages/EventHandlerExample4.razor`: -:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Pages/event-handling/EventHandlerExample4.razor" highlight="6"::: +:::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Pages/event-handling/EventHandlerExample4.razor" highlight="6"::: It's often convenient to close over additional values using C# method parameters, such as when iterating over a set of elements. The following example creates three buttons, each of which calls `UpdateHeading` and passes the following data: @@ -240,7 +244,7 @@ It's often convenient to close over additional values using C# method parameters `Pages/EventHandlerExample5.razor`: -:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Pages/event-handling/EventHandlerExample5.razor" highlight="10,19"::: +:::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Pages/event-handling/EventHandlerExample5.razor" highlight="10,19"::: > [!NOTE] > Do **not** use a loop variable directly in a lambda expression, such as `i` in the preceding `for` loop example. Otherwise, the same variable is used by all lambda expressions, which results in use of the same value in all lambdas. Always capture the variable's value in a local variable and then use it. In the preceding example: @@ -258,13 +262,13 @@ The following `Child` component demonstrates how a button's `onclick` handler is `Shared/Child.razor`: -:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Shared/event-handling/Child.razor"::: +:::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Shared/event-handling/Child.razor"::: The `Parent` component sets the child's <xref:Microsoft.AspNetCore.Components.EventCallback%601> (`OnClickCallback`) to its `ShowMessage` method. `Pages/Parent.razor`: -:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Pages/event-handling/Parent.razor"::: +:::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Pages/event-handling/Parent.razor"::: When the button is selected in the `ChildComponent`: @@ -296,7 +300,7 @@ When a key is selected on an input device and the element focus is on a text box `Pages/EventHandlerExample6.razor`: -:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Pages/event-handling/EventHandlerExample6.razor" highlight="4"::: +:::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Pages/event-handling/EventHandlerExample6.razor" highlight="4"::: Specifying the `@on{DOM EVENT}:preventDefault` attribute without a value is equivalent to `@on{DOM EVENT}:preventDefault="true"`. @@ -325,7 +329,7 @@ In the following example, selecting the checkbox prevents click events from the `Pages/EventHandlerExample7.razor`: -:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Pages/event-handling/EventHandlerExample7.razor" highlight="4,15-16"::: +:::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Pages/event-handling/EventHandlerExample7.razor" highlight="4,15-16"::: ## Focus an element @@ -333,11 +337,11 @@ Call <xref:Microsoft.AspNetCore.Components.ElementReferenceExtensions.FocusAsync `Pages/EventHandlerExample8.razor`: -:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Pages/event-handling/EventHandlerExample8.razor" highlight="16"::: +:::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Pages/event-handling/EventHandlerExample8.razor" highlight="16"::: :::moniker-end -:::moniker range=">= aspnetcore-5.0 < aspnetcore-6.0" +:::moniker range=">= aspnetcore-6.0 < aspnetcore-7.0" Specify delegate event handlers in Razor component markup with [`@on{DOM EVENT}="{DELEGATE}"`](xref:mvc/views/razor#onevent) Razor syntax: @@ -357,7 +361,7 @@ The following code: `Pages/EventHandlerExample1.razor`: -:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Pages/event-handling/EventHandlerExample1.razor" highlight="10,17,27-30,32-35"::: +:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Pages/event-handling/EventHandlerExample1.razor" highlight="10,17,27-30,32-35"::: In the following example, `UpdateHeading`: @@ -366,15 +370,17 @@ In the following example, `UpdateHeading`: `Pages/EventHandlerExample2.razor`: -:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Pages/event-handling/EventHandlerExample2.razor" highlight="10,19-24"::: +:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Pages/event-handling/EventHandlerExample2.razor" highlight="10,19-24"::: ## Event arguments +### Built-in event arguments + For events that support an event argument type, specifying an event parameter in the event method definition is only necessary if the event type is used in the method. In the following example, <xref:Microsoft.AspNetCore.Components.Web.MouseEventArgs> is used in the `ReportPointerLocation` method to set message text that reports the mouse coordinates when the user selects a button in the UI. `Pages/EventHandlerExample3.razor`: -:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Pages/event-handling/EventHandlerExample3.razor" highlight="17-20"::: +:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Pages/event-handling/EventHandlerExample3.razor" highlight="17-20"::: Supported <xref:System.EventArgs> are shown in the following table. @@ -393,17 +399,168 @@ Supported <xref:System.EventArgs> are shown in the following table. | Progress | <xref:Microsoft.AspNetCore.Components.Web.ProgressEventArgs> | `onabort`, `onload`, `onloadend`, `onloadstart`, `onprogress`, `ontimeout` | | Touch | <xref:Microsoft.AspNetCore.Components.Web.TouchEventArgs> | `ontouchstart`, `ontouchend`, `ontouchmove`, `ontouchenter`, `ontouchleave`, `ontouchcancel`<br><br><xref:Microsoft.AspNetCore.Components.Web.TouchPoint> represents a single contact point on a touch-sensitive device. | -For more information, see [`EventArgs` classes in the ASP.NET Core reference source (dotnet/aspnetcore `main` branch)](https://github.com/dotnet/aspnetcore/tree/main/src/Components/Web/src/Web) +For more information, see [`EventArgs` classes in the ASP.NET Core reference source (dotnet/aspnetcore `main` branch)](https://github.com/dotnet/aspnetcore/tree/main/src/Components/Web/src/Web). [!INCLUDE[](~/includes/aspnetcore-repo-ref-source-links.md)] +### Custom event arguments + +Blazor supports custom event arguments, which enable you to pass arbitrary data to .NET event handlers with custom events. + +#### General configuration + +Custom events with custom event arguments are generally enabled with the following steps. + +1. In JavaScript, define a function for building the custom event argument object from the source event: + + ```javascript + function eventArgsCreator(event) { + return { + customProperty1: 'any value for property 1', + customProperty2: event.srcElement.value + }; + } + ``` + +1. Register the custom event with the preceding handler in `wwwroot/index.html` (Blazor WebAssembly) or `Pages/_Layout.cshtml` (Blazor Server) immediately after the Blazor `<script>`: + + ```html + <script> + Blazor.registerCustomEventType('customevent', { + createEventArgs: eventArgsCreator; + }); + </script> + ``` + + > [!NOTE] + > The call to `registerCustomEventType` is performed in a script only once per event. + +1. Define a class for the event arguments: + + ```csharp + public class CustomEventArgs : EventArgs + { + public string? CustomProperty1 {get; set;} + public string? CustomProperty2 {get; set;} + } + ``` + +1. Wire up the custom event with the event arguments by adding an <xref:Microsoft.AspNetCore.Components.EventHandlerAttribute> attribute annotation for the custom event. The class doesn't require members. Note that the class *must* be called `EventHandlers` in order to be found by the Razor compiler, but you should put it in a namespace specific to your app: + + ```csharp + [EventHandler("oncustomevent", typeof(CustomEventArgs), enableStopPropagation: true, enablePreventDefault: true)] + static class EventHandlers + { + } + ``` + +1. Register the event handler on one or more HTML elements. Access the data that was passed in from JavaScript in the delegate handler method: + + ```razor + <button @oncustomevent="HandleCustomEvent">Handle</button> + + @code + { + void HandleCustomEvent(CustomEventArgs eventArgs) + { + // eventArgs.CustomProperty1 + // eventArgs.CustomProperty2 + } + } + ``` + +If the `@oncustomevent` attribute isn't recognized by [IntelliSense](/visualstudio/ide/using-intellisense), make sure that the component or the `_Imports.razor` file contains an `@using` statement for the namespace containing the `EventHandler` class. + +Whenever the custom event is fired on the DOM, the event handler is called with the data passed from the JavaScript. + +If you're attempting to fire a custom event, [`bubbles`](https://developer.mozilla.org/docs/Web/API/Event/bubbles) must be enabled by setting its value to `true`. Otherwise, the event doesn't reach the Blazor handler for processing into the C# custom <xref:Microsoft.AspNetCore.Components.EventHandlerAttribute> method. For more information, see [MDN Web Docs: Event bubbling](https://developer.mozilla.org/docs/Web/Guide/Events/Creating_and_triggering_events#event_bubbling). + +#### Custom clipboard paste event example + +The following example receives a custom clipboard paste event that includes the time of the paste and the user's pasted text. + +Declare a custom name (`oncustompaste`) for the event and a .NET class (`CustomPasteEventArgs`) to hold the event arguments for this event: + +`CustomEvents.cs`: + +```csharp +[EventHandler("oncustompaste", typeof(CustomPasteEventArgs), + enableStopPropagation: true, enablePreventDefault: true)] +public static class EventHandlers +{ +} + +public class CustomPasteEventArgs : EventArgs +{ + public DateTime EventTimestamp { get; set; } + public string? PastedData { get; set; } +} +``` + +Add JavaScript code to supply data for the <xref:System.EventArgs> subclass. In the `wwwroot/index.html` or `Pages/_Layout.cshtml` file, add the following `<script>` tag and content immediately after the Blazor script. The following example only handles pasting text, but you could use arbitrary JavaScript APIs to deal with users pasting other types of data, such as images. + +`wwwroot/index.html` (Blazor WebAssembly) or `Pages/_Layout.cshtml` (Blazor Server) immediately after the Blazor script: + +```html +<script> + Blazor.registerCustomEventType('custompaste', { + browserEventName: 'paste', + createEventArgs: event => { + return { + eventTimestamp: new Date(), + pastedData: event.clipboardData.getData('text') + }; + } + }); +</script> +``` + +The preceding code tells the browser that when a native [`paste`](https://developer.mozilla.org/docs/Web/API/Element/paste_event) event occurs: + +* Raise a `custompaste` event. +* Supply the event arguments data using the custom logic stated: + * For the `eventTimestamp`, create a new date. + * For the `pastedData`, get the clipboard data as text. For more information, see [MDN Web Docs: ClipboardEvent.clipboardData](https://developer.mozilla.org/docs/Web/API/ClipboardEvent/clipboardData). + +Event name conventions differ between .NET and JavaScript: + +* In .NET, event names are prefixed with "`on`". +* In JavaScript, event names don't have a prefix. + +In a Razor component, attach the custom handler to an element. + +`Pages/CustomPasteArguments.razor`: + +```razor +@page "/custom-paste-arguments" + +<label> + Try pasting into the following text box: + <input @oncustompaste="HandleCustomPaste" /> +</label> + +<p> + @message +</p> + +@code { + private string? message; + + private void HandleCustomPaste(CustomPasteEventArgs eventArgs) + { + message = $"At {eventArgs.EventTimestamp.ToShortTimeString()}, " + + $"you pasted: {eventArgs.PastedData}"; + } +} +``` + ## Lambda expressions [Lambda expressions](/dotnet/csharp/programming-guide/statements-expressions-operators/lambda-expressions) are supported as the delegate event handler. `Pages/EventHandlerExample4.razor`: -:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Pages/event-handling/EventHandlerExample4.razor" highlight="6"::: +:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Pages/event-handling/EventHandlerExample4.razor" highlight="6"::: It's often convenient to close over additional values using C# method parameters, such as when iterating over a set of elements. The following example creates three buttons, each of which calls `UpdateHeading` and passes the following data: @@ -412,7 +569,7 @@ It's often convenient to close over additional values using C# method parameters `Pages/EventHandlerExample5.razor`: -:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Pages/event-handling/EventHandlerExample5.razor" highlight="10,19"::: +:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Pages/event-handling/EventHandlerExample5.razor" highlight="10,19"::: > [!NOTE] > Do **not** use a loop variable directly in a lambda expression, such as `i` in the preceding `for` loop example. Otherwise, the same variable is used by all lambda expressions, which results in use of the same value in all lambdas. Always capture the variable's value in a local variable and then use it. In the preceding example: @@ -430,13 +587,13 @@ The following `Child` component demonstrates how a button's `onclick` handler is `Shared/Child.razor`: -:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Shared/event-handling/Child.razor"::: +:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Shared/event-handling/Child.razor"::: The `Parent` component sets the child's <xref:Microsoft.AspNetCore.Components.EventCallback%601> (`OnClickCallback`) to its `ShowMessage` method. `Pages/Parent.razor`: -:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Pages/event-handling/Parent.razor"::: +:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Pages/event-handling/Parent.razor"::: When the button is selected in the `ChildComponent`: @@ -468,7 +625,7 @@ When a key is selected on an input device and the element focus is on a text box `Pages/EventHandlerExample6.razor`: -:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Pages/event-handling/EventHandlerExample6.razor" highlight="4"::: +:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Pages/event-handling/EventHandlerExample6.razor" highlight="4"::: Specifying the `@on{DOM EVENT}:preventDefault` attribute without a value is equivalent to `@on{DOM EVENT}:preventDefault="true"`. @@ -497,7 +654,7 @@ In the following example, selecting the checkbox prevents click events from the `Pages/EventHandlerExample7.razor`: -:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Pages/event-handling/EventHandlerExample7.razor" highlight="4,15-16"::: +:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Pages/event-handling/EventHandlerExample7.razor" highlight="4,15-16"::: ## Focus an element @@ -505,11 +662,11 @@ Call <xref:Microsoft.AspNetCore.Components.ElementReferenceExtensions.FocusAsync `Pages/EventHandlerExample8.razor`: -:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Pages/event-handling/EventHandlerExample8.razor" highlight="16"::: +:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Pages/event-handling/EventHandlerExample8.razor" highlight="16"::: :::moniker-end -:::moniker range="< aspnetcore-5.0" +:::moniker range=">= aspnetcore-5.0 < aspnetcore-6.0" Specify delegate event handlers in Razor component markup with [`@on{DOM EVENT}="{DELEGATE}"`](xref:mvc/views/razor#onevent) Razor syntax: @@ -529,7 +686,7 @@ The following code: `Pages/EventHandlerExample1.razor`: -:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Pages/event-handling/EventHandlerExample1.razor" highlight="10,17,27-30,32-35"::: +:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Pages/event-handling/EventHandlerExample1.razor" highlight="10,17,27-30,32-35"::: In the following example, `UpdateHeading`: @@ -538,7 +695,7 @@ In the following example, `UpdateHeading`: `Pages/EventHandlerExample2.razor`: -:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Pages/event-handling/EventHandlerExample2.razor" highlight="10,19-24"::: +:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Pages/event-handling/EventHandlerExample2.razor" highlight="10,19-24"::: ## Event arguments @@ -546,16 +703,16 @@ For events that support an event argument type, specifying an event parameter in `Pages/EventHandlerExample3.razor`: -:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Pages/event-handling/EventHandlerExample3.razor" highlight="17-20"::: +:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Pages/event-handling/EventHandlerExample3.razor" highlight="17-20"::: Supported <xref:System.EventArgs> are shown in the following table. -| Event | Class | [Document Object Model (DOM)](https://developer.mozilla.org/docs/Web/API/Document_Object_Model/Introduction) events and notes | -| ---------------- | ----- | --- | +| Event | Class | [Document Object Model (DOM)](https://developer.mozilla.org/docs/Web/API/Document_Object_Model/Introduction) events and notes | +| ---------------- | ------ | --- | | Clipboard | <xref:Microsoft.AspNetCore.Components.Web.ClipboardEventArgs> | `oncut`, `oncopy`, `onpaste` | | Drag | <xref:Microsoft.AspNetCore.Components.Web.DragEventArgs> | `ondrag`, `ondragstart`, `ondragenter`, `ondragleave`, `ondragover`, `ondrop`, `ondragend`<br><br><xref:Microsoft.AspNetCore.Components.Web.DataTransfer> and <xref:Microsoft.AspNetCore.Components.Web.DataTransferItem> hold dragged item data.<br><br>Implement drag and drop in Blazor apps using [JS interop](xref:blazor/js-interop/call-javascript-from-dotnet) with [HTML Drag and Drop API](https://developer.mozilla.org/docs/Web/API/HTML_Drag_and_Drop_API). | | Error | <xref:Microsoft.AspNetCore.Components.Web.ErrorEventArgs> | `onerror` | -| Event | <xref:System.EventArgs> | *General*<br>`onactivate`, `onbeforeactivate`, `onbeforedeactivate`, `ondeactivate`, `onfullscreenchange`, `onfullscreenerror`, `onloadeddata`, `onloadedmetadata`, `onpointerlockchange`, `onpointerlockerror`, `onreadystatechange`, `onscroll`<br><br>*Clipboard*<br>`onbeforecut`, `onbeforecopy`, `onbeforepaste`<br><br>*Input*<br>`oninvalid`, `onreset`, `onselect`, `onselectionchange`, `onselectstart`, `onsubmit`<br><br>*Media*<br>`oncanplay`, `oncanplaythrough`, `oncuechange`, `ondurationchange`, `onemptied`, `onended`, `onpause`, `onplay`, `onplaying`, `onratechange`, `onseeked`, `onseeking`, `onstalled`, `onstop`, `onsuspend`, `ontimeupdate`, `onvolumechange`, `onwaiting`<br><br><xref:Microsoft.AspNetCore.Components.Web.EventHandlers> holds attributes to configure the mappings between event names and event argument types. | +| Event | <xref:System.EventArgs> | *General*<br>`onactivate`, `onbeforeactivate`, `onbeforedeactivate`, `ondeactivate`, `onfullscreenchange`, `onfullscreenerror`, `onloadeddata`, `onloadedmetadata`, `onpointerlockchange`, `onpointerlockerror`, `onreadystatechange`, `onscroll`<br><br>*Clipboard*<br>`onbeforecut`, `onbeforecopy`, `onbeforepaste`<br><br>*Input*<br>`oninvalid`, `onreset`, `onselect`, `onselectionchange`, `onselectstart`, `onsubmit`<br><br>*Media*<br>`oncanplay`, `oncanplaythrough`, `oncuechange`, `ondurationchange`, `onemptied`, `onended`, `onpause`, `onplay`, `onplaying`, `onratechange`, `onseeked`, `onseeking`, `onstalled`, `onstop`, `onsuspend`, `ontimeupdate`, `ontoggle`, `onvolumechange`, `onwaiting`<br><br><xref:Microsoft.AspNetCore.Components.Web.EventHandlers> holds attributes to configure the mappings between event names and event argument types. | | Focus | <xref:Microsoft.AspNetCore.Components.Web.FocusEventArgs> | `onfocus`, `onblur`, `onfocusin`, `onfocusout`<br><br>Doesn't include support for `relatedTarget`. | | Input | <xref:Microsoft.AspNetCore.Components.ChangeEventArgs> | `onchange`, `oninput` | | Keyboard | <xref:Microsoft.AspNetCore.Components.Web.KeyboardEventArgs> | `onkeydown`, `onkeypress`, `onkeyup` | @@ -575,7 +732,7 @@ For more information, see [`EventArgs` classes in the ASP.NET Core reference sou `Pages/EventHandlerExample4.razor`: -:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Pages/event-handling/EventHandlerExample4.razor" highlight="6"::: +:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Pages/event-handling/EventHandlerExample4.razor" highlight="6"::: It's often convenient to close over additional values using C# method parameters, such as when iterating over a set of elements. The following example creates three buttons, each of which calls `UpdateHeading` and passes the following data: @@ -584,7 +741,7 @@ It's often convenient to close over additional values using C# method parameters `Pages/EventHandlerExample5.razor`: -:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Pages/event-handling/EventHandlerExample5.razor" highlight="10,19"::: +:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Pages/event-handling/EventHandlerExample5.razor" highlight="10,19"::: > [!NOTE] > Do **not** use a loop variable directly in a lambda expression, such as `i` in the preceding `for` loop example. Otherwise, the same variable is used by all lambda expressions, which results in use of the same value in all lambdas. Always capture the variable's value in a local variable and then use it. In the preceding example: @@ -602,13 +759,13 @@ The following `Child` component demonstrates how a button's `onclick` handler is `Shared/Child.razor`: -:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Shared/event-handling/Child.razor"::: +:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Shared/event-handling/Child.razor"::: The `Parent` component sets the child's <xref:Microsoft.AspNetCore.Components.EventCallback%601> (`OnClickCallback`) to its `ShowMessage` method. `Pages/Parent.razor`: -:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Pages/event-handling/Parent.razor"::: +:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Pages/event-handling/Parent.razor"::: When the button is selected in the `ChildComponent`: @@ -640,7 +797,7 @@ When a key is selected on an input device and the element focus is on a text box `Pages/EventHandlerExample6.razor`: -:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Pages/event-handling/EventHandlerExample6.razor" highlight="4"::: +:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Pages/event-handling/EventHandlerExample6.razor" highlight="4"::: Specifying the `@on{DOM EVENT}:preventDefault` attribute without a value is equivalent to `@on{DOM EVENT}:preventDefault="true"`. @@ -669,11 +826,19 @@ In the following example, selecting the checkbox prevents click events from the `Pages/EventHandlerExample7.razor`: -:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Pages/event-handling/EventHandlerExample7.razor" highlight="4,15-16"::: +:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Pages/event-handling/EventHandlerExample7.razor" highlight="4,15-16"::: + +## Focus an element + +Call <xref:Microsoft.AspNetCore.Components.ElementReferenceExtensions.FocusAsync%2A> on an [element reference](xref:blazor/js-interop/call-javascript-from-dotnet#capture-references-to-elements) to focus an element in code. In the following example, select the button to focus the `<input>` element. + +`Pages/EventHandlerExample8.razor`: + +:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Pages/event-handling/EventHandlerExample8.razor" highlight="16"::: :::moniker-end -:::moniker range=">= aspnetcore-7.0" +:::moniker range="< aspnetcore-5.0" Specify delegate event handlers in Razor component markup with [`@on{DOM EVENT}="{DELEGATE}"`](xref:mvc/views/razor#onevent) Razor syntax: @@ -693,7 +858,7 @@ The following code: `Pages/EventHandlerExample1.razor`: -:::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Pages/event-handling/EventHandlerExample1.razor" highlight="10,17,27-30,32-35"::: +:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Pages/event-handling/EventHandlerExample1.razor" highlight="10,17,27-30,32-35"::: In the following example, `UpdateHeading`: @@ -702,193 +867,36 @@ In the following example, `UpdateHeading`: `Pages/EventHandlerExample2.razor`: -:::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Pages/event-handling/EventHandlerExample2.razor" highlight="10,19-24"::: +:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Pages/event-handling/EventHandlerExample2.razor" highlight="10,19-24"::: ## Event arguments -### Built-in event arguments - For events that support an event argument type, specifying an event parameter in the event method definition is only necessary if the event type is used in the method. In the following example, <xref:Microsoft.AspNetCore.Components.Web.MouseEventArgs> is used in the `ReportPointerLocation` method to set message text that reports the mouse coordinates when the user selects a button in the UI. `Pages/EventHandlerExample3.razor`: -:::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Pages/event-handling/EventHandlerExample3.razor" highlight="17-20"::: +:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Pages/event-handling/EventHandlerExample3.razor" highlight="17-20"::: Supported <xref:System.EventArgs> are shown in the following table. -| Event | Class | [Document Object Model (DOM)](https://developer.mozilla.org/docs/Web/API/Document_Object_Model/Introduction) notes | -| ---------------- | ------ | --- | -| Clipboard | <xref:Microsoft.AspNetCore.Components.Web.ClipboardEventArgs> | | -| Drag | <xref:Microsoft.AspNetCore.Components.Web.DragEventArgs> | <xref:Microsoft.AspNetCore.Components.Web.DataTransfer> and <xref:Microsoft.AspNetCore.Components.Web.DataTransferItem> hold dragged item data.<br><br>Implement drag and drop in Blazor apps using [JS interop](xref:blazor/js-interop/call-javascript-from-dotnet) with [HTML Drag and Drop API](https://developer.mozilla.org/docs/Web/API/HTML_Drag_and_Drop_API). | -| Error | <xref:Microsoft.AspNetCore.Components.Web.ErrorEventArgs> | | -| Event | <xref:System.EventArgs> | <xref:Microsoft.AspNetCore.Components.Web.EventHandlers> holds attributes to configure the mappings between event names and event argument types. | -| Focus | <xref:Microsoft.AspNetCore.Components.Web.FocusEventArgs> | Doesn't include support for `relatedTarget`. | -| Input | <xref:Microsoft.AspNetCore.Components.ChangeEventArgs> | | -| Keyboard | <xref:Microsoft.AspNetCore.Components.Web.KeyboardEventArgs> | | -| Mouse | <xref:Microsoft.AspNetCore.Components.Web.MouseEventArgs> | | -| Mouse pointer | <xref:Microsoft.AspNetCore.Components.Web.PointerEventArgs> | | -| Mouse wheel | <xref:Microsoft.AspNetCore.Components.Web.WheelEventArgs> | | -| Progress | <xref:Microsoft.AspNetCore.Components.Web.ProgressEventArgs> | | -| Touch | <xref:Microsoft.AspNetCore.Components.Web.TouchEventArgs> | <xref:Microsoft.AspNetCore.Components.Web.TouchPoint> represents a single contact point on a touch-sensitive device. | - -For more information, see the following resources: - -* [`EventArgs` classes in the ASP.NET Core reference source (dotnet/aspnetcore `main` branch)](https://github.com/dotnet/aspnetcore/tree/main/src/Components/Web/src/Web) - - [!INCLUDE[](~/includes/aspnetcore-repo-ref-source-links.md)] - -* <xref:Microsoft.AspNetCore.Components.Web.EventHandlers> holds attributes to configure the mappings between event names and event argument types. - -### Custom event arguments - -Blazor supports custom event arguments, which enable you to pass arbitrary data to .NET event handlers with custom events. - -#### General configuration - -Custom events with custom event arguments are generally enabled with the following steps. - -1. In JavaScript, define a function for building the custom event argument object from the source event: - - ```javascript - function eventArgsCreator(event) { - return { - customProperty1: 'any value for property 1', - customProperty2: event.srcElement.value - }; - } - ``` - -1. Register the custom event with the preceding handler in `wwwroot/index.html` (Blazor WebAssembly) or `Pages/_Host.cshtml` (Blazor Server) immediately after the Blazor `<script>`: - - ```html - <script> - Blazor.registerCustomEventType('customevent', { - createEventArgs: eventArgsCreator; - }); - </script> - ``` - - > [!NOTE] - > The call to `registerCustomEventType` is performed in a script only once per event. - -1. Define a class for the event arguments: - - ```csharp - public class CustomEventArgs : EventArgs - { - public string? CustomProperty1 {get; set;} - public string? CustomProperty2 {get; set;} - } - ``` - -1. Wire up the custom event with the event arguments by adding an <xref:Microsoft.AspNetCore.Components.EventHandlerAttribute> attribute annotation for the custom event. The class doesn't require members. Note that the class *must* be called `EventHandlers` in order to be found by the Razor compiler, but you should put it in a namespace specific to your app: - - ```csharp - [EventHandler("oncustomevent", typeof(CustomEventArgs), enableStopPropagation: true, enablePreventDefault: true)] - static class EventHandlers - { - } - ``` - -1. Register the event handler on one or more HTML elements. Access the data that was passed in from JavaScript in the delegate handler method: - - ```razor - <button @oncustomevent="HandleCustomEvent">Handle</button> - - @code - { - void HandleCustomEvent(CustomEventArgs eventArgs) - { - // eventArgs.CustomProperty1 - // eventArgs.CustomProperty2 - } - } - ``` - -If the `@oncustomevent` attribute isn't recognized by [IntelliSense](/visualstudio/ide/using-intellisense), make sure that the component or the `_Imports.razor` file contains an `@using` statement for the namespace containing the `EventHandler` class. - -Whenever the custom event is fired on the DOM, the event handler is called with the data passed from the JavaScript. - -If you're attempting to fire a custom event, [`bubbles`](https://developer.mozilla.org/docs/Web/API/Event/bubbles) must be enabled by setting its value to `true`. Otherwise, the event doesn't reach the Blazor handler for processing into the C# custom <xref:Microsoft.AspNetCore.Components.EventHandlerAttribute> method. For more information, see [MDN Web Docs: Event bubbling](https://developer.mozilla.org/docs/Web/Guide/Events/Creating_and_triggering_events#event_bubbling). - -#### Custom clipboard paste event example - -The following example receives a custom clipboard paste event that includes the time of the paste and the user's pasted text. - -Declare a custom name (`oncustompaste`) for the event and a .NET class (`CustomPasteEventArgs`) to hold the event arguments for this event: - -`CustomEvents.cs`: - -```csharp -[EventHandler("oncustompaste", typeof(CustomPasteEventArgs), - enableStopPropagation: true, enablePreventDefault: true)] -public static class EventHandlers -{ -} - -public class CustomPasteEventArgs : EventArgs -{ - public DateTime EventTimestamp { get; set; } - public string? PastedData { get; set; } -} -``` - -Add JavaScript code to supply data for the <xref:System.EventArgs> subclass. In the `wwwroot/index.html` or `Pages/_Host.cshtml` file, add the following `<script>` tag and content immediately after the Blazor script. The following example only handles pasting text, but you could use arbitrary JavaScript APIs to deal with users pasting other types of data, such as images. - -`wwwroot/index.html` (Blazor WebAssembly) or `Pages/_Host.cshtml` (Blazor Server) immediately after the Blazor script: - -```html -<script> - Blazor.registerCustomEventType('custompaste', { - browserEventName: 'paste', - createEventArgs: event => { - return { - eventTimestamp: new Date(), - pastedData: event.clipboardData.getData('text') - }; - } - }); -</script> -``` - -The preceding code tells the browser that when a native [`paste`](https://developer.mozilla.org/docs/Web/API/Element/paste_event) event occurs: - -* Raise a `custompaste` event. -* Supply the event arguments data using the custom logic stated: - * For the `eventTimestamp`, create a new date. - * For the `pastedData`, get the clipboard data as text. For more information, see [MDN Web Docs: ClipboardEvent.clipboardData](https://developer.mozilla.org/docs/Web/API/ClipboardEvent/clipboardData). - -Event name conventions differ between .NET and JavaScript: - -* In .NET, event names are prefixed with "`on`". -* In JavaScript, event names don't have a prefix. - -In a Razor component, attach the custom handler to an element. - -`Pages/CustomPasteArguments.razor`: - -```razor -@page "/custom-paste-arguments" - -<label> - Try pasting into the following text box: - <input @oncustompaste="HandleCustomPaste" /> -</label> - -<p> - @message -</p> +| Event | Class | [Document Object Model (DOM)](https://developer.mozilla.org/docs/Web/API/Document_Object_Model/Introduction) events and notes | +| ---------------- | ----- | --- | +| Clipboard | <xref:Microsoft.AspNetCore.Components.Web.ClipboardEventArgs> | `oncut`, `oncopy`, `onpaste` | +| Drag | <xref:Microsoft.AspNetCore.Components.Web.DragEventArgs> | `ondrag`, `ondragstart`, `ondragenter`, `ondragleave`, `ondragover`, `ondrop`, `ondragend`<br><br><xref:Microsoft.AspNetCore.Components.Web.DataTransfer> and <xref:Microsoft.AspNetCore.Components.Web.DataTransferItem> hold dragged item data.<br><br>Implement drag and drop in Blazor apps using [JS interop](xref:blazor/js-interop/call-javascript-from-dotnet) with [HTML Drag and Drop API](https://developer.mozilla.org/docs/Web/API/HTML_Drag_and_Drop_API). | +| Error | <xref:Microsoft.AspNetCore.Components.Web.ErrorEventArgs> | `onerror` | +| Event | <xref:System.EventArgs> | *General*<br>`onactivate`, `onbeforeactivate`, `onbeforedeactivate`, `ondeactivate`, `onfullscreenchange`, `onfullscreenerror`, `onloadeddata`, `onloadedmetadata`, `onpointerlockchange`, `onpointerlockerror`, `onreadystatechange`, `onscroll`<br><br>*Clipboard*<br>`onbeforecut`, `onbeforecopy`, `onbeforepaste`<br><br>*Input*<br>`oninvalid`, `onreset`, `onselect`, `onselectionchange`, `onselectstart`, `onsubmit`<br><br>*Media*<br>`oncanplay`, `oncanplaythrough`, `oncuechange`, `ondurationchange`, `onemptied`, `onended`, `onpause`, `onplay`, `onplaying`, `onratechange`, `onseeked`, `onseeking`, `onstalled`, `onstop`, `onsuspend`, `ontimeupdate`, `onvolumechange`, `onwaiting`<br><br><xref:Microsoft.AspNetCore.Components.Web.EventHandlers> holds attributes to configure the mappings between event names and event argument types. | +| Focus | <xref:Microsoft.AspNetCore.Components.Web.FocusEventArgs> | `onfocus`, `onblur`, `onfocusin`, `onfocusout`<br><br>Doesn't include support for `relatedTarget`. | +| Input | <xref:Microsoft.AspNetCore.Components.ChangeEventArgs> | `onchange`, `oninput` | +| Keyboard | <xref:Microsoft.AspNetCore.Components.Web.KeyboardEventArgs> | `onkeydown`, `onkeypress`, `onkeyup` | +| Mouse | <xref:Microsoft.AspNetCore.Components.Web.MouseEventArgs> | `onclick`, `oncontextmenu`, `ondblclick`, `onmousedown`, `onmouseup`, `onmouseover`, `onmousemove`, `onmouseout` | +| Mouse pointer | <xref:Microsoft.AspNetCore.Components.Web.PointerEventArgs> | `onpointerdown`, `onpointerup`, `onpointercancel`, `onpointermove`, `onpointerover`, `onpointerout`, `onpointerenter`, `onpointerleave`, `ongotpointercapture`, `onlostpointercapture` | +| Mouse wheel | <xref:Microsoft.AspNetCore.Components.Web.WheelEventArgs> | `onwheel`, `onmousewheel` | +| Progress | <xref:Microsoft.AspNetCore.Components.Web.ProgressEventArgs> | `onabort`, `onload`, `onloadend`, `onloadstart`, `onprogress`, `ontimeout` | +| Touch | <xref:Microsoft.AspNetCore.Components.Web.TouchEventArgs> | `ontouchstart`, `ontouchend`, `ontouchmove`, `ontouchenter`, `ontouchleave`, `ontouchcancel`<br><br><xref:Microsoft.AspNetCore.Components.Web.TouchPoint> represents a single contact point on a touch-sensitive device. | -@code { - private string? message; +For more information, see [`EventArgs` classes in the ASP.NET Core reference source (dotnet/aspnetcore `main` branch)](https://github.com/dotnet/aspnetcore/tree/main/src/Components/Web/src/Web) - private void HandleCustomPaste(CustomPasteEventArgs eventArgs) - { - message = $"At {eventArgs.EventTimestamp.ToShortTimeString()}, " + - $"you pasted: {eventArgs.PastedData}"; - } -} -``` +[!INCLUDE[](~/includes/aspnetcore-repo-ref-source-links.md)] ## Lambda expressions @@ -896,7 +904,7 @@ In a Razor component, attach the custom handler to an element. `Pages/EventHandlerExample4.razor`: -:::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Pages/event-handling/EventHandlerExample4.razor" highlight="6"::: +:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Pages/event-handling/EventHandlerExample4.razor" highlight="6"::: It's often convenient to close over additional values using C# method parameters, such as when iterating over a set of elements. The following example creates three buttons, each of which calls `UpdateHeading` and passes the following data: @@ -905,7 +913,7 @@ It's often convenient to close over additional values using C# method parameters `Pages/EventHandlerExample5.razor`: -:::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Pages/event-handling/EventHandlerExample5.razor" highlight="10,19"::: +:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Pages/event-handling/EventHandlerExample5.razor" highlight="10,19"::: > [!NOTE] > Do **not** use a loop variable directly in a lambda expression, such as `i` in the preceding `for` loop example. Otherwise, the same variable is used by all lambda expressions, which results in use of the same value in all lambdas. Always capture the variable's value in a local variable and then use it. In the preceding example: @@ -923,13 +931,13 @@ The following `Child` component demonstrates how a button's `onclick` handler is `Shared/Child.razor`: -:::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Shared/event-handling/Child.razor"::: +:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Shared/event-handling/Child.razor"::: The `Parent` component sets the child's <xref:Microsoft.AspNetCore.Components.EventCallback%601> (`OnClickCallback`) to its `ShowMessage` method. `Pages/Parent.razor`: -:::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Pages/event-handling/Parent.razor"::: +:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Pages/event-handling/Parent.razor"::: When the button is selected in the `ChildComponent`: @@ -961,7 +969,7 @@ When a key is selected on an input device and the element focus is on a text box `Pages/EventHandlerExample6.razor`: -:::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Pages/event-handling/EventHandlerExample6.razor" highlight="4"::: +:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Pages/event-handling/EventHandlerExample6.razor" highlight="4"::: Specifying the `@on{DOM EVENT}:preventDefault` attribute without a value is equivalent to `@on{DOM EVENT}:preventDefault="true"`. @@ -990,14 +998,6 @@ In the following example, selecting the checkbox prevents click events from the `Pages/EventHandlerExample7.razor`: -:::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Pages/event-handling/EventHandlerExample7.razor" highlight="4,15-16"::: - -## Focus an element - -Call <xref:Microsoft.AspNetCore.Components.ElementReferenceExtensions.FocusAsync%2A> on an [element reference](xref:blazor/js-interop/call-javascript-from-dotnet#capture-references-to-elements) to focus an element in code. In the following example, select the button to focus the `<input>` element. - -`Pages/EventHandlerExample8.razor`: - -:::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Pages/event-handling/EventHandlerExample8.razor" highlight="16"::: +:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Pages/event-handling/EventHandlerExample7.razor" highlight="4,15-16"::: :::moniker-end diff --git a/aspnetcore/blazor/components/index.md b/aspnetcore/blazor/components/index.md index f62cf51210e8..4163ee5cccff 100644 --- a/aspnetcore/blazor/components/index.md +++ b/aspnetcore/blazor/components/index.md @@ -5,14 +5,14 @@ description: Learn how to create and use Razor components in Blazor apps, includ monikerRange: '>= aspnetcore-3.1' ms.author: riande ms.custom: mvc -ms.date: 11/09/2021 +ms.date: 11/08/2022 uid: blazor/components/index --- # ASP.NET Core Razor components This article explains how to create and use Razor components in Blazor apps, including guidance on Razor syntax, component naming, namespaces, and component parameters. -:::moniker range=">= aspnetcore-6.0 < aspnetcore-7.0" +:::moniker range=">= aspnetcore-7.0" Blazor apps are built using *Razor components*, informally known as *Blazor components*. A component is a self-contained portion of user interface (UI) with processing logic to enable dynamic behavior. Components can be nested, reused, shared among projects, and [used in MVC and Razor Pages apps](xref:blazor/components/prerendering-and-integration). @@ -55,11 +55,11 @@ Common Blazor naming conventions used throughout the Blazor documentation includ Routing in Blazor is achieved by providing a route template to each accessible component in the app with an [`@page`][9] directive. When a Razor file with an [`@page`][9] directive is compiled, the generated class is given a <xref:Microsoft.AspNetCore.Mvc.RouteAttribute> specifying the route template. At runtime, the router searches for component classes with a <xref:Microsoft.AspNetCore.Mvc.RouteAttribute> and renders whichever component has a route template that matches the requested URL. -The following `HelloWorld` component uses a route template of `/hello-world`. The rendered webpage for the component is reached at the relative URL `/hello-world`. When running a Blazor app locally with the default protocol, host, and port, the `HelloWorld` component is requested in the browser at `https://localhost:5001/hello-world`. Components that produce webpages usually reside in the `Pages` folder, but you can use any folder to hold components, including within nested folders. +The following `HelloWorld` component uses a route template of `/hello-world`, and the rendered webpage for the component is reached at the relative URL `/hello-world`. Components that produce webpages usually reside in the `Pages` folder, but you can use any folder to hold components, including within nested folders. `Pages/HelloWorld.razor`: -:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Pages/index/HelloWorld.razor"::: +:::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Pages/index/HelloWorld.razor"::: The preceding component loads in the browser at `/hello-world` regardless of whether or not you add the component to the app's UI navigation. Optionally, components can be added to the `NavMenu` component so that a link to the component appears in the app's UI-based navigation. @@ -82,7 +82,7 @@ Component members are used in rendering logic using C# expressions that start wi `Pages/Markup.razor`: -:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Pages/index/Markup.razor"::: +:::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Pages/index/Markup.razor"::: > [!NOTE] > Examples throughout the Blazor documentation specify the [`private` access modifier](/dotnet/csharp/language-reference/keywords/private) for private members. Private members are scoped to a component's class. However, C# assumes the `private` access modifier when no access modifier is present, so explicitly marking members "`private`" in your own code is optional. For more information on access modifiers, see [Access Modifiers (C# Programming Guide)](/dotnet/csharp/programming-guide/classes-and-structs/access-modifiers). @@ -105,13 +105,13 @@ Consider the following `Heading` component, which can be used by other component `Shared/Heading.razor`: -:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Shared/index/Heading.razor"::: +:::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Shared/index/Heading.razor"::: The following markup in the `HeadingExample` component renders the preceding `Heading` component at the location where the `<Heading />` tag appears. `Pages/HeadingExample.razor`: -:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Pages/index/HeadingExample.razor"::: +:::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Pages/index/HeadingExample.razor"::: If a component contains an HTML element with an uppercase first letter that doesn't match a component name within the same namespace, a warning is emitted indicating that the element has an unexpected name. Adding an [`@using`][2] directive for the component's namespace makes the component available, which resolves the warning. For more information, see the [Namespaces](#namespaces) section. @@ -167,13 +167,13 @@ The following example shows the default `Counter` component with an [`@code`][1] `Pages/Counter.razor`: -:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Pages/Counter.razor"::: +:::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Pages/Counter.razor"::: The following `Counter` component splits HTML and Razor markup from C# code using a code-behind file with a partial class: `Pages/CounterPartialClass.razor`: -:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Pages/index/CounterPartialClass.razor"::: +:::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Pages/index/CounterPartialClass.razor"::: `Pages/CounterPartialClass.razor.cs`: @@ -220,11 +220,11 @@ The [`@inherits`][6] directive is used to specify a base class for a component. `Pages/BlazorRocks.razor`: -:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Pages/index/BlazorRocks.razor"::: +:::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Pages/index/BlazorRocks.razor"::: `BlazorRocksBase.cs`: -:::code language="csharp" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/BlazorRocksBase.cs"::: +:::code language="csharp" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/BlazorRocksBase.cs"::: ## Component parameters @@ -232,11 +232,11 @@ The [`@inherits`][6] directive is used to specify a base class for a component. `PanelBody.cs`: -:::code language="csharp" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/PanelBody.cs"::: +:::code language="csharp" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/PanelBody.cs"::: `Shared/ParameterChild.razor`: -:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Shared/index/ParameterChild.razor"::: +:::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Shared/index/ParameterChild.razor"::: > [!WARNING] > Providing initial values for component parameters is supported, but don't create a component that writes to its own parameters after the component is rendered for the first time. For more information, see the [Overwritten parameters](#overwritten-parameters) section of this article. @@ -248,7 +248,7 @@ The `Title` and `Body` component parameters of the `ParameterChild` component ar `Pages/ParameterParent.razor`: -:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Pages/index/ParameterParent.razor"::: +:::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Pages/index/ParameterParent.razor"::: The following rendered HTML markup from the `ParameterParent` component shows `ParameterChild` component default values when the `ParameterParent` component doesn't supply component parameter values. When the `ParameterParent` component provides component parameter values, they replace the `ParameterChild` component's default values. @@ -294,7 +294,7 @@ Throughout the documentation, code examples: `Pages/ParameterParent2.razor`: -:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Pages/index/ParameterParent2.razor"::: +:::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Pages/index/ParameterParent2.razor"::: > [!NOTE] > When assigning a C# member to a component parameter, prefix the member with the `@` symbol and never prefix the parameter's HTML attribute. @@ -352,7 +352,7 @@ To support the assignment of a composed value, use a method, field, or property. `Pages/ParameterParent3.razor`: -:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Pages/index/ParameterParent3.razor"::: +:::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Pages/index/ParameterParent3.razor"::: For more information, see <xref:mvc/views/razor>. @@ -447,7 +447,7 @@ Optional route parameters are supported. In the following example, the `text` op `Pages/RouteParameter.razor`: -:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Pages/index/RouteParameter.razor" highlight="1,6-7"::: +:::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Pages/index/RouteParameter.razor" highlight="1,6-7"::: For information on catch-all route parameters (`{*pageRoute}`), which capture paths across multiple folder boundaries, see <xref:blazor/fundamentals/routing#catch-all-route-parameters>. @@ -459,7 +459,7 @@ In the following example, the `RenderFragmentChild` component has a `ChildConten `Shared/RenderFragmentChild.razor`: -:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Shared/index/RenderFragmentChild.razor"::: +:::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Shared/index/RenderFragmentChild.razor"::: > [!IMPORTANT] > The property receiving the <xref:Microsoft.AspNetCore.Components.RenderFragment> content must be named `ChildContent` by convention. @@ -470,7 +470,7 @@ The following `RenderFragmentParent` component provides content for rendering th `Pages/RenderFragmentParent.razor`: -:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Pages/index/RenderFragmentParent.razor"::: +:::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Pages/index/RenderFragmentParent.razor"::: Due to the way that Blazor renders child content, rendering components inside a [`for`](/dotnet/csharp/language-reference/keywords/for) loop requires a local index variable if the incrementing loop variable is used in the `RenderFragmentChild` component's content. The following example can be added to the preceding `RenderFragmentParent` component: @@ -560,7 +560,7 @@ After the following `Expander` component demonstrates an overwritten parameter, `Shared/Expander.razor`: -:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Shared/index/BadExpander.razor"::: +:::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Shared/index/BadExpander.razor"::: The `Expander` component is added to the following `ExpanderExample` parent component that may call <xref:Microsoft.AspNetCore.Components.ComponentBase.StateHasChanged%2A>: @@ -569,7 +569,7 @@ The `Expander` component is added to the following `ExpanderExample` parent comp `Pages/ExpanderExample.razor`: -:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Pages/index/ExpanderExample.razor"::: +:::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Pages/index/ExpanderExample.razor"::: Initially, the `Expander` components behave independently when their `Expanded` properties are toggled. The child components maintain their states as expected. @@ -596,7 +596,7 @@ The following revised `Expander` component: `Shared/Expander.razor`: -:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Shared/index/Expander.razor"::: +:::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Shared/index/Expander.razor"::: For two-way parent-child binding examples, see <xref:blazor/components/data-binding#binding-with-component-parameters>. For additional information, see [Blazor Two Way Binding Error (dotnet/aspnetcore #24599)](https://github.com/dotnet/aspnetcore/issues/24599). @@ -613,7 +613,7 @@ In the following `Splat` component: `Pages/Splat.razor`: -:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Pages/index/Splat.razor"::: +:::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Pages/index/Splat.razor"::: The rendered `<input>` elements in the webpage are identical: @@ -646,11 +646,11 @@ The position of [`@attributes`][3] relative to the position of element attribute `Shared/AttributeOrderChild1.razor`: -:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Shared/index/AttributeOrderChild1.razor"::: +:::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Shared/index/AttributeOrderChild1.razor"::: `Pages/AttributeOrderParent1.razor`: -:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Pages/index/AttributeOrderParent1.razor"::: +:::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Pages/index/AttributeOrderParent1.razor"::: The `AttributeOrderChild1` component's `extra` attribute is set to the right of [`@attributes`][3]. The `AttributeOrderParent1` component's rendered `<div>` contains `extra="5"` when passed through the additional attribute because the attributes are processed right to left (last to first): @@ -662,11 +662,11 @@ In the following example, the order of `extra` and [`@attributes`][3] is reverse `Shared/AttributeOrderChild2.razor`: -:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Shared/index/AttributeOrderChild2.razor"::: +:::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Shared/index/AttributeOrderChild2.razor"::: `Pages/AttributeOrderParent2.razor`: -:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Pages/index/AttributeOrderParent2.razor"::: +:::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Pages/index/AttributeOrderParent2.razor"::: The `<div>` in the parent component's rendered webpage contains `extra="10"` when passed through the additional attribute: @@ -687,7 +687,7 @@ Consider the following `ReferenceChild` component that logs a message when its ` `Shared/ReferenceChild.razor`: -:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Shared/index/ReferenceChild.razor"::: +:::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Shared/index/ReferenceChild.razor"::: A component reference is only populated after the component is rendered and its output includes `ReferenceChild`'s element. Until the component is rendered, there's nothing to reference. @@ -699,13 +699,13 @@ The following lambda approach uses the preceding `ReferenceChild` component. `Pages/ReferenceParent1.razor`: -:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Pages/index/ReferenceParent1.razor"::: +:::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Pages/index/ReferenceParent1.razor"::: The following delegate approach uses the preceding `ReferenceChild` component. `Pages/ReferenceParent2.razor`: -:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Pages/index/ReferenceParent2.razor"::: +:::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Pages/index/ReferenceParent2.razor"::: While capturing component references use a similar syntax to [capturing element references](xref:blazor/js-interop/call-javascript-from-dotnet#capture-references-to-elements), capturing component references isn't a JavaScript interop feature. Component references aren't passed to JavaScript code. Component references are only used in .NET code. @@ -738,11 +738,11 @@ In the event a component must be updated based on an external event, such as a t `TimerService.cs`: -:::code language="csharp" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/TimerService.cs"::: +:::code language="csharp" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/TimerService.cs"::: `NotifierService.cs`: -:::code language="csharp" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/NotifierService.cs"::: +:::code language="csharp" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/NotifierService.cs"::: Register the services: @@ -764,7 +764,7 @@ Use the `NotifierService` to update a component. `Pages/ReceiveNotifications.razor`: -:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Pages/index/ReceiveNotifications.razor"::: +:::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Pages/index/ReceiveNotifications.razor"::: In the preceding example: @@ -792,13 +792,13 @@ This demonstration allows you to: `Shared/Details.razor`: -:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Shared/index/Details.razor"::: +:::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Shared/index/Details.razor"::: In the following `People` component, each iteration of adding a person in `OnTimerCallback` results in Blazor rebuilding the entire collection. The page's focus remains on the *same index* position of `<input>` elements, so the focus shifts each time a person is added. *Shifting the focus away from what the user selected isn't desirable behavior.* After demonstrating the poor behavior with the following component, the [`@key`][5] directive attribute is used to improve the user's experience. `Pages/People.razor`: -:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Pages/index/People.razor"::: +:::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Pages/index/People.razor"::: The contents of the `people` collection changes with inserted, deleted, or re-ordered entries. Rerendering can lead to visible behavior differences. For example, each time a person is inserted into the `people` collection, the user's focus is lost. @@ -958,7 +958,7 @@ In the following example, `IsCompleted` determines if the `<input>` element's `c `Pages/ConditionalAttribute.razor`: -:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Pages/index/ConditionalAttribute.razor"::: +:::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Pages/index/ConditionalAttribute.razor"::: For more information, see <xref:mvc/views/razor>. @@ -976,7 +976,7 @@ The following example shows using the <xref:Microsoft.AspNetCore.Components.Mark `Pages/MarkupStringExample.razor`: -:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Pages/index/MarkupStringExample.razor"::: +:::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Pages/index/MarkupStringExample.razor"::: ## Razor templates @@ -990,7 +990,7 @@ The following example illustrates how to specify <xref:Microsoft.AspNetCore.Comp `Pages/RazorTemplate.razor`: -:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Pages/index/RazorTemplate.razor"::: +:::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Pages/index/RazorTemplate.razor"::: Rendered output of the preceding code: @@ -1106,7 +1106,7 @@ In the following example, the `ListGenericTypeItems1` component is generically t `Shared/ListGenericTypeItems1.razor`: -:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Shared/index/ListGenericTypeItems1.razor"::: +:::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Shared/index/ListGenericTypeItems1.razor"::: The following `GenericTypeExample1` component renders two `ListGenericTypeItems1` components: @@ -1115,7 +1115,7 @@ The following `GenericTypeExample1` component renders two `ListGenericTypeItems1 `Pages/GenericTypeExample1.razor`: -:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Pages/index/GenericTypeExample1.razor"::: +:::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Pages/index/GenericTypeExample1.razor"::: For more information, see <xref:mvc/views/razor#typeparam>. For an example of generic typing with templated components, see <xref:blazor/components/templated-components>. @@ -1466,61 +1466,109 @@ await Blazor.rootComponents.add(containerElement, 'counter', { incrementAmount: ## Blazor custom elements -*Experimental* support is available for building custom elements using the [`Microsoft.AspNetCore.Components.CustomElements` NuGet package](https://www.nuget.org/packages/microsoft.aspnetcore.components.customelements). Custom elements use standard HTML interfaces to implement custom HTML elements. +Use Blazor custom elements to dynamically render Razor components from other SPA frameworks, such as Angular or React. -> [!WARNING] -> Experimental features are provided for the purpose of exploring feature viability and may not ship in a stable version. +Blazor custom elements: -Register a root component as a custom element: +* Use standard HTML interfaces to implement custom HTML elements. +* Eliminate the need to manually manage the state and lifecycle of root Razor components using JavaScript APIs. +* Are useful for gradually introducing Razor components into existing projects written in other SPA frameworks. -* In a Blazor Server app, modify the call to <xref:Microsoft.Extensions.DependencyInjection.ComponentServiceCollectionExtensions.AddServerSideBlazor%2A> in `Program.cs`: +### Blazor Server registration - ```csharp - builder.Services.AddServerSideBlazor(options => - { - options.RootComponents.RegisterAsCustomElement<Counter>("my-counter"); - }); - ``` - - > [!NOTE] - > The preceding code example requires a namespace for the app's components (for example, `using BlazorSample.Pages;`) in the `Program.cs` file. +To register a root component as a custom element in a Blazor Server app, modify the call to <xref:Microsoft.Extensions.DependencyInjection.ComponentServiceCollectionExtensions.AddServerSideBlazor%2A> in `Program.cs`. The following example registers the `Counter` component with the custom HTML element `my-counter`: -* In a Blazor WebAssembly app, call `RegisterAsCustomElement` on <xref:Microsoft.AspNetCore.Components.WebAssembly.Hosting.WebAssemblyHostBuilder.RootComponents> in `Program.cs`: +```csharp +builder.Services.AddServerSideBlazor(options => +{ + options.RootComponents.RegisterCustomElement<Counter>("my-counter"); +}); +``` - ```csharp - builder.RootComponents.RegisterAsCustomElement<Counter>("my-counter"); - ``` - - > [!NOTE] - > The preceding code example requires a namespace for the app's components (for example, `using BlazorSample.Pages;`) in the `Program.cs` file. +> [!NOTE] +> The preceding code example requires a namespace for the app's components (for example, `using BlazorSample.Pages;`) in the `Program.cs` file. -Include the following `<script>` tag in the app's HTML ***before*** the Blazor script tag: +### Blazor WebAssembly registration -```html -<script src="/_content/Microsoft.AspNetCore.Components.CustomElements/BlazorCustomElements.js"></script> +To register a root component as a custom element in a Blazor WebAssembly app, call `RegisterCustomElement` on <xref:Microsoft.AspNetCore.Components.WebAssembly.Hosting.WebAssemblyHostBuilder.RootComponents> in `Program.cs`. The following example registers the `Counter` component with the custom HTML element `my-counter`: + +```csharp +builder.RootComponents.RegisterCustomElement<Counter>("my-counter"); ``` -Use the custom element with any web framework. For example, the preceding counter custom element is used in a React app with the following markup: +> [!NOTE] +> The preceding code example requires a namespace for the app's components (for example, `using BlazorSample.Pages;`) in the `Program.cs` file. + +### Use the registered custom element + +Use the custom element with any web framework. For example, the preceding `my-counter` custom HTML element is used in a React app with the following markup: ```html <my-counter increment-amount={incrementAmount}></my-counter> ``` -For a complete example of how to create custom elements with Blazor, see the [Blazor Custom Elements sample project](https://github.com/aspnet/AspLabs/tree/main/src/BlazorCustomElements). +For a complete example of how to create custom elements with Blazor, see the [`CustomElementsComponent` component](https://github.com/dotnet/aspnetcore/blob/main/src/Components/test/testassets/BasicTestApp/CustomElementsComponent.razor) in the reference source. -> [!WARNING] -> The custom elements feature is currently **experimental, unsupported, and subject to change or be removed at any time**. We welcome your feedback on how well this particular approach meets your requirements. +[!INCLUDE[](~/includes/aspnetcore-repo-ref-source-links.md)] ## Generate Angular and React components -Generate framework-specific JavaScript (JS) components from Razor components for web frameworks, such as Angular or React. This capability isn't included with .NET 6, but is enabled by the new support for rendering Razor components from JS. The [JS component generation sample on GitHub](https://github.com/aspnet/samples/tree/main/samples/aspnetcore/blazor/JSComponentGeneration) demonstrates how to generate Angular and React components from Razor components. See the GitHub sample app's `README.md` file for additional information. +Generate framework-specific JavaScript (JS) components from Razor components for web frameworks, such as Angular or React. This capability isn't included with .NET, but is enabled by the support for rendering Razor components from JS. The [JS component generation sample on GitHub](https://github.com/aspnet/samples/tree/main/samples/aspnetcore/blazor/JSComponentGeneration) demonstrates how to generate Angular and React components from Razor components. See the GitHub sample app's `README.md` file for additional information. > [!WARNING] > The Angular and React component features are currently **experimental, unsupported, and subject to change or be removed at any time**. We welcome your feedback on how well this particular approach meets your requirements. +## `QuickGrid` component + +The `QuickGrid` component is an experimental Razor component for quickly and efficiently displaying data in tabular form. `QuickGrid` provides a simple and convenient data grid component for common grid rendering scenarios and serves as a reference architecture and performance baseline for building data grid components. `QuickGrid` is highly optimized and uses advanced techniques to achieve optimal rendering performance. + +To get started with `QuickGrid`: + +1. Add package reference for [`Microsoft.AspNetCore.Components.QuickGrid`](https://www.nuget.org/packages/Microsoft.AspNetCore.Components.QuickGrid). If using the .NET CLI to add the package reference, include the `--prerelease` option when you execute the [`dotnet add package` command](/dotnet/core/tools/dotnet-add-package). + + [!INCLUDE[](~/includes/package-reference.md)] + +1. Add the following component to render a grid. + + `Pages/QuickGridExample.razor`: + + ```razor + @page "/quickgrid-example" + @using Microsoft.AspNetCore.Components.QuickGrid + + <QuickGrid Items="@people"> + <PropertyColumn Property="@(p => p.PersonId)" Sortable="true" /> + <PropertyColumn Property="@(p => p.Name)" Sortable="true" /> + <PropertyColumn Property="@(p => p.BirthDate)" Format="yyyy-MM-dd" Sortable="true" /> + </QuickGrid> + + @code { + private record Person(int PersonId, string Name, DateOnly BirthDate); + + private IQueryable<Person> people = new[] + { + new Person(10895, "Jean Martin", new DateOnly(1985, 3, 16)), + new Person(10944, "António Langa", new DateOnly(1991, 12, 1)), + new Person(11203, "Julie Smith", new DateOnly(1958, 10, 10)), + new Person(11205, "Nur Sari", new DateOnly(1922, 4, 27)), + new Person(11898, "Jose Hernandez", new DateOnly(2011, 5, 3)), + new Person(12130, "Kenji Sato", new DateOnly(2004, 1, 9)), + }.AsQueryable(); + } + ``` + +1. Access the component in a browser at the relative path `/quickgrid-example`. + +For various `QuickGrid` demonstrations, see the [**QuickGrid for Blazor** app](https://aspnet.github.io/quickgridsamples/). The demo site is built using Blazor WebAssembly and is hosted on GitHub Pages. The site loads fast thanks to static prerendering using the community-maintained [`BlazorWasmPrerendering.Build` GitHub project](https://github.com/jsakamoto/BlazorWasmPreRendering.Build). + +There aren't current plans to extend `QuickGrid` with features that full-blown commercial grids tend to offer, for example, hierarchical rows, drag-to-reorder columns, or Excel-like range selections. If you require advanced features that you don't wish to develop on your own, continue using third-party grids. + +> [!WARNING] +> `QuickGrid` is currently **prerelease**, **experimental**, and **unsupported** by Microsoft. `QuickGrid` is ***not*** recommended for use in production apps. + :::moniker-end -:::moniker range=">= aspnetcore-5.0 < aspnetcore-6.0" +:::moniker range=">= aspnetcore-6.0 < aspnetcore-7.0" Blazor apps are built using *Razor components*, informally known as *Blazor components*. A component is a self-contained portion of user interface (UI) with processing logic to enable dynamic behavior. Components can be nested, reused, shared among projects, and [used in MVC and Razor Pages apps](xref:blazor/components/prerendering-and-integration). @@ -1567,7 +1615,7 @@ The following `HelloWorld` component uses a route template of `/hello-world`. Th `Pages/HelloWorld.razor`: -:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Pages/index/HelloWorld.razor"::: +:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Pages/index/HelloWorld.razor"::: The preceding component loads in the browser at `/hello-world` regardless of whether or not you add the component to the app's UI navigation. Optionally, components can be added to the `NavMenu` component so that a link to the component appears in the app's UI-based navigation. @@ -1590,7 +1638,7 @@ Component members are used in rendering logic using C# expressions that start wi `Pages/Markup.razor`: -:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Pages/index/Markup.razor"::: +:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Pages/index/Markup.razor"::: > [!NOTE] > Examples throughout the Blazor documentation specify the [`private` access modifier](/dotnet/csharp/language-reference/keywords/private) for private members. Private members are scoped to a component's class. However, C# assumes the `private` access modifier when no access modifier is present, so explicitly marking members "`private`" in your own code is optional. For more information on access modifiers, see [Access Modifiers (C# Programming Guide)](/dotnet/csharp/programming-guide/classes-and-structs/access-modifiers). @@ -1613,13 +1661,13 @@ Consider the following `Heading` component, which can be used by other component `Shared/Heading.razor`: -:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Shared/index/Heading.razor"::: +:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Shared/index/Heading.razor"::: The following markup in the `HeadingExample` component renders the preceding `Heading` component at the location where the `<Heading />` tag appears. `Pages/HeadingExample.razor`: -:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Pages/index/HeadingExample.razor"::: +:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Pages/index/HeadingExample.razor"::: If a component contains an HTML element with an uppercase first letter that doesn't match a component name within the same namespace, a warning is emitted indicating that the element has an unexpected name. Adding an [`@using`][2] directive for the component's namespace makes the component available, which resolves the warning. For more information, see the [Namespaces](#namespaces) section. @@ -1675,21 +1723,13 @@ The following example shows the default `Counter` component with an [`@code`][1] `Pages/Counter.razor`: -:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Pages/Counter.razor"::: +:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Pages/Counter.razor"::: The following `Counter` component splits HTML and Razor markup from C# code using a code-behind file with a partial class: `Pages/CounterPartialClass.razor`: -```razor -@page "/counter-partial-class" - -<h1>Counter</h1> - -<p>Current count: @currentCount</p> - -<button class="btn btn-primary" @onclick="IncrementCount">Click me</button> -``` +:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Pages/index/CounterPartialClass.razor"::: `Pages/CounterPartialClass.razor.cs`: @@ -1736,11 +1776,11 @@ The [`@inherits`][6] directive is used to specify a base class for a component. `Pages/BlazorRocks.razor`: -:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Pages/index/BlazorRocks.razor"::: +:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Pages/index/BlazorRocks.razor"::: `BlazorRocksBase.cs`: -:::code language="csharp" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/BlazorRocksBase.cs"::: +:::code language="csharp" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/BlazorRocksBase.cs"::: ## Component parameters @@ -1748,11 +1788,11 @@ The [`@inherits`][6] directive is used to specify a base class for a component. `PanelBody.cs`: -:::code language="csharp" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/PanelBody.cs"::: +:::code language="csharp" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/PanelBody.cs"::: `Shared/ParameterChild.razor`: -:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Shared/index/ParameterChild.razor"::: +:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Shared/index/ParameterChild.razor"::: > [!WARNING] > Providing initial values for component parameters is supported, but don't create a component that writes to its own parameters after the component is rendered for the first time. For more information, see the [Overwritten parameters](#overwritten-parameters) section of this article. @@ -1764,7 +1804,7 @@ The `Title` and `Body` component parameters of the `ParameterChild` component ar `Pages/ParameterParent.razor`: -:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Pages/index/ParameterParent.razor"::: +:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Pages/index/ParameterParent.razor"::: The following rendered HTML markup from the `ParameterParent` component shows `ParameterChild` component default values when the `ParameterParent` component doesn't supply component parameter values. When the `ParameterParent` component provides component parameter values, they replace the `ParameterChild` component's default values. @@ -1810,7 +1850,7 @@ Throughout the documentation, code examples: `Pages/ParameterParent2.razor`: -:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Pages/index/ParameterParent2.razor"::: +:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Pages/index/ParameterParent2.razor"::: > [!NOTE] > When assigning a C# member to a component parameter, prefix the member with the `@` symbol and never prefix the parameter's HTML attribute. @@ -1843,7 +1883,7 @@ To obtain a value for the `Title` parameter in the preceding example asynchronou <ParameterChild Title="@title" /> @code { - private string title; + private string? title; protected override async Task OnInitializedAsync() { @@ -1868,7 +1908,7 @@ To support the assignment of a composed value, use a method, field, or property. `Pages/ParameterParent3.razor`: -:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Pages/index/ParameterParent3.razor"::: +:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Pages/index/ParameterParent3.razor"::: For more information, see <xref:mvc/views/razor>. @@ -1900,27 +1940,82 @@ public DateTime StartData { get; set; } = DateTime.Now; After the initial assignment of <xref:System.DateTime.Now?displayProperty=nameWithType>, do **not** assign a value to `StartData` in developer code. For more information, see the [Overwritten parameters](#overwritten-parameters) section of this article. -## Route parameters - -Components can specify route parameters in the route template of the [`@page`][9] directive. The [Blazor router](xref:blazor/fundamentals/routing) uses route parameters to populate corresponding component parameters. +Apply the [`[EditorRequired]` attribute](xref:Microsoft.AspNetCore.Components.EditorRequiredAttribute) to specify a required component parameter. If a parameter value isn't provided, editors or build tools may display warnings to the user. This attribute is only valid on properties also marked with the [`[Parameter]` attribute](xref:Microsoft.AspNetCore.Components.ParameterAttribute). The <xref:Microsoft.AspNetCore.Components.EditorRequiredAttribute> is enforced at design-time and when the app is built. The attribute isn't enforced at runtime, and it doesn't guarantee a non-`null` parameter value. -Optional route parameters are supported. In the following example, the `text` optional parameter assigns the value of the route segment to the component's `Text` property. If the segment isn't present, the value of `Text` is set to "`fantastic`" in the [`OnInitialized` lifecycle method](xref:blazor/components/lifecycle#component-initialization-oninitializedasync). +```csharp +[Parameter] +[EditorRequired] +public string? Title { get; set; } +``` -`Pages/RouteParameter.razor`: +Single-line attribute lists are also supported: -:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Pages/index/RouteParameter.razor" highlight="1,6-7"::: +```csharp +[Parameter, EditorRequired] +public string? Title { get; set; } +``` -For information on catch-all route parameters (`{*pageRoute}`), which capture paths across multiple folder boundaries, see <xref:blazor/fundamentals/routing#catch-all-route-parameters>. +[`Tuples`](/dotnet/csharp/language-reference/builtin-types/value-tuples) ([API documentation](xref:System.Tuple)) are supported for component parameters and [`RenderFragment`](#child-content-render-fragments) types. The following component parameter example passes three values in a `Tuple`: -## Child content render fragments +`Shared/RenderTupleChild.razor`: -Components can set the content of another component. The assigning component provides the content between the child component's opening and closing tags. +```csharp +<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> -In the following example, the `RenderFragmentChild` component has a `ChildContent` component parameter that represents a segment of the UI to render as a <xref:Microsoft.AspNetCore.Components.RenderFragment>. The position of `ChildContent` in the component's Razor markup is where the content is rendered in the final HTML output. +@code { + [Parameter] + public Tuple<int, string, bool>? Data { get; set; } +} +``` + +`Pages/RenderTupleParent.razor`: + +```csharp +@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); +} +``` + +Only ***unnamed tuples*** are supported for C# 7.0 or later in Razor components. [Named tuples](/dotnet/csharp/language-reference/builtin-types/value-tuples#tuple-field-names) support in Razor components is planned for a future ASP.NET Core release. For more information, see [Blazor Transpiler issue with named Tuples (dotnet/aspnetcore #28982)](https://github.com/dotnet/aspnetcore/issues/28982). + +Quote ©2005 [Universal Pictures](https://www.uphe.com): [Serenity](https://www.uphe.com/movies/serenity-2005) ([Nathan Fillion](https://www.imdb.com/name/nm0277213/)) + +## Route parameters + +Components can specify route parameters in the route template of the [`@page`][9] directive. The [Blazor router](xref:blazor/fundamentals/routing) uses route parameters to populate corresponding component parameters. + +Optional route parameters are supported. In the following example, the `text` optional parameter assigns the value of the route segment to the component's `Text` property. If the segment isn't present, the value of `Text` is set to "`fantastic`" in the [`OnInitialized` lifecycle method](xref:blazor/components/lifecycle#component-initialization-oninitializedasync). + +`Pages/RouteParameter.razor`: + +:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Pages/index/RouteParameter.razor" highlight="1,6-7"::: + +For information on catch-all route parameters (`{*pageRoute}`), which capture paths across multiple folder boundaries, see <xref:blazor/fundamentals/routing#catch-all-route-parameters>. + +## Child content render fragments + +Components can set the content of another component. The assigning component provides the content between the child component's opening and closing tags. + +In the following example, the `RenderFragmentChild` component has a `ChildContent` component parameter that represents a segment of the UI to render as a <xref:Microsoft.AspNetCore.Components.RenderFragment>. The position of `ChildContent` in the component's Razor markup is where the content is rendered in the final HTML output. `Shared/RenderFragmentChild.razor`: -:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Shared/index/RenderFragmentChild.razor"::: +:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Shared/index/RenderFragmentChild.razor"::: > [!IMPORTANT] > The property receiving the <xref:Microsoft.AspNetCore.Components.RenderFragment> content must be named `ChildContent` by convention. @@ -1931,7 +2026,7 @@ The following `RenderFragmentParent` component provides content for rendering th `Pages/RenderFragmentParent.razor`: -:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Pages/index/RenderFragmentParent.razor"::: +:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Pages/index/RenderFragmentParent.razor"::: Due to the way that Blazor renders child content, rendering components inside a [`for`](/dotnet/csharp/language-reference/keywords/for) loop requires a local index variable if the incrementing loop variable is used in the `RenderFragmentChild` component's content. The following example can be added to the preceding `RenderFragmentParent` component: @@ -2021,7 +2116,7 @@ After the following `Expander` component demonstrates an overwritten parameter, `Shared/Expander.razor`: -:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Shared/index/BadExpander.razor"::: +:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Shared/index/BadExpander.razor"::: The `Expander` component is added to the following `ExpanderExample` parent component that may call <xref:Microsoft.AspNetCore.Components.ComponentBase.StateHasChanged%2A>: @@ -2030,7 +2125,7 @@ The `Expander` component is added to the following `ExpanderExample` parent comp `Pages/ExpanderExample.razor`: -:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Pages/index/ExpanderExample.razor"::: +:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Pages/index/ExpanderExample.razor"::: Initially, the `Expander` components behave independently when their `Expanded` properties are toggled. The child components maintain their states as expected. @@ -2057,9 +2152,9 @@ The following revised `Expander` component: `Shared/Expander.razor`: -:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Shared/index/Expander.razor"::: +:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Shared/index/Expander.razor"::: -For additional information, see [Blazor Two Way Binding Error (dotnet/aspnetcore #24599)](https://github.com/dotnet/aspnetcore/issues/24599). +For two-way parent-child binding examples, see <xref:blazor/components/data-binding#binding-with-component-parameters>. For additional information, see [Blazor Two Way Binding Error (dotnet/aspnetcore #24599)](https://github.com/dotnet/aspnetcore/issues/24599). For more information on change detection, including information on the exact types that Blazor checks, see <xref:blazor/components/rendering#rendering-conventions-for-componentbase>. @@ -2074,7 +2169,7 @@ In the following `Splat` component: `Pages/Splat.razor`: -:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Pages/index/Splat.razor"::: +:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Pages/index/Splat.razor"::: The rendered `<input>` elements in the webpage are identical: @@ -2097,7 +2192,7 @@ To accept arbitrary attributes, define a [component parameter](#component-parame ```razor @code { [Parameter(CaptureUnmatchedValues = true)] - public Dictionary<string, object> InputAttributes { get; set; } + public Dictionary<string, object>? InputAttributes { get; set; } } ``` @@ -2107,11 +2202,11 @@ The position of [`@attributes`][3] relative to the position of element attribute `Shared/AttributeOrderChild1.razor`: -:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Shared/index/AttributeOrderChild1.razor"::: +:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Shared/index/AttributeOrderChild1.razor"::: `Pages/AttributeOrderParent1.razor`: -:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Pages/index/AttributeOrderParent1.razor"::: +:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Pages/index/AttributeOrderParent1.razor"::: The `AttributeOrderChild1` component's `extra` attribute is set to the right of [`@attributes`][3]. The `AttributeOrderParent1` component's rendered `<div>` contains `extra="5"` when passed through the additional attribute because the attributes are processed right to left (last to first): @@ -2123,11 +2218,11 @@ In the following example, the order of `extra` and [`@attributes`][3] is reverse `Shared/AttributeOrderChild2.razor`: -:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Shared/index/AttributeOrderChild2.razor"::: +:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Shared/index/AttributeOrderChild2.razor"::: `Pages/AttributeOrderParent2.razor`: -:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Pages/index/AttributeOrderParent2.razor"::: +:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Pages/index/AttributeOrderParent2.razor"::: The `<div>` in the parent component's rendered webpage contains `extra="10"` when passed through the additional attribute: @@ -2148,7 +2243,7 @@ Consider the following `ReferenceChild` component that logs a message when its ` `Shared/ReferenceChild.razor`: -:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Shared/index/ReferenceChild.razor"::: +:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Shared/index/ReferenceChild.razor"::: A component reference is only populated after the component is rendered and its output includes `ReferenceChild`'s element. Until the component is rendered, there's nothing to reference. @@ -2160,13 +2255,13 @@ The following lambda approach uses the preceding `ReferenceChild` component. `Pages/ReferenceParent1.razor`: -:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Pages/index/ReferenceParent1.razor"::: +:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Pages/index/ReferenceParent1.razor"::: The following delegate approach uses the preceding `ReferenceChild` component. `Pages/ReferenceParent2.razor`: -:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Pages/index/ReferenceParent2.razor"::: +:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Pages/index/ReferenceParent2.razor"::: While capturing component references use a similar syntax to [capturing element references](xref:blazor/js-interop/call-javascript-from-dotnet#capture-references-to-elements), capturing component references isn't a JavaScript interop feature. Component references aren't passed to JavaScript code. Component references are only used in .NET code. @@ -2199,11 +2294,11 @@ In the event a component must be updated based on an external event, such as a t `TimerService.cs`: -:::code language="csharp" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/TimerService.cs"::: +:::code language="csharp" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/TimerService.cs"::: `NotifierService.cs`: -:::code language="csharp" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/NotifierService.cs"::: +:::code language="csharp" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/NotifierService.cs"::: Register the services: @@ -2214,18 +2309,18 @@ Register the services: builder.Services.AddSingleton<TimerService>(); ``` -* In a Blazor Server app, register the services as scoped in `Startup.ConfigureServices`: +* In a Blazor Server app, register the services as scoped in `Program.cs`: ```csharp - services.AddScoped<NotifierService>(); - services.AddScoped<TimerService>(); + builder.Services.AddScoped<NotifierService>(); + builder.Services.AddScoped<TimerService>(); ``` Use the `NotifierService` to update a component. `Pages/ReceiveNotifications.razor`: -:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Pages/index/ReceiveNotifications.razor"::: +:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Pages/index/ReceiveNotifications.razor"::: In the preceding example: @@ -2241,7 +2336,7 @@ When rendering a list of elements or components and the elements or components s Consider the following example that demonstrates a collection mapping problem that's solved by using [`@key`][5]. -For following `Details` and `People` components: +For the following `Details` and `People` components: * The `Details` component receives data (`Data`) from the parent `People` component, which is displayed in an `<input>` element. Any given displayed `<input>` element can receive the focus of the page from the user when they select one of the `<input>` elements. * The `People` component creates a list of person objects for display using the `Details` component. Every three seconds, a new person is added to the collection. @@ -2253,13 +2348,13 @@ This demonstration allows you to: `Shared/Details.razor`: -:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Shared/index/Details.razor"::: +:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Shared/index/Details.razor"::: In the following `People` component, each iteration of adding a person in `OnTimerCallback` results in Blazor rebuilding the entire collection. The page's focus remains on the *same index* position of `<input>` elements, so the focus shifts each time a person is added. *Shifting the focus away from what the user selected isn't desirable behavior.* After demonstrating the poor behavior with the following component, the [`@key`][5] directive attribute is used to improve the user's experience. `Pages/People.razor`: -:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Pages/index/People.razor"::: +:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Pages/index/People.razor"::: The contents of the `people` collection changes with inserted, deleted, or re-ordered entries. Rerendering can lead to visible behavior differences. For example, each time a person is inserted into the `people` collection, the user's focus is lost. @@ -2419,7 +2514,7 @@ In the following example, `IsCompleted` determines if the `<input>` element's `c `Pages/ConditionalAttribute.razor`: -:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Pages/index/ConditionalAttribute.razor"::: +:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Pages/index/ConditionalAttribute.razor"::: For more information, see <xref:mvc/views/razor>. @@ -2437,7 +2532,7 @@ The following example shows using the <xref:Microsoft.AspNetCore.Components.Mark `Pages/MarkupStringExample.razor`: -:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Pages/index/MarkupStringExample.razor"::: +:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Pages/index/MarkupStringExample.razor"::: ## Razor templates @@ -2451,7 +2546,7 @@ The following example illustrates how to specify <xref:Microsoft.AspNetCore.Comp `Pages/RazorTemplate.razor`: -:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Pages/index/RazorTemplate.razor"::: +:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Pages/index/RazorTemplate.razor"::: Rendered output of the preceding code: @@ -2494,6 +2589,46 @@ Similarly, SVG images are supported in the CSS rules of a stylesheet file (`.css } ``` +Blazor supports the [`<foreignObject>`](https://developer.mozilla.org/docs/Web/SVG/Element/foreignObject) element to display arbitrary HTML within an SVG. The markup can represent arbitrary HTML, a <xref:Microsoft.AspNetCore.Components.RenderFragment>, or a Razor component. + +The following example demonstrates: + +* Display of a `string` (`@message`). +* Two-way binding with an `<input>` element and a `value` field. +* A `Robot` component. + +```razor +<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; +} +``` + ## Whitespace rendering behavior Unless the [`@preservewhitespace`](xref:mvc/views/razor#preservewhitespace) directive is used with a value of `true`, extra whitespace is removed by default if: @@ -2517,11 +2652,17 @@ The [`@typeparam`][11] directive declares a [generic type parameter](/dotnet/csh @typeparam TItem ``` +C# syntax with [`where`](/dotnet/csharp/language-reference/keywords/where-generic-type-constraint) type constraints is supported: + +```razor +@typeparam TEntity where TEntity : IEntity +``` + In the following example, the `ListGenericTypeItems1` component is generically typed as `TExample`. `Shared/ListGenericTypeItems1.razor`: -:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Shared/index/ListGenericTypeItems1.razor"::: +:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Shared/index/ListGenericTypeItems1.razor"::: The following `GenericTypeExample1` component renders two `ListGenericTypeItems1` components: @@ -2530,2603 +2671,2462 @@ The following `GenericTypeExample1` component renders two `ListGenericTypeItems1 `Pages/GenericTypeExample1.razor`: -:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Pages/index/GenericTypeExample1.razor"::: +:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Pages/index/GenericTypeExample1.razor"::: -For more information, see the following articles: +For more information, see <xref:mvc/views/razor#typeparam>. For an example of generic typing with templated components, see <xref:blazor/components/templated-components>. -* <xref:mvc/views/razor#typeparam> -* <xref:blazor/components/templated-components> +## Cascaded generic type support -:::moniker-end +An ancestor component can cascade a type parameter by name to descendants using the [`[CascadingTypeParameter]` attribute](xref:Microsoft.AspNetCore.Components.CascadingTypeParameterAttribute). This attribute allows a generic type inference to use the specified type parameter automatically with descendants that have a type parameter with the same name. -:::moniker range="< aspnetcore-5.0" +By adding `@attribute [CascadingTypeParameter(...)]` to a component, the specified generic type argument is automatically used by descendants that: -Blazor apps are built using *Razor components*, informally known as *Blazor components*. A component is a self-contained portion of user interface (UI) with processing logic to enable dynamic behavior. Components can be nested, reused, shared among projects, and [used in MVC and Razor Pages apps](xref:blazor/components/prerendering-and-integration). +* Are nested as child content for the component in the same `.razor` document. +* Also declare a [`@typeparam`](xref:mvc/views/razor#typeparam) with the exact same name. +* Don't have another value explicitly supplied or implicitly inferred for the type parameter. If another value is supplied or inferred, it takes precedence over the cascaded generic type. -## Component classes +When receiving a cascaded type parameter, components obtain the parameter value from the closest ancestor that has a <xref:Microsoft.AspNetCore.Components.CascadingTypeParameterAttribute> with a matching name. Cascaded generic type parameters are overridden within a particular subtree. -Components are implemented using a combination of C# and HTML markup in [Razor](xref:mvc/views/razor) component files with the `.razor` file extension. +Matching is only performed by name. Therefore, we recommend avoiding a cascaded generic type parameter with a generic name, for example `T` or `TItem`. If a developer opts into cascading a type parameter, they're implicitly promising that its name is unique enough not to clash with other cascaded type parameters from unrelated components. -By default, <xref:Microsoft.AspNetCore.Components.ComponentBase> is the base class for components described by Razor component files. <xref:Microsoft.AspNetCore.Components.ComponentBase> implements the lowest abstraction of components, the <xref:Microsoft.AspNetCore.Components.IComponent> interface. <xref:Microsoft.AspNetCore.Components.ComponentBase> defines component properties and methods for basic functionality, for example, to process a set of built-in component lifecycle events. +Generic types can be cascaded to child components in either of the following approaches with ancestor (parent) components, which are demonstrated in the following two sub-sections: -[`ComponentBase` in `dotnet/aspnetcore` reference source](https://github.com/dotnet/aspnetcore/blob/main/src/Components/Components/src/ComponentBase.cs): The reference source contains additional remarks on the built-in lifecycle events. However, keep in mind that the internal implementations of component features are subject to change at any time without notice. +* Explicitly set the cascaded generic type. +* Infer the cascaded generic type. -[!INCLUDE[](~/includes/aspnetcore-repo-ref-source-links.md)] +The following subsections provide examples of the preceding approaches using the following two `ListDisplay` components. The components receive and render list data and are generically typed as `TExample`. These components are for demonstration purposes and only differ in the color of text that the list is rendered. If you wish to experiment with the components in the following sub-sections in a local test app, add the following two components to the app first. -Developers typically create Razor components from Razor component files (`.razor`) or base their components on <xref:Microsoft.AspNetCore.Components.ComponentBase>, but components can also be built by implementing <xref:Microsoft.AspNetCore.Components.IComponent>. Developer-built components that implement <xref:Microsoft.AspNetCore.Components.IComponent> can take low-level control over rendering at the cost of having to manually trigger rendering with events and lifecycle methods that the developer must create and maintain. +`Shared/ListDisplay1.razor`: -### Razor syntax +```razor +@typeparam TExample -Components use [Razor syntax](xref:mvc/views/razor). Two Razor features are extensively used by components, *directives* and *directive attributes*. These are reserved keywords prefixed with `@` that appear in Razor markup: +@if (ExampleList is not null) +{ + <ul style="color:blue"> + @foreach (var item in ExampleList) + { + <li>@item</li> + } + </ul> +} -* [Directives](xref:mvc/views/razor#directives): Change the way component markup is parsed or functions. For example, the [`@page`][9] directive specifies a routable component with a route template and can be reached directly by a user's request in the browser at a specific URL. -* [Directive attributes](xref:mvc/views/razor#directive-attributes): Change the way a component element is parsed or functions. For example, the [`@bind`][10] directive attribute for an `<input>` element binds data to the element's value. +@code { + [Parameter] + public IEnumerable<TExample>? ExampleList { get; set; } +} +``` -Directives and directive attributes used in components are explained further in this article and other articles of the Blazor documentation set. For general information on Razor syntax, see <xref:mvc/views/razor>. +`Shared/ListDisplay2.razor`: -### Names +```razor +@typeparam TExample -A component's name must start with an uppercase character: +@if (ExampleList is not null) +{ + <ul style="color:red"> + @foreach (var item in ExampleList) + { + <li>@item</li> + } + </ul> +} -* `ProductDetail.razor` is valid. -* `productDetail.razor` is invalid. +@code { + [Parameter] + public IEnumerable<TExample>? ExampleList { get; set; } +} +``` -Common Blazor naming conventions used throughout the Blazor documentation include: +### Explicit generic types based on ancestor components -* Component file paths use Pascal case† and appear before showing component code examples. Paths indicate typical folder locations. For example, `Pages/ProductDetail.razor` indicates that the `ProductDetail` component has a file name of `ProductDetail.razor` and resides in the `Pages` folder of the app. -* Component file paths for routable components match their URLs with hyphens appearing for spaces between words in a component's route template. For example, a `ProductDetail` component with a route template of `/product-detail` (`@page "/product-detail"`) is requested in a browser at the relative URL `/product-detail`. +The demonstration in this section cascades a type explicitly for `TExample`. -†Pascal case (upper camel case) is a naming convention without spaces and punctuation and with the first letter of each word capitalized, including the first word. +> [!NOTE] +> This section uses the two `ListDisplay` components in the [Cascaded generic type support](#cascaded-generic-type-support) section. -### Routing +The following `ListGenericTypeItems2` component receives data and cascades a generic type parameter named `TExample` to its descendent components. In the upcoming parent component, the `ListGenericTypeItems2` component is used to display list data with the preceding `ListDisplay` component. -Routing in Blazor is achieved by providing a route template to each accessible component in the app with an [`@page`][9] directive. When a Razor file with an [`@page`][9] directive is compiled, the generated class is given a <xref:Microsoft.AspNetCore.Mvc.RouteAttribute> specifying the route template. At runtime, the router searches for component classes with a <xref:Microsoft.AspNetCore.Mvc.RouteAttribute> and renders whichever component has a route template that matches the requested URL. +`Shared/ListGenericTypeItems2.razor`: -The following `HelloWorld` component uses a route template of `/hello-world`. The rendered webpage for the component is reached at the relative URL `/hello-world`. When running a Blazor app locally with the default protocol, host, and port, the `HelloWorld` component is requested in the browser at `https://localhost:5001/hello-world`. Components that produce webpages usually reside in the `Pages` folder, but you can use any folder to hold components, including within nested folders. +```razor +@attribute [CascadingTypeParameter(nameof(TExample))] +@typeparam TExample -`Pages/HelloWorld.razor`: +<h2>List Generic Type Items 2</h2> -:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Pages/index/HelloWorld.razor"::: +@ChildContent -The preceding component loads in the browser at `/hello-world` regardless of whether or not you add the component to the app's UI navigation. Optionally, components can be added to the `NavMenu` component so that a link to the component appears in the app's UI-based navigation. +@code { + [Parameter] + public RenderFragment? ChildContent { get; set; } +} +``` -For the preceding `HelloWorld` component, you can add a `NavLink` component to the `NavMenu` component in the `Shared` folder. For more information, including descriptions of the `NavLink` and `NavMenu` components, see <xref:blazor/fundamentals/routing>. +The following `GenericTypeExample2` parent component sets the child content (<xref:Microsoft.AspNetCore.Components.RenderFragment>) of two `ListGenericTypeItems2` components specifying the `ListGenericTypeItems2` types (`TExample`), which are cascaded to child components. `ListDisplay` components are rendered with the list item data shown in the example. String data is used with the first `ListGenericTypeItems2` component, and integer data is used with the second `ListGenericTypeItems2` component. -### Markup +`Pages/GenericTypeExample2.razor`: -A component's UI is defined using [Razor syntax](xref:mvc/views/razor), which consists of Razor markup, C#, and HTML. When an app is compiled, the HTML markup and C# rendering logic are converted into a component class. The name of the generated class matches the name of the file. +```razor +@page "/generic-type-example-2" -Members of the component class are defined in one or more [`@code`][1] blocks. In [`@code`][1] blocks, component state is specified and processed with C#: +<h1>Generic Type Example 2</h1> -* Property and field initializers. -* Parameter values from arguments passed by parent components and route parameters. -* Methods for user event handling, lifecycle events, and custom component logic. +<ListGenericTypeItems2 TExample="string"> + <ListDisplay1 ExampleList="@(new List<string> { "Item 1", "Item 2" })" /> + <ListDisplay2 ExampleList="@(new List<string> { "Item 3", "Item 4" })" /> +</ListGenericTypeItems2> -Component members are used in rendering logic using C# expressions that start with the `@` symbol. For example, a C# field is rendered by prefixing `@` to the field name. The following `Markup` component evaluates and renders: +<ListGenericTypeItems2 TExample="int"> + <ListDisplay1 ExampleList="@(new List<int> { 1, 2, 3 })" /> + <ListDisplay2 ExampleList="@(new List<int> { 4, 5, 6 })" /> +</ListGenericTypeItems2> +``` -* `headingFontStyle` for the CSS property value `font-style` of the heading element. -* `headingText` for the content of the heading element. +Specifying the type explicitly also allows the use of [cascading values and parameters](xref:blazor/components/cascading-values-and-parameters) to provide data to child components, as the following demonstration shows. -`Pages/Markup.razor`: +`Shared/ListDisplay3.razor`: -:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Pages/index/Markup.razor"::: +```razor +@typeparam TExample -> [!NOTE] -> Examples throughout the Blazor documentation specify the [`private` access modifier](/dotnet/csharp/language-reference/keywords/private) for private members. Private members are scoped to a component's class. However, C# assumes the `private` access modifier when no access modifier is present, so explicitly marking members "`private`" in your own code is optional. For more information on access modifiers, see [Access Modifiers (C# Programming Guide)](/dotnet/csharp/programming-guide/classes-and-structs/access-modifiers). +@if (ExampleList is not null) +{ + <ul style="color:blue"> + @foreach (var item in ExampleList) + { + <li>@item</li> + } + </ul> +} -The Blazor framework processes a component internally as a [*render tree*](https://developer.mozilla.org/docs/Web/Performance/How_browsers_work#render), which is the combination of a component's [Document Object Model (DOM)](https://developer.mozilla.org/docs/Web/API/Document_Object_Model/Introduction) and [Cascading Style Sheet Object Model (CSSOM)](https://developer.mozilla.org/docs/Web/API/CSS_Object_Model). After the component is initially rendered, the component's render tree is regenerated in response to events. Blazor compares the new render tree against the previous render tree and applies any modifications to the browser's DOM for display. For more information, see <xref:blazor/components/rendering>. +@code { + [CascadingParameter] + protected IEnumerable<TExample>? ExampleList { get; set; } +} +``` -Components are ordinary [C# classes](/dotnet/csharp/programming-guide/classes-and-structs/classes) and can be placed anywhere within a project. Components that produce webpages usually reside in the `Pages` folder. Non-page components are frequently placed in the `Shared` folder or a custom folder added to the project. +`Shared/ListDisplay4.razor`: -Razor syntax for C# control structures, directives, and directive attributes are lowercase (examples: [`@if`](xref:mvc/views/razor#conditionals-if-else-if-else-and-switch), [`@code`](xref:mvc/views/razor#code), [`@bind`](xref:mvc/views/razor#bind)). Property names are uppercase (example: `@Body` for <xref:Microsoft.AspNetCore.Components.LayoutComponentBase.Body?displayProperty=nameWithType>). +```razor +@typeparam TExample -### Asynchronous methods (`async`) don't support returning `void` +@if (ExampleList is not null) +{ + <ul style="color:red"> + @foreach (var item in ExampleList) + { + <li>@item</li> + } + </ul> +} -The Blazor framework doesn't track `void`-returning asynchronous methods (`async`). As a result, exceptions aren't caught if `void` is returned. Always return a <xref:System.Threading.Tasks.Task> from asynchronous methods. +@code { + [CascadingParameter] + protected IEnumerable<TExample>? ExampleList { get; set; } +} +``` -### Nested components +`Shared/ListGenericTypeItems3.razor`: -Components can include other components by declaring them using HTML syntax. The markup for using a component looks like an HTML tag where the name of the tag is the component type. +```razor +@attribute [CascadingTypeParameter(nameof(TExample))] +@typeparam TExample -Consider the following `Heading` component, which can be used by other components to display a heading. +<h2>List Generic Type Items 3</h2> -`Shared/Heading.razor`: +@ChildContent -:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Shared/index/Heading.razor"::: +@if (ExampleList is not null) +{ + <ul style="color:green"> + @foreach(var item in ExampleList) + { + <li>@item</li> + } + </ul> -The following markup in the `HeadingExample` component renders the preceding `Heading` component at the location where the `<Heading />` tag appears. + <p> + Type of <code>TExample</code>: @typeof(TExample) + </p> +} -`Pages/HeadingExample.razor`: +@code { + [CascadingParameter] + protected IEnumerable<TExample>? ExampleList { get; set; } -:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Pages/index/HeadingExample.razor"::: + [Parameter] + public RenderFragment? ChildContent { get; set; } +} +``` -If a component contains an HTML element with an uppercase first letter that doesn't match a component name within the same namespace, a warning is emitted indicating that the element has an unexpected name. Adding an [`@using`][2] directive for the component's namespace makes the component available, which resolves the warning. For more information, see the [Namespaces](#namespaces) section. +When cascading the data in the following example, the type must be provided to the `ListGenericTypeItems3` component. -The `Heading` component example shown in this section doesn't have an [`@page`][9] directive, so the `Heading` component isn't directly accessible to a user via a direct request in the browser. However, any component with an [`@page`][9] directive can be nested in another component. If the `Heading` component was directly accessible by including `@page "/heading"` at the top of its Razor file, then the component would be rendered for browser requests at both `/heading` and `/heading-example`. +`Pages/GenericTypeExample3.razor`: -### Namespaces +```razor +@page "/generic-type-example-3" -Typically, a component's namespace is derived from the app's root namespace and the component's location (folder) within the app. If the app's root namespace is `BlazorSample` and the `Counter` component resides in the `Pages` folder: +<h1>Generic Type Example 3</h1> -* The `Counter` component's namespace is `BlazorSample.Pages`. -* The fully qualified type name of the component is `BlazorSample.Pages.Counter`. +<CascadingValue Value="@stringData"> + <ListGenericTypeItems3 TExample="string"> + <ListDisplay3 /> + <ListDisplay4 /> + </ListGenericTypeItems3> +</CascadingValue> -For custom folders that hold components, add an [`@using`][2] directive to the parent component or to the app's `_Imports.razor` file. The following example makes components in the `Components` folder available: +<CascadingValue Value="@integerData"> + <ListGenericTypeItems3 TExample="int"> + <ListDisplay3 /> + <ListDisplay4 /> + </ListGenericTypeItems3> +</CascadingValue> -```razor -@using BlazorSample.Components +@code { + private List<string> stringData = new() { "Item 1", "Item 2" }; + private List<int> integerData = new() { 1, 2, 3 }; +} ``` -> [!NOTE] -> [`@using`][2] directives in the `_Imports.razor` file are only applied to Razor files (`.razor`), not C# files (`.cs`). - -Components can also be referenced using their fully qualified names, which doesn't require an [`@using`][2] directive. The following example directly references the `ProductDetail` component in the `Components` folder of the app: +When multiple generic types are cascaded, values for all generic types in the set must be passed. In the following example, `TItem`, `TValue`, and `TEdit` are `GridColumn` generic types, but the parent component that places `GridColumn` doesn't specify the `TItem` type: ```razor -<BlazorSample.Components.ProductDetail /> +<GridColumn TValue="string" TEdit="@TextEdit" /> ``` -The namespace of a component authored with Razor is based on the following (in priority order): - -* The [`@namespace`][8] directive in the Razor file's markup (for example, `@namespace BlazorSample.CustomNamespace`). -* The project's `RootNamespace` in the project file (for example, `<RootNamespace>BlazorSample</RootNamespace>`). -* The project name, taken from the project file's file name (`.csproj`), and the path from the project root to the component. For example, the framework resolves `{PROJECT ROOT}/Pages/Index.razor` with a project namespace of `BlazorSample` (`BlazorSample.csproj`) to the namespace `BlazorSample.Pages` for the `Index` component. `{PROJECT ROOT}` is the project root path. Components follow C# name binding rules. For the `Index` component in this example, the components in scope are all of the components: - * In the same folder, `Pages`. - * The components in the project's root that don't explicitly specify a different namespace. - -The following are **not** supported: - -* The [`global::`](/dotnet/csharp/language-reference/operators/namespace-alias-qualifier) qualification. -* Importing components with aliased [`using`](/dotnet/csharp/language-reference/keywords/using-statement) statements. For example, `@using Foo = Bar` isn't supported. -* Partially-qualified names. For example, you can't add `@using BlazorSample` to a component and then reference the `NavMenu` component in the app's `Shared` folder (`Shared/NavMenu.razor`) with `<Shared.NavMenu></Shared.NavMenu>`. - -### Partial class support - -Components are generated as [C# partial classes](/dotnet/csharp/programming-guide/classes-and-structs/partial-classes-and-methods) and are authored using either of the following approaches: - -* A single file contains C# code defined in one or more [`@code`][1] blocks, HTML markup, and Razor markup. Blazor project templates define their components using this single-file approach. -* HTML and Razor markup are placed in a Razor file (`.razor`). C# code is placed in a code-behind file defined as a partial class (`.cs`). +The preceding example generates a compile-time error that the `GridColumn` component is missing the `TItem` type parameter. Valid code specifies all of the types: -The following example shows the default `Counter` component with an [`@code`][1] block in an app generated from a Blazor project template. Markup and C# code are in the same file. This is the most common approach taken in component authoring. +```razor +<GridColumn TValue="string" TEdit="@TextEdit" TItem="@User" /> +``` -`Pages/Counter.razor`: +### Infer generic types based on ancestor components -:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Pages/Counter.razor"::: +The demonstration in this section cascades a type inferred for `TExample`. -The following `Counter` component splits HTML and Razor markup from C# code using a code-behind file with a partial class: +> [!NOTE] +> This section uses the two `ListDisplay` components in the [Cascaded generic type support](#cascaded-generic-type-support) section. -`Pages/CounterPartialClass.razor`: +`Shared/ListGenericTypeItems4.razor`: ```razor -@page "/counter-partial-class" - -<h1>Counter</h1> - -<p>Current count: @currentCount</p> +@attribute [CascadingTypeParameter(nameof(TExample))] +@typeparam TExample -<button class="btn btn-primary" @onclick="IncrementCount">Click me</button> -``` +<h2>List Generic Type Items 4</h2> -`Pages/CounterPartialClass.razor.cs`: +@ChildContent -```csharp -namespace BlazorSample.Pages +@if (ExampleList is not null) { - public partial class CounterPartialClass - { - private int currentCount = 0; - - void IncrementCount() + <ul style="color:green"> + @foreach(var item in ExampleList) { - currentCount++; + <li>@item</li> } - } -} -``` + </ul> -[`@using`][2] directives in the `_Imports.razor` file are only applied to Razor files (`.razor`), not C# files (`.cs`). Add namespaces to a partial class file as needed. + <p> + Type of <code>TExample</code>: @typeof(TExample) + </p> +} -Typical namespaces used by components: +@code { + [Parameter] + public IEnumerable<TExample>? ExampleList { get; set; } -```csharp -using System.Net.Http; -using Microsoft.AspNetCore.Components.Forms; -using Microsoft.AspNetCore.Components.Routing; -using Microsoft.AspNetCore.Components.Web; -using Microsoft.JSInterop; + [Parameter] + public RenderFragment? ChildContent { get; set; } +} ``` -Typical namespaces also include the namespace of the app and the namespace corresponding to the app's `Shared` folder: +The following `GenericTypeExample4` component with inferred cascaded types provides different data for display. -```csharp -using BlazorSample; -using BlazorSample.Shared; -``` +`Pages/GenericTypeExample4.razor`: -### Specify a base class +```razor +@page "/generic-type-example-4" -The [`@inherits`][6] directive is used to specify a base class for a component. The following example shows how a component can inherit a base class to provide the component's properties and methods. The `BlazorRocksBase` base class derives from <xref:Microsoft.AspNetCore.Components.ComponentBase>. +<h1>Generic Type Example 4</h1> -`Pages/BlazorRocks.razor`: +<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> -:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Pages/index/BlazorRocks.razor"::: +<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> +``` -`BlazorRocksBase.cs`: +The following `GenericTypeExample5` component with inferred cascaded types provides the same data for display. The following example directly assigns the data to the components. -:::code language="csharp" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/BlazorRocksBase.cs"::: +`Pages/GenericTypeExample5.razor`: -## Component parameters +```razor +@page "/generic-type-example-5" -*Component parameters* pass data to components and are defined using public [C# properties](/dotnet/csharp/programming-guide/classes-and-structs/properties) on the component class with the [`[Parameter]` attribute](xref:Microsoft.AspNetCore.Components.ParameterAttribute). In the following example, a built-in reference type (<xref:System.String?displayProperty=fullName>) and a user-defined reference type (`PanelBody`) are passed as component parameters. +<h1>Generic Type Example 5</h1> -`PanelBody.cs`: +<ListGenericTypeItems4 ExampleList="@stringData"> + <ListDisplay1 ExampleList="@stringData" /> + <ListDisplay2 ExampleList="@stringData" /> +</ListGenericTypeItems4> -:::code language="csharp" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/PanelBody.cs"::: +<ListGenericTypeItems4 ExampleList="@integerData"> + <ListDisplay1 ExampleList="@integerData" /> + <ListDisplay2 ExampleList="@integerData" /> +</ListGenericTypeItems4> -`Shared/ParameterChild.razor`: +@code { + private List<string> stringData = new() { "Item 1", "Item 2" }; + private List<int> integerData = new() { 1, 2, 3 }; +} +``` -:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Shared/index/ParameterChild.razor"::: +## Render Razor components from JavaScript -> [!WARNING] -> Providing initial values for component parameters is supported, but don't create a component that writes to its own parameters after the component is rendered for the first time. For more information, see the [Overwritten parameters](#overwritten-parameters) section of this article. +Razor components can be dynamically-rendered from JavaScript (JS) for existing JS apps. -The `Title` and `Body` component parameters of the `ParameterChild` component are set by arguments in the HTML tag that renders the instance of the component. The following `ParameterParent` component renders two `ParameterChild` components: +To render a Razor component from JS, register the component as a root component for JS rendering and assign the component an identifier: -* The first `ParameterChild` component is rendered without supplying parameter arguments. -* The second `ParameterChild` component receives values for `Title` and `Body` from the `ParameterParent` component, which uses an [explicit C# expression](xref:mvc/views/razor#explicit-razor-expressions) to set the values of the `PanelBody`'s properties. - -`Pages/ParameterParent.razor`: - -:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Pages/index/ParameterParent.razor"::: - -The following rendered HTML markup from the `ParameterParent` component shows `ParameterChild` component default values when the `ParameterParent` component doesn't supply component parameter values. When the `ParameterParent` component provides component parameter values, they replace the `ParameterChild` component's default values. +* In a Blazor Server app, modify the call to <xref:Microsoft.Extensions.DependencyInjection.ComponentServiceCollectionExtensions.AddServerSideBlazor%2A> in `Program.cs`: -> [!NOTE] -> For clarity, rendered CSS style classes aren't shown in the following rendered HTML markup. + ```csharp + builder.Services.AddServerSideBlazor(options => + { + options.RootComponents.RegisterForJavaScript<Counter>(identifier: "counter"); + }); + ``` + + > [!NOTE] + > The preceding code example requires a namespace for the app's components (for example, `using BlazorSample.Pages;`) in the `Program.cs` file. -```html -<h1>Child component (without attribute values)</h1> +* In a Blazor WebAssembly app, call <xref:Microsoft.AspNetCore.Components.Web.JSComponentConfigurationExtensions.RegisterForJavaScript%2A> on <xref:Microsoft.AspNetCore.Components.WebAssembly.Hosting.WebAssemblyHostBuilder.RootComponents> in `Program.cs`: -<div> - <div>Set By Child</div> - <div>Set by child.</div> -</div> + ```csharp + builder.RootComponents.RegisterForJavaScript<Counter>(identifier: "counter"); + ``` + + > [!NOTE] + > The preceding code example requires a namespace for the app's components (for example, `using BlazorSample.Pages;`) in the `Program.cs` file. -<h1>Child component (with attribute values)</h1> +Load Blazor into the JS app (`blazor.server.js` or `blazor.webassembly.js`). Render the component from JS into a container element using the registered identifier, passing component parameters as needed: -<div> - <div>Set by Parent</div> - <div>Set by parent.</div> -</div> +```javascript +let containerElement = document.getElementById('my-counter'); +await Blazor.rootComponents.add(containerElement, 'counter', { incrementAmount: 10 }); ``` -Assign a C# field, property, or result of a method to a component parameter as an HTML attribute value using [Razor's reserved `@` symbol](xref:mvc/views/razor#razor-syntax). The following `ParameterParent2` component displays four instances of the preceding `ParameterChild` component and sets their `Title` parameter values to: - -* The value of the `title` field. -* The result of the `GetTitle` C# method. -* The current local date in long format with <xref:System.DateTime.ToLongDateString%2A>, which uses an [implicit C# expression](xref:mvc/views/razor#implicit-razor-expressions). -* The `panelData` object's `Title` property. +## Blazor custom elements -The `@` prefix is required for string parameters. Otherwise, the framework assumes that a string literal is set. +*Experimental* support is available for building custom elements using the [`Microsoft.AspNetCore.Components.CustomElements` NuGet package](https://www.nuget.org/packages/microsoft.aspnetcore.components.customelements). Custom elements use standard HTML interfaces to implement custom HTML elements. -Outside of string parameters, we recommend use the use of the `@` prefix for nonliterals, even when they aren't strictly required. +> [!WARNING] +> Experimental features are provided for the purpose of exploring feature viability and may not ship in a stable version. -We don't recommend the use of the `@` prefix for literals (for example, boolean values), keywords (for example, `this`), or `null`, but you can choose to use them if you wish. For example, `IsFixed="@true"` is uncommon but supported. +Register a root component as a custom element: -Quotes around parameter attribute values are optional in most cases per the HTML5 specification. For example, `Value=this` is supported, instead of `Value="this"`. However, we recommend using quotes because it's easier to remember and widely adopted across web-based technologies. +* In a Blazor Server app, modify the call to <xref:Microsoft.Extensions.DependencyInjection.ComponentServiceCollectionExtensions.AddServerSideBlazor%2A> in `Program.cs`: -Throughout the documentation, code examples: + ```csharp + builder.Services.AddServerSideBlazor(options => + { + options.RootComponents.RegisterAsCustomElement<Counter>("my-counter"); + }); + ``` + + > [!NOTE] + > The preceding code example requires a namespace for the app's components (for example, `using BlazorSample.Pages;`) in the `Program.cs` file. -* Always use quotes. Example: `Value="this"`. -* Nonliterals always use the `@` prefix, even when it's optional. Examples: `Title="@title"`, where `title` is a string-typed variable. `Count="@ct"`, where `ct` is a number-typed variable. -* Literals, outside of Razor expressions, always avoid `@`. Example: `IsFixed="true"`. +* In a Blazor WebAssembly app, call `RegisterAsCustomElement` on <xref:Microsoft.AspNetCore.Components.WebAssembly.Hosting.WebAssemblyHostBuilder.RootComponents> in `Program.cs`: -`Pages/ParameterParent2.razor`: + ```csharp + builder.RootComponents.RegisterAsCustomElement<Counter>("my-counter"); + ``` + + > [!NOTE] + > The preceding code example requires a namespace for the app's components (for example, `using BlazorSample.Pages;`) in the `Program.cs` file. -:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Pages/index/ParameterParent2.razor"::: +Include the following `<script>` tag in the app's HTML ***before*** the Blazor script tag: -> [!NOTE] -> When assigning a C# member to a component parameter, prefix the member with the `@` symbol and never prefix the parameter's HTML attribute. -> -> Correct: -> -> ```razor -> <ParameterChild Title="@title" /> -> ``` -> -> Incorrect: -> -> ```razor -> <ParameterChild @Title="title" /> -> ``` +```html +<script src="/_content/Microsoft.AspNetCore.Components.CustomElements/BlazorCustomElements.js"></script> +``` -Unlike in Razor pages (`.cshtml`), Blazor can't perform asynchronous work in a Razor expression while rendering a component. This is because Blazor is designed for rendering interactive UIs. In an interactive UI, the screen must always display something, so it doesn't make sense to block the rendering flow. Instead, asynchronous work is performed during one of the [asynchronous lifecycle events](xref:blazor/components/lifecycle). After each asynchronous lifecycle event, the component may render again. The following Razor syntax is **not** supported: +Use the custom element with any web framework. For example, the preceding counter custom element is used in a React app with the following markup: -```razor -<ParameterChild Title="@await ..." /> +```html +<my-counter increment-amount={incrementAmount}></my-counter> ``` -The code in the preceding example generates a *compiler error* when the app is built: +For a complete example of how to create custom elements with Blazor, see the [Blazor Custom Elements sample project](https://github.com/aspnet/AspLabs/tree/main/src/BlazorCustomElements). -> The 'await' operator can only be used within an async method. Consider marking this method with the 'async' modifier and changing its return type to 'Task'. +> [!WARNING] +> The custom elements feature is currently **experimental, unsupported, and subject to change or be removed at any time**. We welcome your feedback on how well this particular approach meets your requirements. -To obtain a value for the `Title` parameter in the preceding example asynchronously, the component can use the [`OnInitializedAsync` lifecycle event](xref:blazor/components/lifecycle#component-initialization-oninitializedasync), as the following example demonstrates: +## Generate Angular and React components -```razor -<ParameterChild Title="@title" /> +Generate framework-specific JavaScript (JS) components from Razor components for web frameworks, such as Angular or React. This capability isn't included with .NET 6, but is enabled by the new support for rendering Razor components from JS. The [JS component generation sample on GitHub](https://github.com/aspnet/samples/tree/main/samples/aspnetcore/blazor/JSComponentGeneration) demonstrates how to generate Angular and React components from Razor components. See the GitHub sample app's `README.md` file for additional information. -@code { - private string title; - - protected override async Task OnInitializedAsync() - { - title = await ...; - } -} -``` +> [!WARNING] +> The Angular and React component features are currently **experimental, unsupported, and subject to change or be removed at any time**. We welcome your feedback on how well this particular approach meets your requirements. -For more information, see <xref:blazor/components/lifecycle>. +:::moniker-end -Use of an explicit Razor expression to concatenate text with an expression result for assignment to a parameter is **not** supported. The following example seeks to concatenate the text "`Set by `" with an object's property value. Although this syntax is supported in a Razor page (`.cshtml`), it isn't valid for assignment to the child's `Title` parameter in a component. The following Razor syntax is **not** supported: +:::moniker range=">= aspnetcore-5.0 < aspnetcore-6.0" -```razor -<ParameterChild Title="Set by @(panelData.Title)" /> -``` +Blazor apps are built using *Razor components*, informally known as *Blazor components*. A component is a self-contained portion of user interface (UI) with processing logic to enable dynamic behavior. Components can be nested, reused, shared among projects, and [used in MVC and Razor Pages apps](xref:blazor/components/prerendering-and-integration). -The code in the preceding example generates a *compiler error* when the app is built: +## Component classes -> Component attributes do not support complex content (mixed C# and markup). +Components are implemented using a combination of C# and HTML markup in [Razor](xref:mvc/views/razor) component files with the `.razor` file extension. -To support the assignment of a composed value, use a method, field, or property. The following example performs the concatenation of "`Set by `" and an object's property value in the C# method `GetTitle`: +By default, <xref:Microsoft.AspNetCore.Components.ComponentBase> is the base class for components described by Razor component files. <xref:Microsoft.AspNetCore.Components.ComponentBase> implements the lowest abstraction of components, the <xref:Microsoft.AspNetCore.Components.IComponent> interface. <xref:Microsoft.AspNetCore.Components.ComponentBase> defines component properties and methods for basic functionality, for example, to process a set of built-in component lifecycle events. -`Pages/ParameterParent3.razor`: +[`ComponentBase` in `dotnet/aspnetcore` reference source](https://github.com/dotnet/aspnetcore/blob/main/src/Components/Components/src/ComponentBase.cs): The reference source contains additional remarks on the built-in lifecycle events. However, keep in mind that the internal implementations of component features are subject to change at any time without notice. -:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Pages/index/ParameterParent3.razor"::: +[!INCLUDE[](~/includes/aspnetcore-repo-ref-source-links.md)] -For more information, see <xref:mvc/views/razor>. +Developers typically create Razor components from Razor component files (`.razor`) or base their components on <xref:Microsoft.AspNetCore.Components.ComponentBase>, but components can also be built by implementing <xref:Microsoft.AspNetCore.Components.IComponent>. Developer-built components that implement <xref:Microsoft.AspNetCore.Components.IComponent> can take low-level control over rendering at the cost of having to manually trigger rendering with events and lifecycle methods that the developer must create and maintain. -> [!WARNING] -> Providing initial values for component parameters is supported, but don't create a component that writes to its own parameters after the component is rendered for the first time. For more information, see the [Overwritten parameters](#overwritten-parameters) section of this article. +### Razor syntax -Component parameters should be declared as *auto-properties*, meaning that they shouldn't contain custom logic in their `get` or `set` accessors. For example, the following `StartData` property is an auto-property: +Components use [Razor syntax](xref:mvc/views/razor). Two Razor features are extensively used by components, *directives* and *directive attributes*. These are reserved keywords prefixed with `@` that appear in Razor markup: -```csharp -[Parameter] -public DateTime StartData { get; set; } -``` +* [Directives](xref:mvc/views/razor#directives): Change the way component markup is parsed or functions. For example, the [`@page`][9] directive specifies a routable component with a route template and can be reached directly by a user's request in the browser at a specific URL. +* [Directive attributes](xref:mvc/views/razor#directive-attributes): Change the way a component element is parsed or functions. For example, the [`@bind`][10] directive attribute for an `<input>` element binds data to the element's value. -Don't place custom logic in the `get` or `set` accessor because component parameters are purely intended for use as a channel for a parent component to flow information to a child component. If a `set` accessor of a child component property contains logic that causes rerendering of the parent component, an infinite rendering loop results. +Directives and directive attributes used in components are explained further in this article and other articles of the Blazor documentation set. For general information on Razor syntax, see <xref:mvc/views/razor>. -To transform a received parameter value: +### Names -* Leave the parameter property as an auto-property to represent the supplied raw data. -* Create a different property or method to supply the transformed data based on the parameter property. +A component's name must start with an uppercase character: -Override [`OnParametersSetAsync`](xref:blazor/components/lifecycle#after-parameters-are-set-onparameterssetasync) to transform a received parameter each time new data is received. +* `ProductDetail.razor` is valid. +* `productDetail.razor` is invalid. -Writing an initial value to a component parameter is supported because initial value assignments don't interfere with the Blazor's automatic component rendering. The following assignment of the current local <xref:System.DateTime> with <xref:System.DateTime.Now?displayProperty=nameWithType> to `StartData` is valid syntax in a component: +Common Blazor naming conventions used throughout the Blazor documentation include: -```csharp -[Parameter] -public DateTime StartData { get; set; } = DateTime.Now; -``` +* Component file paths use Pascal case† and appear before showing component code examples. Paths indicate typical folder locations. For example, `Pages/ProductDetail.razor` indicates that the `ProductDetail` component has a file name of `ProductDetail.razor` and resides in the `Pages` folder of the app. +* Component file paths for routable components match their URLs with hyphens appearing for spaces between words in a component's route template. For example, a `ProductDetail` component with a route template of `/product-detail` (`@page "/product-detail"`) is requested in a browser at the relative URL `/product-detail`. -After the initial assignment of <xref:System.DateTime.Now?displayProperty=nameWithType>, do **not** assign a value to `StartData` in developer code. For more information, see the [Overwritten parameters](#overwritten-parameters) section of this article. +†Pascal case (upper camel case) is a naming convention without spaces and punctuation and with the first letter of each word capitalized, including the first word. -## Route parameters +### Routing -Components can specify route parameters in the route template of the [`@page`][9] directive. The [Blazor router](xref:blazor/fundamentals/routing) uses route parameters to populate corresponding component parameters. +Routing in Blazor is achieved by providing a route template to each accessible component in the app with an [`@page`][9] directive. When a Razor file with an [`@page`][9] directive is compiled, the generated class is given a <xref:Microsoft.AspNetCore.Mvc.RouteAttribute> specifying the route template. At runtime, the router searches for component classes with a <xref:Microsoft.AspNetCore.Mvc.RouteAttribute> and renders whichever component has a route template that matches the requested URL. -`Pages/RouteParameter.razor`: +The following `HelloWorld` component uses a route template of `/hello-world`. The rendered webpage for the component is reached at the relative URL `/hello-world`. When running a Blazor app locally with the default protocol, host, and port, the `HelloWorld` component is requested in the browser at `https://localhost:5001/hello-world`. Components that produce webpages usually reside in the `Pages` folder, but you can use any folder to hold components, including within nested folders. -:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Pages/index/RouteParameter.razor" highlight="2,7-8"::: +`Pages/HelloWorld.razor`: -Optional route parameters aren't supported, so two [`@page`][9] directives are applied in the preceding example. The first [`@page`][9] directive permits navigation to the component without a route parameter. The second [`@page`][9] directive receives the `{text}` route parameter and assigns the value to the `Text` property. +:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Pages/index/HelloWorld.razor"::: -For information on catch-all route parameters (`{*pageRoute}`), which capture paths across multiple folder boundaries, see <xref:blazor/fundamentals/routing#catch-all-route-parameters>. +The preceding component loads in the browser at `/hello-world` regardless of whether or not you add the component to the app's UI navigation. Optionally, components can be added to the `NavMenu` component so that a link to the component appears in the app's UI-based navigation. -## Child content render fragments +For the preceding `HelloWorld` component, you can add a `NavLink` component to the `NavMenu` component in the `Shared` folder. For more information, including descriptions of the `NavLink` and `NavMenu` components, see <xref:blazor/fundamentals/routing>. -Components can set the content of another component. The assigning component provides the content between the child component's opening and closing tags. +### Markup -In the following example, the `RenderFragmentChild` component has a `ChildContent` component parameter that represents a segment of the UI to render as a <xref:Microsoft.AspNetCore.Components.RenderFragment>. The position of `ChildContent` in the component's Razor markup is where the content is rendered in the final HTML output. +A component's UI is defined using [Razor syntax](xref:mvc/views/razor), which consists of Razor markup, C#, and HTML. When an app is compiled, the HTML markup and C# rendering logic are converted into a component class. The name of the generated class matches the name of the file. -`Shared/RenderFragmentChild.razor`: +Members of the component class are defined in one or more [`@code`][1] blocks. In [`@code`][1] blocks, component state is specified and processed with C#: -:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Shared/index/RenderFragmentChild.razor"::: +* Property and field initializers. +* Parameter values from arguments passed by parent components and route parameters. +* Methods for user event handling, lifecycle events, and custom component logic. -> [!IMPORTANT] -> The property receiving the <xref:Microsoft.AspNetCore.Components.RenderFragment> content must be named `ChildContent` by convention. -> -> [Event callbacks](xref:blazor/components/event-handling#eventcallback) aren't supported for <xref:Microsoft.AspNetCore.Components.RenderFragment>. +Component members are used in rendering logic using C# expressions that start with the `@` symbol. For example, a C# field is rendered by prefixing `@` to the field name. The following `Markup` component evaluates and renders: -The following `RenderFragmentParent` component provides content for rendering the `RenderFragmentChild` by placing the content inside the child component's opening and closing tags. +* `headingFontStyle` for the CSS property value `font-style` of the heading element. +* `headingText` for the content of the heading element. -`Pages/RenderFragmentParent.razor`: +`Pages/Markup.razor`: -:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Pages/index/RenderFragmentParent.razor"::: +:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Pages/index/Markup.razor"::: -Due to the way that Blazor renders child content, rendering components inside a [`for`](/dotnet/csharp/language-reference/keywords/for) loop requires a local index variable if the incrementing loop variable is used in the `RenderFragmentChild` component's content. The following example can be added to the preceding `RenderFragmentParent` component: +> [!NOTE] +> Examples throughout the Blazor documentation specify the [`private` access modifier](/dotnet/csharp/language-reference/keywords/private) for private members. Private members are scoped to a component's class. However, C# assumes the `private` access modifier when no access modifier is present, so explicitly marking members "`private`" in your own code is optional. For more information on access modifiers, see [Access Modifiers (C# Programming Guide)](/dotnet/csharp/programming-guide/classes-and-structs/access-modifiers). -```razor -<h1>Three children with an index variable</h1> +The Blazor framework processes a component internally as a [*render tree*](https://developer.mozilla.org/docs/Web/Performance/How_browsers_work#render), which is the combination of a component's [Document Object Model (DOM)](https://developer.mozilla.org/docs/Web/API/Document_Object_Model/Introduction) and [Cascading Style Sheet Object Model (CSSOM)](https://developer.mozilla.org/docs/Web/API/CSS_Object_Model). After the component is initially rendered, the component's render tree is regenerated in response to events. Blazor compares the new render tree against the previous render tree and applies any modifications to the browser's DOM for display. For more information, see <xref:blazor/components/rendering>. -@for (int c = 0; c < 3; c++) -{ - var current = c; +Components are ordinary [C# classes](/dotnet/csharp/programming-guide/classes-and-structs/classes) and can be placed anywhere within a project. Components that produce webpages usually reside in the `Pages` folder. Non-page components are frequently placed in the `Shared` folder or a custom folder added to the project. - <RenderFragmentChild> - Count: @current - </RenderFragmentChild> -} -``` +Razor syntax for C# control structures, directives, and directive attributes are lowercase (examples: [`@if`](xref:mvc/views/razor#conditionals-if-else-if-else-and-switch), [`@code`](xref:mvc/views/razor#code), [`@bind`](xref:mvc/views/razor#bind)). Property names are uppercase (example: `@Body` for <xref:Microsoft.AspNetCore.Components.LayoutComponentBase.Body?displayProperty=nameWithType>). -Alternatively, use a [`foreach`](/dotnet/csharp/language-reference/keywords/foreach-in) loop with <xref:System.Linq.Enumerable.Range%2A?displayProperty=nameWithType> instead of a [`for`](/dotnet/csharp/language-reference/keywords/for) loop. The following example can be added to the preceding `RenderFragmentParent` component: +### Asynchronous methods (`async`) don't support returning `void` -```razor -<h1>Second example of three children with an index variable</h1> +The Blazor framework doesn't track `void`-returning asynchronous methods (`async`). As a result, exceptions aren't caught if `void` is returned. Always return a <xref:System.Threading.Tasks.Task> from asynchronous methods. -@foreach (var c in Enumerable.Range(0,3)) -{ - <RenderFragmentChild> - Count: @c - </RenderFragmentChild> -} -``` +### Nested components -Render fragments are used to render child content throughout Blazor apps and are described with examples in the following articles and article sections: +Components can include other components by declaring them using HTML syntax. The markup for using a component looks like an HTML tag where the name of the tag is the component type. -* [Blazor layouts](xref:blazor/components/layouts) -* [Pass data across a component hierarchy](xref:blazor/components/cascading-values-and-parameters#pass-data-across-a-component-hierarchy) -* [Templated components](xref:blazor/components/templated-components) -* [Global exception handling](xref:blazor/fundamentals/handle-errors#global-exception-handling) +Consider the following `Heading` component, which can be used by other components to display a heading. -> [!NOTE] -> Blazor framework's [built-in Razor components](xref:blazor/components/built-in-components) use the same `ChildContent` component parameter convention to set their content. You can see the components that set child content by searching for the component parameter property name `ChildContent` in the [API documentation (filters API with the search term "ChildContent")](/dotnet/api/?term=ChildContent). +`Shared/Heading.razor`: -## Render fragments for reusable rendering logic +:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Shared/index/Heading.razor"::: -You can factor out child components purely as a way of reusing rendering logic. In any component's `@code` block, define a <xref:Microsoft.AspNetCore.Components.RenderFragment> and render the fragment from any location as many times as needed: +The following markup in the `HeadingExample` component renders the preceding `Heading` component at the location where the `<Heading />` tag appears. -```razor -<h1>Hello, world!</h1> - -@RenderWelcomeInfo +`Pages/HeadingExample.razor`: -<p>Render the welcome info a second time:</p> +:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Pages/index/HeadingExample.razor"::: -@RenderWelcomeInfo +If a component contains an HTML element with an uppercase first letter that doesn't match a component name within the same namespace, a warning is emitted indicating that the element has an unexpected name. Adding an [`@using`][2] directive for the component's namespace makes the component available, which resolves the warning. For more information, see the [Namespaces](#namespaces) section. -@code { - private RenderFragment RenderWelcomeInfo = __builder => - { - <p>Welcome to your new app!</p> - }; -} -``` +The `Heading` component example shown in this section doesn't have an [`@page`][9] directive, so the `Heading` component isn't directly accessible to a user via a direct request in the browser. However, any component with an [`@page`][9] directive can be nested in another component. If the `Heading` component was directly accessible by including `@page "/heading"` at the top of its Razor file, then the component would be rendered for browser requests at both `/heading` and `/heading-example`. -For more information, see [Reuse rendering logic](xref:blazor/performance#define-reusable-renderfragments-in-code). +### Namespaces -## Overwritten parameters +Typically, a component's namespace is derived from the app's root namespace and the component's location (folder) within the app. If the app's root namespace is `BlazorSample` and the `Counter` component resides in the `Pages` folder: -The Blazor framework generally imposes safe parent-to-child parameter assignment: +* The `Counter` component's namespace is `BlazorSample.Pages`. +* The fully qualified type name of the component is `BlazorSample.Pages.Counter`. -* Parameters aren't overwritten unexpectedly. -* Side effects are minimized. For example, additional renders are avoided because they may create infinite rendering loops. +For custom folders that hold components, add an [`@using`][2] directive to the parent component or to the app's `_Imports.razor` file. The following example makes components in the `Components` folder available: -A child component receives new parameter values that possibly overwrite existing values when the parent component rerenders. Accidentally overwriting parameter values in a child component often occurs when developing the component with one or more data-bound parameters and the developer writes directly to a parameter in the child: +```razor +@using BlazorSample.Components +``` -* The child component is rendered with one or more parameter values from the parent component. -* The child writes directly to the value of a parameter. -* The parent component rerenders and overwrites the value of the child's parameter. +> [!NOTE] +> [`@using`][2] directives in the `_Imports.razor` file are only applied to Razor files (`.razor`), not C# files (`.cs`). -The potential for overwriting parameter values extends into the child component's property `set` accessors, too. +Components can also be referenced using their fully qualified names, which doesn't require an [`@using`][2] directive. The following example directly references the `ProductDetail` component in the `Components` folder of the app: -> [!IMPORTANT] -> Our general guidance is not to create components that directly write to their own parameters after the component is rendered for the first time. +```razor +<BlazorSample.Components.ProductDetail /> +``` -Consider the following `Expander` component that: +The namespace of a component authored with Razor is based on the following (in priority order): -* Renders child content. -* Toggles showing child content with a component parameter (`Expanded`). +* The [`@namespace`][8] directive in the Razor file's markup (for example, `@namespace BlazorSample.CustomNamespace`). +* The project's `RootNamespace` in the project file (for example, `<RootNamespace>BlazorSample</RootNamespace>`). +* The project name, taken from the project file's file name (`.csproj`), and the path from the project root to the component. For example, the framework resolves `{PROJECT ROOT}/Pages/Index.razor` with a project namespace of `BlazorSample` (`BlazorSample.csproj`) to the namespace `BlazorSample.Pages` for the `Index` component. `{PROJECT ROOT}` is the project root path. Components follow C# name binding rules. For the `Index` component in this example, the components in scope are all of the components: + * In the same folder, `Pages`. + * The components in the project's root that don't explicitly specify a different namespace. -After the following `Expander` component demonstrates an overwritten parameter, a modified `Expander` component is shown to demonstrate the correct approach for this scenario. The following examples can be placed in a local sample app to experience the behaviors described. +The following are **not** supported: -`Shared/Expander.razor`: +* The [`global::`](/dotnet/csharp/language-reference/operators/namespace-alias-qualifier) qualification. +* Importing components with aliased [`using`](/dotnet/csharp/language-reference/keywords/using-statement) statements. For example, `@using Foo = Bar` isn't supported. +* Partially-qualified names. For example, you can't add `@using BlazorSample` to a component and then reference the `NavMenu` component in the app's `Shared` folder (`Shared/NavMenu.razor`) with `<Shared.NavMenu></Shared.NavMenu>`. -:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Shared/index/BadExpander.razor"::: +### Partial class support -The `Expander` component is added to the following `ExpanderExample` parent component that may call <xref:Microsoft.AspNetCore.Components.ComponentBase.StateHasChanged%2A>: +Components are generated as [C# partial classes](/dotnet/csharp/programming-guide/classes-and-structs/partial-classes-and-methods) and are authored using either of the following approaches: -* Calling <xref:Microsoft.AspNetCore.Components.ComponentBase.StateHasChanged%2A> in developer code notifies a component that its state has changed and typically triggers component rerendering to update the UI. <xref:Microsoft.AspNetCore.Components.ComponentBase.StateHasChanged%2A> is covered in more detail later in <xref:blazor/components/lifecycle> and <xref:blazor/components/rendering>. -* The button's `@onclick` directive attribute attaches an event handler to the button's `onclick` event. Event handling is covered in more detail later in <xref:blazor/components/event-handling>. +* A single file contains C# code defined in one or more [`@code`][1] blocks, HTML markup, and Razor markup. Blazor project templates define their components using this single-file approach. +* HTML and Razor markup are placed in a Razor file (`.razor`). C# code is placed in a code-behind file defined as a partial class (`.cs`). -`Pages/ExpanderExample.razor`: +> [!NOTE] +> A component stylesheet that defines component-specific styles is a separate file (`.css`). Blazor CSS isolation is described later in <xref:blazor/components/css-isolation>. -:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Pages/index/ExpanderExample.razor"::: +The following example shows the default `Counter` component with an [`@code`][1] block in an app generated from a Blazor project template. Markup and C# code are in the same file. This is the most common approach taken in component authoring. -Initially, the `Expander` components behave independently when their `Expanded` properties are toggled. The child components maintain their states as expected. +`Pages/Counter.razor`: -If <xref:Microsoft.AspNetCore.Components.ComponentBase.StateHasChanged%2A> is called in a parent component, the Blazor framework rerenders child components if their parameters might have changed: +:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Pages/Counter.razor"::: -* For a group of parameter types that Blazor explicitly checks, Blazor rerenders a child component if it detects that any of the parameters have changed. -* For unchecked parameter types, Blazor rerenders the child component *regardless of whether or not the parameters have changed*. Child content falls into this category of parameter types because child content is of type <xref:Microsoft.AspNetCore.Components.RenderFragment>, which is a delegate that refers to other mutable objects. +The following `Counter` component splits HTML and Razor markup from C# code using a code-behind file with a partial class: -For the `ExpanderExample` component: +`Pages/CounterPartialClass.razor`: -* The first `Expander` component sets child content in a potentially mutable <xref:Microsoft.AspNetCore.Components.RenderFragment>, so a call to <xref:Microsoft.AspNetCore.Components.ComponentBase.StateHasChanged%2A> in the parent component automatically rerenders the component and potentially overwrites the value of `Expanded` to its initial value of `true`. -* The second `Expander` component doesn't set child content. Therefore, a potentially mutable <xref:Microsoft.AspNetCore.Components.RenderFragment> doesn't exist. A call to <xref:Microsoft.AspNetCore.Components.ComponentBase.StateHasChanged%2A> in the parent component doesn't automatically rerender the child component, so the component's `Expanded` value isn't overwritten. +```razor +@page "/counter-partial-class" -To maintain state in the preceding scenario, use a *private field* in the `Expander` component to maintain its toggled state. +<h1>Counter</h1> -The following revised `Expander` component: +<p>Current count: @currentCount</p> -* Accepts the `Expanded` component parameter value from the parent. -* Assigns the component parameter value to a *private field* (`expanded`) in the [`OnInitialized` event](xref:blazor/components/lifecycle#component-initialization-oninitializedasync). -* Uses the private field to maintain its internal toggle state, which demonstrates how to avoid writing directly to a parameter. +<button class="btn btn-primary" @onclick="IncrementCount">Click me</button> +``` -> [!NOTE] -> The advice in this section extends to similar logic in component parameter `set` accessors, which can result in similar undesirable side effects. +`Pages/CounterPartialClass.razor.cs`: -`Shared/Expander.razor`: +```csharp +namespace BlazorSample.Pages +{ + public partial class CounterPartialClass + { + private int currentCount = 0; -:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Shared/index/Expander.razor"::: + void IncrementCount() + { + currentCount++; + } + } +} +``` -For additional information, see [Blazor Two Way Binding Error (dotnet/aspnetcore #24599)](https://github.com/dotnet/aspnetcore/issues/24599). +[`@using`][2] directives in the `_Imports.razor` file are only applied to Razor files (`.razor`), not C# files (`.cs`). Add namespaces to a partial class file as needed. -For more information on change detection, including information on the exact types that Blazor checks, see <xref:blazor/components/rendering#rendering-conventions-for-componentbase>. +Typical namespaces used by components: -## Attribute splatting and arbitrary parameters +```csharp +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; +``` -Components can capture and render additional attributes in addition to the component's declared parameters. Additional attributes can be captured in a dictionary and then *splatted* onto an element when the component is rendered using the [`@attributes`][3] Razor directive attribute. This scenario is useful for defining a component that produces a markup element that supports a variety of customizations. For example, it can be tedious to define attributes separately for an `<input>` that supports many parameters. +Typical namespaces also include the namespace of the app and the namespace corresponding to the app's `Shared` folder: -In the following `Splat` component: +```csharp +using BlazorSample; +using BlazorSample.Shared; +``` -* The first `<input>` element (`id="useIndividualParams"`) uses individual component parameters. -* The second `<input>` element (`id="useAttributesDict"`) uses attribute splatting. +### Specify a base class -`Pages/Splat.razor`: +The [`@inherits`][6] directive is used to specify a base class for a component. The following example shows how a component can inherit a base class to provide the component's properties and methods. The `BlazorRocksBase` base class derives from <xref:Microsoft.AspNetCore.Components.ComponentBase>. -:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Pages/index/Splat.razor"::: +`Pages/BlazorRocks.razor`: -The rendered `<input>` elements in the webpage are identical: +:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Pages/index/BlazorRocks.razor"::: -```html -<input id="useIndividualParams" - maxlength="10" - placeholder="Input placeholder text" - required="required" - size="50"> +`BlazorRocksBase.cs`: -<input id="useAttributesDict" - maxlength="10" - placeholder="Input placeholder text" - required="required" - size="50"> -``` +:::code language="csharp" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/BlazorRocksBase.cs"::: -To accept arbitrary attributes, define a [component parameter](#component-parameters) with the <xref:Microsoft.AspNetCore.Components.ParameterAttribute.CaptureUnmatchedValues> property set to `true`: +## Component parameters -```razor -@code { - [Parameter(CaptureUnmatchedValues = true)] - public Dictionary<string, object> InputAttributes { get; set; } -} -``` +*Component parameters* pass data to components and are defined using public [C# properties](/dotnet/csharp/programming-guide/classes-and-structs/properties) on the component class with the [`[Parameter]` attribute](xref:Microsoft.AspNetCore.Components.ParameterAttribute). In the following example, a built-in reference type (<xref:System.String?displayProperty=fullName>) and a user-defined reference type (`PanelBody`) are passed as component parameters. -The <xref:Microsoft.AspNetCore.Components.ParameterAttribute.CaptureUnmatchedValues> property on [`[Parameter]`](xref:Microsoft.AspNetCore.Components.ParameterAttribute) allows the parameter to match all attributes that don't match any other parameter. A component can only define a single parameter with <xref:Microsoft.AspNetCore.Components.ParameterAttribute.CaptureUnmatchedValues>. The property type used with <xref:Microsoft.AspNetCore.Components.ParameterAttribute.CaptureUnmatchedValues> must be assignable from [`Dictionary<string, object>`](xref:System.Collections.Generic.Dictionary%602) with string keys. Use of [`IEnumerable<KeyValuePair<string, object>>`](xref:System.Collections.Generic.IEnumerable%601) or [`IReadOnlyDictionary<string, object>`](xref:System.Collections.Generic.IReadOnlyDictionary%602) are also options in this scenario. +`PanelBody.cs`: -The position of [`@attributes`][3] relative to the position of element attributes is important. When [`@attributes`][3] are splatted on the element, the attributes are processed from right to left (last to first). Consider the following example of a parent component that consumes a child component: +:::code language="csharp" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/PanelBody.cs"::: -`Shared/AttributeOrderChild1.razor`: +`Shared/ParameterChild.razor`: -:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Shared/index/AttributeOrderChild1.razor"::: +:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Shared/index/ParameterChild.razor"::: -`Pages/AttributeOrderParent1.razor`: +> [!WARNING] +> Providing initial values for component parameters is supported, but don't create a component that writes to its own parameters after the component is rendered for the first time. For more information, see the [Overwritten parameters](#overwritten-parameters) section of this article. -:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Pages/index/AttributeOrderParent1.razor"::: +The `Title` and `Body` component parameters of the `ParameterChild` component are set by arguments in the HTML tag that renders the instance of the component. The following `ParameterParent` component renders two `ParameterChild` components: -The `AttributeOrderChild1` component's `extra` attribute is set to the right of [`@attributes`][3]. The `AttributeOrderParent1` component's rendered `<div>` contains `extra="5"` when passed through the additional attribute because the attributes are processed right to left (last to first): +* The first `ParameterChild` component is rendered without supplying parameter arguments. +* The second `ParameterChild` component receives values for `Title` and `Body` from the `ParameterParent` component, which uses an [explicit C# expression](xref:mvc/views/razor#explicit-razor-expressions) to set the values of the `PanelBody`'s properties. -```html -<div extra="5" /> -``` +`Pages/ParameterParent.razor`: -In the following example, the order of `extra` and [`@attributes`][3] is reversed in the child component's `<div>`: +:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Pages/index/ParameterParent.razor"::: -`Shared/AttributeOrderChild2.razor`: +The following rendered HTML markup from the `ParameterParent` component shows `ParameterChild` component default values when the `ParameterParent` component doesn't supply component parameter values. When the `ParameterParent` component provides component parameter values, they replace the `ParameterChild` component's default values. -:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Shared/index/AttributeOrderChild2.razor"::: +> [!NOTE] +> For clarity, rendered CSS style classes aren't shown in the following rendered HTML markup. -`Pages/AttributeOrderParent2.razor`: +```html +<h1>Child component (without attribute values)</h1> -:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Pages/index/AttributeOrderParent2.razor"::: +<div> + <div>Set By Child</div> + <div>Set by child.</div> +</div> -The `<div>` in the parent component's rendered webpage contains `extra="10"` when passed through the additional attribute: +<h1>Child component (with attribute values)</h1> -```html -<div extra="10" /> +<div> + <div>Set by Parent</div> + <div>Set by parent.</div> +</div> ``` -## Capture references to components +Assign a C# field, property, or result of a method to a component parameter as an HTML attribute value using [Razor's reserved `@` symbol](xref:mvc/views/razor#razor-syntax). The following `ParameterParent2` component displays four instances of the preceding `ParameterChild` component and sets their `Title` parameter values to: -Component references provide a way to reference a component instance for issuing commands. To capture a component reference: +* The value of the `title` field. +* The result of the `GetTitle` C# method. +* The current local date in long format with <xref:System.DateTime.ToLongDateString%2A>, which uses an [implicit C# expression](xref:mvc/views/razor#implicit-razor-expressions). +* The `panelData` object's `Title` property. -* Add an [`@ref`][4] attribute to the child component. -* Define a field with the same type as the child component. +The `@` prefix is required for string parameters. Otherwise, the framework assumes that a string literal is set. -When the component is rendered, the field is populated with the component instance. You can then invoke .NET methods on the instance. +Outside of string parameters, we recommend use the use of the `@` prefix for nonliterals, even when they aren't strictly required. -Consider the following `ReferenceChild` component that logs a message when its `ChildMethod` is called. +We don't recommend the use of the `@` prefix for literals (for example, boolean values), keywords (for example, `this`), or `null`, but you can choose to use them if you wish. For example, `IsFixed="@true"` is uncommon but supported. -`Shared/ReferenceChild.razor`: +Quotes around parameter attribute values are optional in most cases per the HTML5 specification. For example, `Value=this` is supported, instead of `Value="this"`. However, we recommend using quotes because it's easier to remember and widely adopted across web-based technologies. -:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Shared/index/ReferenceChild.razor"::: +Throughout the documentation, code examples: -A component reference is only populated after the component is rendered and its output includes `ReferenceChild`'s element. Until the component is rendered, there's nothing to reference. +* Always use quotes. Example: `Value="this"`. +* Nonliterals always use the `@` prefix, even when it's optional. Examples: `Title="@title"`, where `title` is a string-typed variable. `Count="@ct"`, where `ct` is a number-typed variable. +* Literals, outside of Razor expressions, always avoid `@`. Example: `IsFixed="true"`. -To manipulate component references after the component has finished rendering, use the [`OnAfterRender` or `OnAfterRenderAsync` methods](xref:blazor/components/lifecycle#after-component-render-onafterrenderasync). +`Pages/ParameterParent2.razor`: -To use a reference variable with an event handler, use a lambda expression or assign the event handler delegate in the [`OnAfterRender` or `OnAfterRenderAsync` methods](xref:blazor/components/lifecycle#after-component-render-onafterrenderasync). This ensures that the reference variable is assigned before the event handler is assigned. +:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Pages/index/ParameterParent2.razor"::: -The following lambda approach uses the preceding `ReferenceChild` component. +> [!NOTE] +> When assigning a C# member to a component parameter, prefix the member with the `@` symbol and never prefix the parameter's HTML attribute. +> +> Correct: +> +> ```razor +> <ParameterChild Title="@title" /> +> ``` +> +> Incorrect: +> +> ```razor +> <ParameterChild @Title="title" /> +> ``` -`Pages/ReferenceParent1.razor`: +Unlike in Razor pages (`.cshtml`), Blazor can't perform asynchronous work in a Razor expression while rendering a component. This is because Blazor is designed for rendering interactive UIs. In an interactive UI, the screen must always display something, so it doesn't make sense to block the rendering flow. Instead, asynchronous work is performed during one of the [asynchronous lifecycle events](xref:blazor/components/lifecycle). After each asynchronous lifecycle event, the component may render again. The following Razor syntax is **not** supported: -:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Pages/index/ReferenceParent1.razor"::: +```razor +<ParameterChild Title="@await ..." /> +``` -The following delegate approach uses the preceding `ReferenceChild` component. +The code in the preceding example generates a *compiler error* when the app is built: -`Pages/ReferenceParent2.razor`: +> The 'await' operator can only be used within an async method. Consider marking this method with the 'async' modifier and changing its return type to 'Task'. -:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Pages/index/ReferenceParent2.razor"::: +To obtain a value for the `Title` parameter in the preceding example asynchronously, the component can use the [`OnInitializedAsync` lifecycle event](xref:blazor/components/lifecycle#component-initialization-oninitializedasync), as the following example demonstrates: -While capturing component references use a similar syntax to [capturing element references](xref:blazor/js-interop/call-javascript-from-dotnet#capture-references-to-elements), capturing component references isn't a JavaScript interop feature. Component references aren't passed to JavaScript code. Component references are only used in .NET code. +```razor +<ParameterChild Title="@title" /> -> [!IMPORTANT] -> Do **not** use component references to mutate the state of child components. Instead, use normal declarative component parameters to pass data to child components. Use of component parameters result in child components that rerender at the correct times automatically. For more information, see the [component parameters](#component-parameters) section and the <xref:blazor/components/data-binding> article. +@code { + private string title; + + protected override async Task OnInitializedAsync() + { + title = await ...; + } +} +``` -## Synchronization context +For more information, see <xref:blazor/components/lifecycle>. -Blazor uses a synchronization context (<xref:System.Threading.SynchronizationContext>) to enforce a single logical thread of execution. A component's [lifecycle methods](xref:blazor/components/lifecycle) and event callbacks raised by Blazor are executed on the synchronization context. +Use of an explicit Razor expression to concatenate text with an expression result for assignment to a parameter is **not** supported. The following example seeks to concatenate the text "`Set by `" with an object's property value. Although this syntax is supported in a Razor page (`.cshtml`), it isn't valid for assignment to the child's `Title` parameter in a component. The following Razor syntax is **not** supported: -Blazor Server's synchronization context attempts to emulate a single-threaded environment so that it closely matches the WebAssembly model in the browser, which is single threaded. At any given point in time, work is performed on exactly one thread, which yields the impression of a single logical thread. No two operations execute concurrently. +```razor +<ParameterChild Title="Set by @(panelData.Title)" /> +``` -### Avoid thread-blocking calls +The code in the preceding example generates a *compiler error* when the app is built: -Generally, don't call the following methods in components. The following methods block the execution thread and thus block the app from resuming work until the underlying <xref:System.Threading.Tasks.Task> is complete: +> Component attributes do not support complex content (mixed C# and markup). -* <xref:System.Threading.Tasks.Task%601.Result%2A> -* <xref:System.Threading.Tasks.Task.Wait%2A> -* <xref:System.Threading.Tasks.Task.WaitAny%2A> -* <xref:System.Threading.Tasks.Task.WaitAll%2A> -* <xref:System.Threading.Thread.Sleep%2A> -* <xref:System.Runtime.CompilerServices.TaskAwaiter.GetResult%2A> +To support the assignment of a composed value, use a method, field, or property. The following example performs the concatenation of "`Set by `" and an object's property value in the C# method `GetTitle`: -> [!NOTE] -> Blazor documentation examples that use the thread-blocking methods mentioned in this section are only using the methods for demonstration purposes, not as recommended coding guidance. For example, a few component code demonstrations simulate a long-running process by calling <xref:System.Threading.Thread.Sleep%2A?displayProperty=nameWithType>. +`Pages/ParameterParent3.razor`: -### Invoke component methods externally to update state +:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Pages/index/ParameterParent3.razor"::: -In the event a component must be updated based on an external event, such as a timer or other notification, use the `InvokeAsync` method, which dispatches code execution to Blazor's synchronization context. For example, consider the following *notifier service* that can notify any listening component about updated state. The `Update` method can be called from anywhere in the app. +For more information, see <xref:mvc/views/razor>. -`TimerService.cs`: +> [!WARNING] +> Providing initial values for component parameters is supported, but don't create a component that writes to its own parameters after the component is rendered for the first time. For more information, see the [Overwritten parameters](#overwritten-parameters) section of this article. -:::code language="csharp" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/TimerService.cs"::: +Component parameters should be declared as *auto-properties*, meaning that they shouldn't contain custom logic in their `get` or `set` accessors. For example, the following `StartData` property is an auto-property: -`NotifierService.cs`: +```csharp +[Parameter] +public DateTime StartData { get; set; } +``` -:::code language="csharp" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/NotifierService.cs"::: +Don't place custom logic in the `get` or `set` accessor because component parameters are purely intended for use as a channel for a parent component to flow information to a child component. If a `set` accessor of a child component property contains logic that causes rerendering of the parent component, an infinite rendering loop results. -Register the services: +To transform a received parameter value: -* In a Blazor WebAssembly app, register the services as singletons in `Program.cs`: +* Leave the parameter property as an auto-property to represent the supplied raw data. +* Create a different property or method to supply the transformed data based on the parameter property. - ```csharp - builder.Services.AddSingleton<NotifierService>(); - builder.Services.AddSingleton<TimerService>(); - ``` +Override [`OnParametersSetAsync`](xref:blazor/components/lifecycle#after-parameters-are-set-onparameterssetasync) to transform a received parameter each time new data is received. -* In a Blazor Server app, register the services as scoped in `Startup.ConfigureServices`: +Writing an initial value to a component parameter is supported because initial value assignments don't interfere with the Blazor's automatic component rendering. The following assignment of the current local <xref:System.DateTime> with <xref:System.DateTime.Now?displayProperty=nameWithType> to `StartData` is valid syntax in a component: - ```csharp - services.AddScoped<NotifierService>(); - services.AddScoped<TimerService>(); - ``` +```csharp +[Parameter] +public DateTime StartData { get; set; } = DateTime.Now; +``` -Use the `NotifierService` to update a component. +After the initial assignment of <xref:System.DateTime.Now?displayProperty=nameWithType>, do **not** assign a value to `StartData` in developer code. For more information, see the [Overwritten parameters](#overwritten-parameters) section of this article. -`Pages/ReceiveNotifications.razor`: +## Route parameters -:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Pages/index/ReceiveNotifications.razor"::: +Components can specify route parameters in the route template of the [`@page`][9] directive. The [Blazor router](xref:blazor/fundamentals/routing) uses route parameters to populate corresponding component parameters. -In the preceding example: +Optional route parameters are supported. In the following example, the `text` optional parameter assigns the value of the route segment to the component's `Text` property. If the segment isn't present, the value of `Text` is set to "`fantastic`" in the [`OnInitialized` lifecycle method](xref:blazor/components/lifecycle#component-initialization-oninitializedasync). -* `NotifierService` invokes the component's `OnNotify` method outside of Blazor's synchronization context. `InvokeAsync` is used to switch to the correct context and queue a render. For more information, see <xref:blazor/components/rendering>. -* The component implements <xref:System.IDisposable>. The `OnNotify` delegate is unsubscribed in the `Dispose` method, which is called by the framework when the component is disposed. For more information, see <xref:blazor/components/lifecycle#component-disposal-with-idisposable-and-iasyncdisposable>. +`Pages/RouteParameter.razor`: -> [!IMPORTANT] -> If a Razor component defines an event that's triggered from a background thread, the component might be required to capture and restore the execution context (<xref:System.Threading.ExecutionContext>) at the time the handler is registered. For more information, see [Calling `InvokeAsync(StateHasChanged)` causes page to fallback to default culture (dotnet/aspnetcore #28521)](https://github.com/dotnet/aspnetcore/issues/28521). +:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Pages/index/RouteParameter.razor" highlight="1,6-7"::: -## Use `@key` to control the preservation of elements and components +For information on catch-all route parameters (`{*pageRoute}`), which capture paths across multiple folder boundaries, see <xref:blazor/fundamentals/routing#catch-all-route-parameters>. -When rendering a list of elements or components and the elements or components subsequently change, Blazor must decide which of the previous elements or components are retained and how model objects should map to them. Normally, this process is automatic and sufficient for general rendering, but there are often cases where controlling the process using the [`@key`][5] directive attribute is required. +## Child content render fragments -Consider the following example that demonstrates a collection mapping problem that's solved by using [`@key`][5]. +Components can set the content of another component. The assigning component provides the content between the child component's opening and closing tags. -For the following `Details` and `People` components: +In the following example, the `RenderFragmentChild` component has a `ChildContent` component parameter that represents a segment of the UI to render as a <xref:Microsoft.AspNetCore.Components.RenderFragment>. The position of `ChildContent` in the component's Razor markup is where the content is rendered in the final HTML output. -* The `Details` component receives data (`Data`) from the parent `People` component, which is displayed in an `<input>` element. Any given displayed `<input>` element can receive the focus of the page from the user when they select one of the `<input>` elements. -* The `People` component creates a list of person objects for display using the `Details` component. Every three seconds, a new person is added to the collection. +`Shared/RenderFragmentChild.razor`: -This demonstration allows you to: +:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Shared/index/RenderFragmentChild.razor"::: -* Select an `<input>` from among several rendered `Details` components. -* Study the behavior of the page's focus as the people collection automatically grows. +> [!IMPORTANT] +> The property receiving the <xref:Microsoft.AspNetCore.Components.RenderFragment> content must be named `ChildContent` by convention. +> +> [Event callbacks](xref:blazor/components/event-handling#eventcallback) aren't supported for <xref:Microsoft.AspNetCore.Components.RenderFragment>. -`Shared/Details.razor`: +The following `RenderFragmentParent` component provides content for rendering the `RenderFragmentChild` by placing the content inside the child component's opening and closing tags. -:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Shared/index/Details.razor"::: +`Pages/RenderFragmentParent.razor`: -In the following `People` component, each iteration of adding a person in `OnTimerCallback` results in Blazor rebuilding the entire collection. The page's focus remains on the *same index* position of `<input>` elements, so the focus shifts each time a person is added. *Shifting the focus away from what the user selected isn't desirable behavior.* After demonstrating the poor behavior with the following component, the [`@key`][5] directive attribute is used to improve the user's experience. +:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Pages/index/RenderFragmentParent.razor"::: -`Pages/People.razor`: +Due to the way that Blazor renders child content, rendering components inside a [`for`](/dotnet/csharp/language-reference/keywords/for) loop requires a local index variable if the incrementing loop variable is used in the `RenderFragmentChild` component's content. The following example can be added to the preceding `RenderFragmentParent` component: -:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Pages/index/People.razor"::: +```razor +<h1>Three children with an index variable</h1> -The contents of the `people` collection changes with inserted, deleted, or re-ordered entries. Rerendering can lead to visible behavior differences. For example, each time a person is inserted into the `people` collection, the user's focus is lost. +@for (int c = 0; c < 3; c++) +{ + var current = c; -The mapping process of elements or components to a collection can be controlled with the [`@key`][5] directive attribute. Use of [`@key`][5] guarantees the preservation of elements or components based on the key's value. If the `Details` component in the preceding example is keyed on the `person` item, Blazor ignores rerendering `Details` components that haven't changed. + <RenderFragmentChild> + Count: @current + </RenderFragmentChild> +} +``` -To modify the `People` component to use the [`@key`][5] directive attribute with the `people` collection, update the `<Details>` element to the following: +Alternatively, use a [`foreach`](/dotnet/csharp/language-reference/keywords/foreach-in) loop with <xref:System.Linq.Enumerable.Range%2A?displayProperty=nameWithType> instead of a [`for`](/dotnet/csharp/language-reference/keywords/for) loop. The following example can be added to the preceding `RenderFragmentParent` component: ```razor -<Details @key="person" Data="@person.Data" /> +<h1>Second example of three children with an index variable</h1> + +@foreach (var c in Enumerable.Range(0,3)) +{ + <RenderFragmentChild> + Count: @c + </RenderFragmentChild> +} ``` -When the `people` collection changes, the association between `Details` instances and `person` instances is retained. When a `Person` is inserted at the beginning of the collection, one new `Details` instance is inserted at that corresponding position. Other instances are left unchanged. Therefore, the user's focus isn't lost as people are added to the collection. +Render fragments are used to render child content throughout Blazor apps and are described with examples in the following articles and article sections: -Other collection updates exhibit the same behavior when the [`@key`][5] directive attribute is used: +* [Blazor layouts](xref:blazor/components/layouts) +* [Pass data across a component hierarchy](xref:blazor/components/cascading-values-and-parameters#pass-data-across-a-component-hierarchy) +* [Templated components](xref:blazor/components/templated-components) +* [Global exception handling](xref:blazor/fundamentals/handle-errors#global-exception-handling) -* If an instance is deleted from the collection, only the corresponding component instance is removed from the UI. Other instances are left unchanged. -* If collection entries are re-ordered, the corresponding component instances are preserved and re-ordered in the UI. +> [!NOTE] +> Blazor framework's [built-in Razor components](xref:blazor/components/built-in-components) use the same `ChildContent` component parameter convention to set their content. You can see the components that set child content by searching for the component parameter property name `ChildContent` in the [API documentation (filters API with the search term "ChildContent")](/dotnet/api/?term=ChildContent). -> [!IMPORTANT] -> Keys are local to each container element or component. Keys aren't compared globally across the document. +## Render fragments for reusable rendering logic -### When to use `@key` +You can factor out child components purely as a way of reusing rendering logic. In any component's `@code` block, define a <xref:Microsoft.AspNetCore.Components.RenderFragment> and render the fragment from any location as many times as needed: -Typically, it makes sense to use [`@key`][5] whenever a list is rendered (for example, in a [`foreach`](/dotnet/csharp/language-reference/keywords/foreach-in) block) and a suitable value exists to define the [`@key`][5]. +```razor +<h1>Hello, world!</h1> -You can also use [`@key`][5] to preserve an element or component subtree when an object doesn't change, as the following examples show. +@RenderWelcomeInfo -Example 1: +<p>Render the welcome info a second time:</p> -```razor -<li @key="person"> - <input value="@person.Data" /> -</li> +@RenderWelcomeInfo + +@code { + private RenderFragment RenderWelcomeInfo = __builder => + { + <p>Welcome to your new app!</p> + }; +} ``` -Example 2: +For more information, see [Reuse rendering logic](xref:blazor/performance#define-reusable-renderfragments-in-code). -```razor -<div @key="person"> - @* other HTML elements *@ -</div> -``` +## Overwritten parameters -If an `person` instance changes, the [`@key`][5] attribute directive forces Blazor to: +The Blazor framework generally imposes safe parent-to-child parameter assignment: -* Discard the entire `<li>` or `<div>` and their descendants. -* Rebuild the subtree within the UI with new elements and components. +* Parameters aren't overwritten unexpectedly. +* Side effects are minimized. For example, additional renders are avoided because they may create infinite rendering loops. -This is useful to guarantee that no UI state is preserved when the collection changes within a subtree. +A child component receives new parameter values that possibly overwrite existing values when the parent component rerenders. Accidentally overwriting parameter values in a child component often occurs when developing the component with one or more data-bound parameters and the developer writes directly to a parameter in the child: -### Scope of `@key` +* The child component is rendered with one or more parameter values from the parent component. +* The child writes directly to the value of a parameter. +* The parent component rerenders and overwrites the value of the child's parameter. -The [`@key`][5] attribute directive is scoped to its own siblings within its parent. +The potential for overwriting parameter values extends into the child component's property `set` accessors, too. -Consider the following example. The `first` and `second` keys are compared against each other within the same scope of the outer `<div>` element: +> [!IMPORTANT] +> Our general guidance is not to create components that directly write to their own parameters after the component is rendered for the first time. -```razor -<div> - <div @key="first">...</div> - <div @key="second">...</div> -</div> -``` +Consider the following `Expander` component that: -The following example demonstrates `first` and `second` keys in their own scopes, unrelated to each other and without influence on each other. Each [`@key`][5] scope only applies to its parent `<div>` element, not across the parent `<div>` elements: +* Renders child content. +* Toggles showing child content with a component parameter (`Expanded`). -```razor -<div> - <div @key="first">...</div> -</div> -<div> - <div @key="second">...</div> -</div> -``` +After the following `Expander` component demonstrates an overwritten parameter, a modified `Expander` component is shown to demonstrate the correct approach for this scenario. The following examples can be placed in a local sample app to experience the behaviors described. -For the `Details` component shown earlier, the following examples render `person` data within the same [`@key`][5] scope and demonstrate typical use cases for [`@key`][5]: +`Shared/Expander.razor`: -```razor -<div> - @foreach (var person in people) - { - <Details @key="person" Data="@person.Data" /> - } -</div> -``` +:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Shared/index/BadExpander.razor"::: -```razor -@foreach (var person in people) -{ - <div @key="person"> - <Details Data="@person.Data" /> - </div> -} -``` +The `Expander` component is added to the following `ExpanderExample` parent component that may call <xref:Microsoft.AspNetCore.Components.ComponentBase.StateHasChanged%2A>: -```razor -<ol> - @foreach (var person in people) - { - <li @key="person"> - <Details Data="@person.Data" /> - </li> - } -</ol> -``` +* Calling <xref:Microsoft.AspNetCore.Components.ComponentBase.StateHasChanged%2A> in developer code notifies a component that its state has changed and typically triggers component rerendering to update the UI. <xref:Microsoft.AspNetCore.Components.ComponentBase.StateHasChanged%2A> is covered in more detail later in <xref:blazor/components/lifecycle> and <xref:blazor/components/rendering>. +* The button's `@onclick` directive attribute attaches an event handler to the button's `onclick` event. Event handling is covered in more detail later in <xref:blazor/components/event-handling>. -The following examples only scope [`@key`][5] to the `<div>` or `<li>` element that surrounds each `Details` component instance. Therefore, `person` data for each member of the `people` collection is **not** keyed on each `person` instance across the rendered `Details` components. Avoid the following patterns when using [`@key`][5]: +`Pages/ExpanderExample.razor`: -```razor -@foreach (var person in people) -{ - <div> - <Details @key="person" Data="@person.Data" /> - </div> -} -``` - -```razor -<ol> - @foreach (var person in people) - { - <li> - <Details @key="person" Data="@person.Data" /> - </li> - } -</ol> -``` - -### When not to use `@key` - -There's a performance cost when rendering with [`@key`][5]. The performance cost isn't large, but only specify [`@key`][5] if preserving the element or component benefits the app. +:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Pages/index/ExpanderExample.razor"::: -Even if [`@key`][5] isn't used, Blazor preserves child element and component instances as much as possible. The only advantage to using [`@key`][5] is control over *how* model instances are mapped to the preserved component instances, instead of Blazor selecting the mapping. +Initially, the `Expander` components behave independently when their `Expanded` properties are toggled. The child components maintain their states as expected. -### Values to use for `@key` +If <xref:Microsoft.AspNetCore.Components.ComponentBase.StateHasChanged%2A> is called in a parent component, the Blazor framework rerenders child components if their parameters might have changed: -Generally, it makes sense to supply one of the following values for [`@key`][5]: +* For a group of parameter types that Blazor explicitly checks, Blazor rerenders a child component if it detects that any of the parameters have changed. +* For unchecked parameter types, Blazor rerenders the child component *regardless of whether or not the parameters have changed*. Child content falls into this category of parameter types because child content is of type <xref:Microsoft.AspNetCore.Components.RenderFragment>, which is a delegate that refers to other mutable objects. -* Model object instances. For example, the `Person` instance (`person`) was used in the earlier example. This ensures preservation based on object reference equality. -* Unique identifiers. For example, unique identifiers can be based on primary key values of type `int`, `string`, or `Guid`. +For the `ExpanderExample` component: -Ensure that values used for [`@key`][5] don't clash. If clashing values are detected within the same parent element, Blazor throws an exception because it can't deterministically map old elements or components to new elements or components. Only use distinct values, such as object instances or primary key values. +* The first `Expander` component sets child content in a potentially mutable <xref:Microsoft.AspNetCore.Components.RenderFragment>, so a call to <xref:Microsoft.AspNetCore.Components.ComponentBase.StateHasChanged%2A> in the parent component automatically rerenders the component and potentially overwrites the value of `Expanded` to its initial value of `true`. +* The second `Expander` component doesn't set child content. Therefore, a potentially mutable <xref:Microsoft.AspNetCore.Components.RenderFragment> doesn't exist. A call to <xref:Microsoft.AspNetCore.Components.ComponentBase.StateHasChanged%2A> in the parent component doesn't automatically rerender the child component, so the component's `Expanded` value isn't overwritten. -## Apply an attribute +To maintain state in the preceding scenario, use a *private field* in the `Expander` component to maintain its toggled state. -Attributes can be applied to components with the [`@attribute`][7] directive. The following example applies the [`[Authorize]` attribute](xref:Microsoft.AspNetCore.Authorization.AuthorizeAttribute) to the component's class: +The following revised `Expander` component: -```razor -@page "/" -@attribute [Authorize] -``` +* Accepts the `Expanded` component parameter value from the parent. +* Assigns the component parameter value to a *private field* (`expanded`) in the [`OnInitialized` event](xref:blazor/components/lifecycle#component-initialization-oninitializedasync). +* Uses the private field to maintain its internal toggle state, which demonstrates how to avoid writing directly to a parameter. -## Conditional HTML element attributes +> [!NOTE] +> The advice in this section extends to similar logic in component parameter `set` accessors, which can result in similar undesirable side effects. -HTML element attribute properties are conditionally set based on the .NET value. If the value is `false` or `null`, the property isn't set. If the value is `true`, the property is set. +`Shared/Expander.razor`: -In the following example, `IsCompleted` determines if the `<input>` element's `checked` property is set. +:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Shared/index/Expander.razor"::: -`Pages/ConditionalAttribute.razor`: +For additional information, see [Blazor Two Way Binding Error (dotnet/aspnetcore #24599)](https://github.com/dotnet/aspnetcore/issues/24599). -:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Pages/index/ConditionalAttribute.razor"::: +For more information on change detection, including information on the exact types that Blazor checks, see <xref:blazor/components/rendering#rendering-conventions-for-componentbase>. -For more information, see <xref:mvc/views/razor>. +## Attribute splatting and arbitrary parameters -> [!WARNING] -> Some HTML attributes, such as [`aria-pressed`](https://developer.mozilla.org/docs/Web/Accessibility/ARIA/Roles/button_role#Toggle_buttons), don't function properly when the .NET type is a `bool`. In those cases, use a `string` type instead of a `bool`. +Components can capture and render additional attributes in addition to the component's declared parameters. Additional attributes can be captured in a dictionary and then *splatted* onto an element when the component is rendered using the [`@attributes`][3] Razor directive attribute. This scenario is useful for defining a component that produces a markup element that supports a variety of customizations. For example, it can be tedious to define attributes separately for an `<input>` that supports many parameters. -## Raw HTML +In the following `Splat` component: -Strings are normally rendered using DOM text nodes, which means that any markup they may contain is ignored and treated as literal text. To render raw HTML, wrap the HTML content in a <xref:Microsoft.AspNetCore.Components.MarkupString> value. The value is parsed as HTML or SVG and inserted into the DOM. +* The first `<input>` element (`id="useIndividualParams"`) uses individual component parameters. +* The second `<input>` element (`id="useAttributesDict"`) uses attribute splatting. -> [!WARNING] -> Rendering raw HTML constructed from any untrusted source is a **security risk** and should **always** be avoided. +`Pages/Splat.razor`: -The following example shows using the <xref:Microsoft.AspNetCore.Components.MarkupString> type to add a block of static HTML content to the rendered output of a component. +:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Pages/index/Splat.razor"::: -`Pages/MarkupStringExample.razor`: +The rendered `<input>` elements in the webpage are identical: -:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Pages/index/MarkupStringExample.razor"::: +```html +<input id="useIndividualParams" + maxlength="10" + placeholder="Input placeholder text" + required="required" + size="50"> -## Razor templates +<input id="useAttributesDict" + maxlength="10" + placeholder="Input placeholder text" + required="required" + size="50"> +``` -Render fragments can be defined using Razor template syntax to define a UI snippet. Razor templates use the following format: +To accept arbitrary attributes, define a [component parameter](#component-parameters) with the <xref:Microsoft.AspNetCore.Components.ParameterAttribute.CaptureUnmatchedValues> property set to `true`: ```razor -@<{HTML tag}>...</{HTML tag}> +@code { + [Parameter(CaptureUnmatchedValues = true)] + public Dictionary<string, object> InputAttributes { get; set; } +} ``` -The following example illustrates how to specify <xref:Microsoft.AspNetCore.Components.RenderFragment> and <xref:Microsoft.AspNetCore.Components.RenderFragment%601> values and render templates directly in a component. Render fragments can also be passed as arguments to [templated components](xref:blazor/components/templated-components). - -`Pages/RazorTemplate.razor`: +The <xref:Microsoft.AspNetCore.Components.ParameterAttribute.CaptureUnmatchedValues> property on [`[Parameter]`](xref:Microsoft.AspNetCore.Components.ParameterAttribute) allows the parameter to match all attributes that don't match any other parameter. A component can only define a single parameter with <xref:Microsoft.AspNetCore.Components.ParameterAttribute.CaptureUnmatchedValues>. The property type used with <xref:Microsoft.AspNetCore.Components.ParameterAttribute.CaptureUnmatchedValues> must be assignable from [`Dictionary<string, object>`](xref:System.Collections.Generic.Dictionary%602) with string keys. Use of [`IEnumerable<KeyValuePair<string, object>>`](xref:System.Collections.Generic.IEnumerable%601) or [`IReadOnlyDictionary<string, object>`](xref:System.Collections.Generic.IReadOnlyDictionary%602) are also options in this scenario. -:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Pages/index/RazorTemplate.razor"::: +The position of [`@attributes`][3] relative to the position of element attributes is important. When [`@attributes`][3] are splatted on the element, the attributes are processed from right to left (last to first). Consider the following example of a parent component that consumes a child component: -Rendered output of the preceding code: +`Shared/AttributeOrderChild1.razor`: -```html -<p>The time is 4/19/2021 8:54:46 AM.</p> -<p>Pet: Nutty Rex</p> -``` +:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Shared/index/AttributeOrderChild1.razor"::: -## Static assets +`Pages/AttributeOrderParent1.razor`: -Blazor follows the convention of ASP.NET Core apps for static assets. Static assets are located in the project's [`web root` (`wwwroot`) folder](xref:fundamentals/index#web-root) or folders under the `wwwroot` folder. +:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Pages/index/AttributeOrderParent1.razor"::: -Use a base-relative path (`/`) to refer to the web root for a static asset. In the following example, `logo.png` is physically located in the `{PROJECT ROOT}/wwwroot/images` folder. `{PROJECT ROOT}` is the app's project root. +The `AttributeOrderChild1` component's `extra` attribute is set to the right of [`@attributes`][3]. The `AttributeOrderParent1` component's rendered `<div>` contains `extra="5"` when passed through the additional attribute because the attributes are processed right to left (last to first): -```razor -<img alt="Company logo" src="/images/logo.png" /> +```html +<div extra="5" /> ``` -Components do **not** support tilde-slash notation (`~/`). +In the following example, the order of `extra` and [`@attributes`][3] is reversed in the child component's `<div>`: -For information on setting an app's base path, see <xref:blazor/host-and-deploy/index#app-base-path>. +`Shared/AttributeOrderChild2.razor`: -## Tag Helpers aren't supported in components +:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Shared/index/AttributeOrderChild2.razor"::: -[`Tag Helpers`](xref:mvc/views/tag-helpers/intro) aren't supported in components. To provide Tag Helper-like functionality in Blazor, create a component with the same functionality as the Tag Helper and use the component instead. +`Pages/AttributeOrderParent2.razor`: -## Scalable Vector Graphics (SVG) images +:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Pages/index/AttributeOrderParent2.razor"::: -Since Blazor renders HTML, browser-supported images, including [Scalable Vector Graphics (SVG) images (`.svg`)](https://developer.mozilla.org/docs/Web/SVG), are supported via the `<img>` tag: +The `<div>` in the parent component's rendered webpage contains `extra="10"` when passed through the additional attribute: ```html -<img alt="Example image" src="image.svg" /> +<div extra="10" /> ``` -Similarly, SVG images are supported in the CSS rules of a stylesheet file (`.css`): - -```css -.element-class { - background-image: url("image.svg"); -} -``` +## Capture references to components -## Whitespace rendering behavior +Component references provide a way to reference a component instance for issuing commands. To capture a component reference: -Whitespace is retained in a component's source markup. Whitespace-only text renders in the browser's DOM even when there's no visual effect. +* Add an [`@ref`][4] attribute to the child component. +* Define a field with the same type as the child component. -Consider the following component markup: +When the component is rendered, the field is populated with the component instance. You can then invoke .NET methods on the instance. -```razor -<ul> - @foreach (var item in Items) - { - <li> - @item.Text - </li> - } -</ul> -``` +Consider the following `ReferenceChild` component that logs a message when its `ChildMethod` is called. -The preceding example renders the following unnecessary whitespace: +`Shared/ReferenceChild.razor`: -* Outside of the `@foreach` code block. -* Around the `<li>` element. -* Around the `@item.Text` output. +:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Shared/index/ReferenceChild.razor"::: -A list of 100 items results in over 400 areas of whitespace. None of the extra whitespace visually affects the rendered output. +A component reference is only populated after the component is rendered and its output includes `ReferenceChild`'s element. Until the component is rendered, there's nothing to reference. -When rendering static HTML for components, whitespace inside a tag isn't preserved. For example, view the rendered output of the following `<img>` tag in a component Razor file (`.razor`): +To manipulate component references after the component has finished rendering, use the [`OnAfterRender` or `OnAfterRenderAsync` methods](xref:blazor/components/lifecycle#after-component-render-onafterrenderasync). -```razor -<img alt="Example image" src="img.png" /> -``` +To use a reference variable with an event handler, use a lambda expression or assign the event handler delegate in the [`OnAfterRender` or `OnAfterRenderAsync` methods](xref:blazor/components/lifecycle#after-component-render-onafterrenderasync). This ensures that the reference variable is assigned before the event handler is assigned. -Whitespace isn't preserved from the preceding markup: +The following lambda approach uses the preceding `ReferenceChild` component. -```razor -<img alt="Example image" src="img.png" /> -``` +`Pages/ReferenceParent1.razor`: -## Generic type parameter support +:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Pages/index/ReferenceParent1.razor"::: -The [`@typeparam`][11] directive declares a [generic type parameter](/dotnet/csharp/programming-guide/generics/generic-type-parameters) for the generated component class: +The following delegate approach uses the preceding `ReferenceChild` component. -```razor -@typeparam TItem -``` +`Pages/ReferenceParent2.razor`: -In the following example, the `ListGenericTypeItems1` component is generically typed as `TExample`. +:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Pages/index/ReferenceParent2.razor"::: -`Shared/ListGenericTypeItems1.razor`: +While capturing component references use a similar syntax to [capturing element references](xref:blazor/js-interop/call-javascript-from-dotnet#capture-references-to-elements), capturing component references isn't a JavaScript interop feature. Component references aren't passed to JavaScript code. Component references are only used in .NET code. -:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Shared/index/ListGenericTypeItems1.razor"::: +> [!IMPORTANT] +> Do **not** use component references to mutate the state of child components. Instead, use normal declarative component parameters to pass data to child components. Use of component parameters result in child components that rerender at the correct times automatically. For more information, see the [component parameters](#component-parameters) section and the <xref:blazor/components/data-binding> article. -The following `GenericTypeExample1` component renders two `ListGenericTypeItems1` components: +## Synchronization context -* String or integer data is assigned to the `ExampleList` parameter of each component. -* Type `string` or `int` that matches the type of the assigned data is set for the type parameter (`TExample`) of each component. +Blazor uses a synchronization context (<xref:System.Threading.SynchronizationContext>) to enforce a single logical thread of execution. A component's [lifecycle methods](xref:blazor/components/lifecycle) and event callbacks raised by Blazor are executed on the synchronization context. -`Pages/GenericTypeExample1.razor`: +Blazor Server's synchronization context attempts to emulate a single-threaded environment so that it closely matches the WebAssembly model in the browser, which is single threaded. At any given point in time, work is performed on exactly one thread, which yields the impression of a single logical thread. No two operations execute concurrently. -:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Pages/index/GenericTypeExample1.razor"::: +### Avoid thread-blocking calls -For more information, see the following articles: +Generally, don't call the following methods in components. The following methods block the execution thread and thus block the app from resuming work until the underlying <xref:System.Threading.Tasks.Task> is complete: -* <xref:mvc/views/razor#typeparam> -* <xref:blazor/components/templated-components> +* <xref:System.Threading.Tasks.Task%601.Result%2A> +* <xref:System.Threading.Tasks.Task.Wait%2A> +* <xref:System.Threading.Tasks.Task.WaitAny%2A> +* <xref:System.Threading.Tasks.Task.WaitAll%2A> +* <xref:System.Threading.Thread.Sleep%2A> +* <xref:System.Runtime.CompilerServices.TaskAwaiter.GetResult%2A> -:::moniker-end +> [!NOTE] +> Blazor documentation examples that use the thread-blocking methods mentioned in this section are only using the methods for demonstration purposes, not as recommended coding guidance. For example, a few component code demonstrations simulate a long-running process by calling <xref:System.Threading.Thread.Sleep%2A?displayProperty=nameWithType>. -:::moniker range=">= aspnetcore-7.0" +### Invoke component methods externally to update state -Blazor apps are built using *Razor components*, informally known as *Blazor components*. A component is a self-contained portion of user interface (UI) with processing logic to enable dynamic behavior. Components can be nested, reused, shared among projects, and [used in MVC and Razor Pages apps](xref:blazor/components/prerendering-and-integration). +In the event a component must be updated based on an external event, such as a timer or other notification, use the `InvokeAsync` method, which dispatches code execution to Blazor's synchronization context. For example, consider the following *notifier service* that can notify any listening component about updated state. The `Update` method can be called from anywhere in the app. -## Component classes +`TimerService.cs`: -Components are implemented using a combination of C# and HTML markup in [Razor](xref:mvc/views/razor) component files with the `.razor` file extension. +:::code language="csharp" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/TimerService.cs"::: -By default, <xref:Microsoft.AspNetCore.Components.ComponentBase> is the base class for components described by Razor component files. <xref:Microsoft.AspNetCore.Components.ComponentBase> implements the lowest abstraction of components, the <xref:Microsoft.AspNetCore.Components.IComponent> interface. <xref:Microsoft.AspNetCore.Components.ComponentBase> defines component properties and methods for basic functionality, for example, to process a set of built-in component lifecycle events. +`NotifierService.cs`: -[`ComponentBase` in `dotnet/aspnetcore` reference source](https://github.com/dotnet/aspnetcore/blob/main/src/Components/Components/src/ComponentBase.cs): The reference source contains additional remarks on the built-in lifecycle events. However, keep in mind that the internal implementations of component features are subject to change at any time without notice. +:::code language="csharp" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/NotifierService.cs"::: -[!INCLUDE[](~/includes/aspnetcore-repo-ref-source-links.md)] +Register the services: -Developers typically create Razor components from Razor component files (`.razor`) or base their components on <xref:Microsoft.AspNetCore.Components.ComponentBase>, but components can also be built by implementing <xref:Microsoft.AspNetCore.Components.IComponent>. Developer-built components that implement <xref:Microsoft.AspNetCore.Components.IComponent> can take low-level control over rendering at the cost of having to manually trigger rendering with events and lifecycle methods that the developer must create and maintain. +* In a Blazor WebAssembly app, register the services as singletons in `Program.cs`: -### Razor syntax + ```csharp + builder.Services.AddSingleton<NotifierService>(); + builder.Services.AddSingleton<TimerService>(); + ``` -Components use [Razor syntax](xref:mvc/views/razor). Two Razor features are extensively used by components, *directives* and *directive attributes*. These are reserved keywords prefixed with `@` that appear in Razor markup: +* In a Blazor Server app, register the services as scoped in `Startup.ConfigureServices`: -* [Directives](xref:mvc/views/razor#directives): Change the way component markup is parsed or functions. For example, the [`@page`][9] directive specifies a routable component with a route template and can be reached directly by a user's request in the browser at a specific URL. -* [Directive attributes](xref:mvc/views/razor#directive-attributes): Change the way a component element is parsed or functions. For example, the [`@bind`][10] directive attribute for an `<input>` element binds data to the element's value. + ```csharp + services.AddScoped<NotifierService>(); + services.AddScoped<TimerService>(); + ``` -Directives and directive attributes used in components are explained further in this article and other articles of the Blazor documentation set. For general information on Razor syntax, see <xref:mvc/views/razor>. +Use the `NotifierService` to update a component. -### Names +`Pages/ReceiveNotifications.razor`: -A component's name must start with an uppercase character: +:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Pages/index/ReceiveNotifications.razor"::: -* `ProductDetail.razor` is valid. -* `productDetail.razor` is invalid. +In the preceding example: -Common Blazor naming conventions used throughout the Blazor documentation include: +* `NotifierService` invokes the component's `OnNotify` method outside of Blazor's synchronization context. `InvokeAsync` is used to switch to the correct context and queue a render. For more information, see <xref:blazor/components/rendering>. +* The component implements <xref:System.IDisposable>. The `OnNotify` delegate is unsubscribed in the `Dispose` method, which is called by the framework when the component is disposed. For more information, see <xref:blazor/components/lifecycle#component-disposal-with-idisposable-and-iasyncdisposable>. -* Component file paths use Pascal case† and appear before showing component code examples. Paths indicate typical folder locations. For example, `Pages/ProductDetail.razor` indicates that the `ProductDetail` component has a file name of `ProductDetail.razor` and resides in the `Pages` folder of the app. -* Component file paths for routable components match their URLs with hyphens appearing for spaces between words in a component's route template. For example, a `ProductDetail` component with a route template of `/product-detail` (`@page "/product-detail"`) is requested in a browser at the relative URL `/product-detail`. - -†Pascal case (upper camel case) is a naming convention without spaces and punctuation and with the first letter of each word capitalized, including the first word. +> [!IMPORTANT] +> If a Razor component defines an event that's triggered from a background thread, the component might be required to capture and restore the execution context (<xref:System.Threading.ExecutionContext>) at the time the handler is registered. For more information, see [Calling `InvokeAsync(StateHasChanged)` causes page to fallback to default culture (dotnet/aspnetcore #28521)](https://github.com/dotnet/aspnetcore/issues/28521). -### Routing +## Use `@key` to control the preservation of elements and components -Routing in Blazor is achieved by providing a route template to each accessible component in the app with an [`@page`][9] directive. When a Razor file with an [`@page`][9] directive is compiled, the generated class is given a <xref:Microsoft.AspNetCore.Mvc.RouteAttribute> specifying the route template. At runtime, the router searches for component classes with a <xref:Microsoft.AspNetCore.Mvc.RouteAttribute> and renders whichever component has a route template that matches the requested URL. +When rendering a list of elements or components and the elements or components subsequently change, Blazor must decide which of the previous elements or components are retained and how model objects should map to them. Normally, this process is automatic and sufficient for general rendering, but there are often cases where controlling the process using the [`@key`][5] directive attribute is required. -The following `HelloWorld` component uses a route template of `/hello-world`, and the rendered webpage for the component is reached at the relative URL `/hello-world`. Components that produce webpages usually reside in the `Pages` folder, but you can use any folder to hold components, including within nested folders. +Consider the following example that demonstrates a collection mapping problem that's solved by using [`@key`][5]. -`Pages/HelloWorld.razor`: +For following `Details` and `People` components: -:::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Pages/index/HelloWorld.razor"::: +* The `Details` component receives data (`Data`) from the parent `People` component, which is displayed in an `<input>` element. Any given displayed `<input>` element can receive the focus of the page from the user when they select one of the `<input>` elements. +* The `People` component creates a list of person objects for display using the `Details` component. Every three seconds, a new person is added to the collection. -The preceding component loads in the browser at `/hello-world` regardless of whether or not you add the component to the app's UI navigation. Optionally, components can be added to the `NavMenu` component so that a link to the component appears in the app's UI-based navigation. +This demonstration allows you to: -For the preceding `HelloWorld` component, you can add a `NavLink` component to the `NavMenu` component in the `Shared` folder. For more information, including descriptions of the `NavLink` and `NavMenu` components, see <xref:blazor/fundamentals/routing>. +* Select an `<input>` from among several rendered `Details` components. +* Study the behavior of the page's focus as the people collection automatically grows. -### Markup +`Shared/Details.razor`: -A component's UI is defined using [Razor syntax](xref:mvc/views/razor), which consists of Razor markup, C#, and HTML. When an app is compiled, the HTML markup and C# rendering logic are converted into a component class. The name of the generated class matches the name of the file. +:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Shared/index/Details.razor"::: -Members of the component class are defined in one or more [`@code`][1] blocks. In [`@code`][1] blocks, component state is specified and processed with C#: +In the following `People` component, each iteration of adding a person in `OnTimerCallback` results in Blazor rebuilding the entire collection. The page's focus remains on the *same index* position of `<input>` elements, so the focus shifts each time a person is added. *Shifting the focus away from what the user selected isn't desirable behavior.* After demonstrating the poor behavior with the following component, the [`@key`][5] directive attribute is used to improve the user's experience. -* Property and field initializers. -* Parameter values from arguments passed by parent components and route parameters. -* Methods for user event handling, lifecycle events, and custom component logic. +`Pages/People.razor`: -Component members are used in rendering logic using C# expressions that start with the `@` symbol. For example, a C# field is rendered by prefixing `@` to the field name. The following `Markup` component evaluates and renders: +:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Pages/index/People.razor"::: -* `headingFontStyle` for the CSS property value `font-style` of the heading element. -* `headingText` for the content of the heading element. +The contents of the `people` collection changes with inserted, deleted, or re-ordered entries. Rerendering can lead to visible behavior differences. For example, each time a person is inserted into the `people` collection, the user's focus is lost. -`Pages/Markup.razor`: +The mapping process of elements or components to a collection can be controlled with the [`@key`][5] directive attribute. Use of [`@key`][5] guarantees the preservation of elements or components based on the key's value. If the `Details` component in the preceding example is keyed on the `person` item, Blazor ignores rerendering `Details` components that haven't changed. -:::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Pages/index/Markup.razor"::: +To modify the `People` component to use the [`@key`][5] directive attribute with the `people` collection, update the `<Details>` element to the following: -> [!NOTE] -> Examples throughout the Blazor documentation specify the [`private` access modifier](/dotnet/csharp/language-reference/keywords/private) for private members. Private members are scoped to a component's class. However, C# assumes the `private` access modifier when no access modifier is present, so explicitly marking members "`private`" in your own code is optional. For more information on access modifiers, see [Access Modifiers (C# Programming Guide)](/dotnet/csharp/programming-guide/classes-and-structs/access-modifiers). +```razor +<Details @key="person" Data="@person.Data" /> +``` -The Blazor framework processes a component internally as a [*render tree*](https://developer.mozilla.org/docs/Web/Performance/How_browsers_work#render), which is the combination of a component's [Document Object Model (DOM)](https://developer.mozilla.org/docs/Web/API/Document_Object_Model/Introduction) and [Cascading Style Sheet Object Model (CSSOM)](https://developer.mozilla.org/docs/Web/API/CSS_Object_Model). After the component is initially rendered, the component's render tree is regenerated in response to events. Blazor compares the new render tree against the previous render tree and applies any modifications to the browser's DOM for display. For more information, see <xref:blazor/components/rendering>. +When the `people` collection changes, the association between `Details` instances and `person` instances is retained. When a `Person` is inserted at the beginning of the collection, one new `Details` instance is inserted at that corresponding position. Other instances are left unchanged. Therefore, the user's focus isn't lost as people are added to the collection. -Components are ordinary [C# classes](/dotnet/csharp/programming-guide/classes-and-structs/classes) and can be placed anywhere within a project. Components that produce webpages usually reside in the `Pages` folder. Non-page components are frequently placed in the `Shared` folder or a custom folder added to the project. +Other collection updates exhibit the same behavior when the [`@key`][5] directive attribute is used: -Razor syntax for C# control structures, directives, and directive attributes are lowercase (examples: [`@if`](xref:mvc/views/razor#conditionals-if-else-if-else-and-switch), [`@code`](xref:mvc/views/razor#code), [`@bind`](xref:mvc/views/razor#bind)). Property names are uppercase (example: `@Body` for <xref:Microsoft.AspNetCore.Components.LayoutComponentBase.Body?displayProperty=nameWithType>). +* If an instance is deleted from the collection, only the corresponding component instance is removed from the UI. Other instances are left unchanged. +* If collection entries are re-ordered, the corresponding component instances are preserved and re-ordered in the UI. -### Asynchronous methods (`async`) don't support returning `void` +> [!IMPORTANT] +> Keys are local to each container element or component. Keys aren't compared globally across the document. -The Blazor framework doesn't track `void`-returning asynchronous methods (`async`). As a result, exceptions aren't caught if `void` is returned. Always return a <xref:System.Threading.Tasks.Task> from asynchronous methods. +### When to use `@key` -### Nested components +Typically, it makes sense to use [`@key`][5] whenever a list is rendered (for example, in a [`foreach`](/dotnet/csharp/language-reference/keywords/foreach-in) block) and a suitable value exists to define the [`@key`][5]. -Components can include other components by declaring them using HTML syntax. The markup for using a component looks like an HTML tag where the name of the tag is the component type. +You can also use [`@key`][5] to preserve an element or component subtree when an object doesn't change, as the following examples show. -Consider the following `Heading` component, which can be used by other components to display a heading. +Example 1: -`Shared/Heading.razor`: +```razor +<li @key="person"> + <input value="@person.Data" /> +</li> +``` -:::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Shared/index/Heading.razor"::: +Example 2: -The following markup in the `HeadingExample` component renders the preceding `Heading` component at the location where the `<Heading />` tag appears. +```razor +<div @key="person"> + @* other HTML elements *@ +</div> +``` -`Pages/HeadingExample.razor`: +If an `person` instance changes, the [`@key`][5] attribute directive forces Blazor to: -:::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Pages/index/HeadingExample.razor"::: +* Discard the entire `<li>` or `<div>` and their descendants. +* Rebuild the subtree within the UI with new elements and components. -If a component contains an HTML element with an uppercase first letter that doesn't match a component name within the same namespace, a warning is emitted indicating that the element has an unexpected name. Adding an [`@using`][2] directive for the component's namespace makes the component available, which resolves the warning. For more information, see the [Namespaces](#namespaces) section. +This is useful to guarantee that no UI state is preserved when the collection changes within a subtree. -The `Heading` component example shown in this section doesn't have an [`@page`][9] directive, so the `Heading` component isn't directly accessible to a user via a direct request in the browser. However, any component with an [`@page`][9] directive can be nested in another component. If the `Heading` component was directly accessible by including `@page "/heading"` at the top of its Razor file, then the component would be rendered for browser requests at both `/heading` and `/heading-example`. +### Scope of `@key` -### Namespaces +The [`@key`][5] attribute directive is scoped to its own siblings within its parent. -Typically, a component's namespace is derived from the app's root namespace and the component's location (folder) within the app. If the app's root namespace is `BlazorSample` and the `Counter` component resides in the `Pages` folder: +Consider the following example. The `first` and `second` keys are compared against each other within the same scope of the outer `<div>` element: -* The `Counter` component's namespace is `BlazorSample.Pages`. -* The fully qualified type name of the component is `BlazorSample.Pages.Counter`. +```razor +<div> + <div @key="first">...</div> + <div @key="second">...</div> +</div> +``` -For custom folders that hold components, add an [`@using`][2] directive to the parent component or to the app's `_Imports.razor` file. The following example makes components in the `Components` folder available: +The following example demonstrates `first` and `second` keys in their own scopes, unrelated to each other and without influence on each other. Each [`@key`][5] scope only applies to its parent `<div>` element, not across the parent `<div>` elements: ```razor -@using BlazorSample.Components +<div> + <div @key="first">...</div> +</div> +<div> + <div @key="second">...</div> +</div> ``` -> [!NOTE] -> [`@using`][2] directives in the `_Imports.razor` file are only applied to Razor files (`.razor`), not C# files (`.cs`). +For the `Details` component shown earlier, the following examples render `person` data within the same [`@key`][5] scope and demonstrate typical use cases for [`@key`][5]: -Components can also be referenced using their fully qualified names, which doesn't require an [`@using`][2] directive. The following example directly references the `ProductDetail` component in the `Components` folder of the app: +```razor +<div> + @foreach (var person in people) + { + <Details @key="person" Data="@person.Data" /> + } +</div> +``` ```razor -<BlazorSample.Components.ProductDetail /> +@foreach (var person in people) +{ + <div @key="person"> + <Details Data="@person.Data" /> + </div> +} ``` -The namespace of a component authored with Razor is based on the following (in priority order): +```razor +<ol> + @foreach (var person in people) + { + <li @key="person"> + <Details Data="@person.Data" /> + </li> + } +</ol> +``` -* The [`@namespace`][8] directive in the Razor file's markup (for example, `@namespace BlazorSample.CustomNamespace`). -* The project's `RootNamespace` in the project file (for example, `<RootNamespace>BlazorSample</RootNamespace>`). -* The project name, taken from the project file's file name (`.csproj`), and the path from the project root to the component. For example, the framework resolves `{PROJECT ROOT}/Pages/Index.razor` with a project namespace of `BlazorSample` (`BlazorSample.csproj`) to the namespace `BlazorSample.Pages` for the `Index` component. `{PROJECT ROOT}` is the project root path. Components follow C# name binding rules. For the `Index` component in this example, the components in scope are all of the components: - * In the same folder, `Pages`. - * The components in the project's root that don't explicitly specify a different namespace. +The following examples only scope [`@key`][5] to the `<div>` or `<li>` element that surrounds each `Details` component instance. Therefore, `person` data for each member of the `people` collection is **not** keyed on each `person` instance across the rendered `Details` components. Avoid the following patterns when using [`@key`][5]: -The following are **not** supported: +```razor +@foreach (var person in people) +{ + <div> + <Details @key="person" Data="@person.Data" /> + </div> +} +``` -* The [`global::`](/dotnet/csharp/language-reference/operators/namespace-alias-qualifier) qualification. -* Importing components with aliased [`using`](/dotnet/csharp/language-reference/keywords/using-statement) statements. For example, `@using Foo = Bar` isn't supported. -* Partially-qualified names. For example, you can't add `@using BlazorSample` to a component and then reference the `NavMenu` component in the app's `Shared` folder (`Shared/NavMenu.razor`) with `<Shared.NavMenu></Shared.NavMenu>`. +```razor +<ol> + @foreach (var person in people) + { + <li> + <Details @key="person" Data="@person.Data" /> + </li> + } +</ol> +``` -### Partial class support +### When not to use `@key` -Components are generated as [C# partial classes](/dotnet/csharp/programming-guide/classes-and-structs/partial-classes-and-methods) and are authored using either of the following approaches: +There's a performance cost when rendering with [`@key`][5]. The performance cost isn't large, but only specify [`@key`][5] if preserving the element or component benefits the app. -* A single file contains C# code defined in one or more [`@code`][1] blocks, HTML markup, and Razor markup. Blazor project templates define their components using this single-file approach. -* HTML and Razor markup are placed in a Razor file (`.razor`). C# code is placed in a code-behind file defined as a partial class (`.cs`). +Even if [`@key`][5] isn't used, Blazor preserves child element and component instances as much as possible. The only advantage to using [`@key`][5] is control over *how* model instances are mapped to the preserved component instances, instead of Blazor selecting the mapping. -> [!NOTE] -> A component stylesheet that defines component-specific styles is a separate file (`.css`). Blazor CSS isolation is described later in <xref:blazor/components/css-isolation>. +### Values to use for `@key` -The following example shows the default `Counter` component with an [`@code`][1] block in an app generated from a Blazor project template. Markup and C# code are in the same file. This is the most common approach taken in component authoring. +Generally, it makes sense to supply one of the following values for [`@key`][5]: -`Pages/Counter.razor`: +* Model object instances. For example, the `Person` instance (`person`) was used in the earlier example. This ensures preservation based on object reference equality. +* Unique identifiers. For example, unique identifiers can be based on primary key values of type `int`, `string`, or `Guid`. -:::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Pages/Counter.razor"::: +Ensure that values used for [`@key`][5] don't clash. If clashing values are detected within the same parent element, Blazor throws an exception because it can't deterministically map old elements or components to new elements or components. Only use distinct values, such as object instances or primary key values. -The following `Counter` component splits HTML and Razor markup from C# code using a code-behind file with a partial class: +## Apply an attribute -`Pages/CounterPartialClass.razor`: +Attributes can be applied to components with the [`@attribute`][7] directive. The following example applies the [`[Authorize]` attribute](xref:Microsoft.AspNetCore.Authorization.AuthorizeAttribute) to the component's class: -:::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Pages/index/CounterPartialClass.razor"::: +```razor +@page "/" +@attribute [Authorize] +``` -`Pages/CounterPartialClass.razor.cs`: +## Conditional HTML element attributes -```csharp -namespace BlazorSample.Pages -{ - public partial class CounterPartialClass - { - private int currentCount = 0; +HTML element attribute properties are conditionally set based on the .NET value. If the value is `false` or `null`, the property isn't set. If the value is `true`, the property is set. - void IncrementCount() - { - currentCount++; - } - } -} -``` +In the following example, `IsCompleted` determines if the `<input>` element's `checked` property is set. -[`@using`][2] directives in the `_Imports.razor` file are only applied to Razor files (`.razor`), not C# files (`.cs`). Add namespaces to a partial class file as needed. +`Pages/ConditionalAttribute.razor`: -Typical namespaces used by components: +:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Pages/index/ConditionalAttribute.razor"::: -```csharp -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; -``` +For more information, see <xref:mvc/views/razor>. -Typical namespaces also include the namespace of the app and the namespace corresponding to the app's `Shared` folder: +> [!WARNING] +> Some HTML attributes, such as [`aria-pressed`](https://developer.mozilla.org/docs/Web/Accessibility/ARIA/Roles/button_role#Toggle_buttons), don't function properly when the .NET type is a `bool`. In those cases, use a `string` type instead of a `bool`. -```csharp -using BlazorSample; -using BlazorSample.Shared; -``` +## Raw HTML -### Specify a base class +Strings are normally rendered using DOM text nodes, which means that any markup they may contain is ignored and treated as literal text. To render raw HTML, wrap the HTML content in a <xref:Microsoft.AspNetCore.Components.MarkupString> value. The value is parsed as HTML or SVG and inserted into the DOM. -The [`@inherits`][6] directive is used to specify a base class for a component. The following example shows how a component can inherit a base class to provide the component's properties and methods. The `BlazorRocksBase` base class derives from <xref:Microsoft.AspNetCore.Components.ComponentBase>. +> [!WARNING] +> Rendering raw HTML constructed from any untrusted source is a **security risk** and should **always** be avoided. -`Pages/BlazorRocks.razor`: +The following example shows using the <xref:Microsoft.AspNetCore.Components.MarkupString> type to add a block of static HTML content to the rendered output of a component. -:::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Pages/index/BlazorRocks.razor"::: +`Pages/MarkupStringExample.razor`: -`BlazorRocksBase.cs`: +:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Pages/index/MarkupStringExample.razor"::: -:::code language="csharp" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/BlazorRocksBase.cs"::: +## Razor templates -## Component parameters +Render fragments can be defined using Razor template syntax to define a UI snippet. Razor templates use the following format: -*Component parameters* pass data to components and are defined using public [C# properties](/dotnet/csharp/programming-guide/classes-and-structs/properties) on the component class with the [`[Parameter]` attribute](xref:Microsoft.AspNetCore.Components.ParameterAttribute). In the following example, a built-in reference type (<xref:System.String?displayProperty=fullName>) and a user-defined reference type (`PanelBody`) are passed as component parameters. +```razor +@<{HTML tag}>...</{HTML tag}> +``` -`PanelBody.cs`: +The following example illustrates how to specify <xref:Microsoft.AspNetCore.Components.RenderFragment> and <xref:Microsoft.AspNetCore.Components.RenderFragment%601> values and render templates directly in a component. Render fragments can also be passed as arguments to [templated components](xref:blazor/components/templated-components). -:::code language="csharp" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/PanelBody.cs"::: +`Pages/RazorTemplate.razor`: -`Shared/ParameterChild.razor`: - -:::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Shared/index/ParameterChild.razor"::: +:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Pages/index/RazorTemplate.razor"::: -> [!WARNING] -> Providing initial values for component parameters is supported, but don't create a component that writes to its own parameters after the component is rendered for the first time. For more information, see the [Overwritten parameters](#overwritten-parameters) section of this article. +Rendered output of the preceding code: -The `Title` and `Body` component parameters of the `ParameterChild` component are set by arguments in the HTML tag that renders the instance of the component. The following `ParameterParent` component renders two `ParameterChild` components: +```html +<p>The time is 4/19/2021 8:54:46 AM.</p> +<p>Pet: Nutty Rex</p> +``` -* The first `ParameterChild` component is rendered without supplying parameter arguments. -* The second `ParameterChild` component receives values for `Title` and `Body` from the `ParameterParent` component, which uses an [explicit C# expression](xref:mvc/views/razor#explicit-razor-expressions) to set the values of the `PanelBody`'s properties. +## Static assets -`Pages/ParameterParent.razor`: +Blazor follows the convention of ASP.NET Core apps for static assets. Static assets are located in the project's [`web root` (`wwwroot`) folder](xref:fundamentals/index#web-root) or folders under the `wwwroot` folder. -:::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Pages/index/ParameterParent.razor"::: +Use a base-relative path (`/`) to refer to the web root for a static asset. In the following example, `logo.png` is physically located in the `{PROJECT ROOT}/wwwroot/images` folder. `{PROJECT ROOT}` is the app's project root. -The following rendered HTML markup from the `ParameterParent` component shows `ParameterChild` component default values when the `ParameterParent` component doesn't supply component parameter values. When the `ParameterParent` component provides component parameter values, they replace the `ParameterChild` component's default values. +```razor +<img alt="Company logo" src="/images/logo.png" /> +``` -> [!NOTE] -> For clarity, rendered CSS style classes aren't shown in the following rendered HTML markup. +Components do **not** support tilde-slash notation (`~/`). -```html -<h1>Child component (without attribute values)</h1> +For information on setting an app's base path, see <xref:blazor/host-and-deploy/index#app-base-path>. -<div> - <div>Set By Child</div> - <div>Set by child.</div> -</div> +## Tag Helpers aren't supported in components -<h1>Child component (with attribute values)</h1> +[`Tag Helpers`](xref:mvc/views/tag-helpers/intro) aren't supported in components. To provide Tag Helper-like functionality in Blazor, create a component with the same functionality as the Tag Helper and use the component instead. -<div> - <div>Set by Parent</div> - <div>Set by parent.</div> -</div> -``` +## Scalable Vector Graphics (SVG) images -Assign a C# field, property, or result of a method to a component parameter as an HTML attribute value using [Razor's reserved `@` symbol](xref:mvc/views/razor#razor-syntax). The following `ParameterParent2` component displays four instances of the preceding `ParameterChild` component and sets their `Title` parameter values to: +Since Blazor renders HTML, browser-supported images, including [Scalable Vector Graphics (SVG) images (`.svg`)](https://developer.mozilla.org/docs/Web/SVG), are supported via the `<img>` tag: -* The value of the `title` field. -* The result of the `GetTitle` C# method. -* The current local date in long format with <xref:System.DateTime.ToLongDateString%2A>, which uses an [implicit C# expression](xref:mvc/views/razor#implicit-razor-expressions). -* The `panelData` object's `Title` property. +```html +<img alt="Example image" src="image.svg" /> +``` -The `@` prefix is required for string parameters. Otherwise, the framework assumes that a string literal is set. +Similarly, SVG images are supported in the CSS rules of a stylesheet file (`.css`): -Outside of string parameters, we recommend use the use of the `@` prefix for nonliterals, even when they aren't strictly required. +```css +.element-class { + background-image: url("image.svg"); +} +``` -We don't recommend the use of the `@` prefix for literals (for example, boolean values), keywords (for example, `this`), or `null`, but you can choose to use them if you wish. For example, `IsFixed="@true"` is uncommon but supported. +## Whitespace rendering behavior -Quotes around parameter attribute values are optional in most cases per the HTML5 specification. For example, `Value=this` is supported, instead of `Value="this"`. However, we recommend using quotes because it's easier to remember and widely adopted across web-based technologies. +Unless the [`@preservewhitespace`](xref:mvc/views/razor#preservewhitespace) directive is used with a value of `true`, extra whitespace is removed by default if: -Throughout the documentation, code examples: +* Leading or trailing within an element. +* Leading or trailing within a <xref:Microsoft.AspNetCore.Components.RenderFragment>/<xref:Microsoft.AspNetCore.Components.RenderFragment%601> parameter (for example, child content passed to another component). +* It precedes or follows a C# code block, such as `@if` or `@foreach`. -* Always use quotes. Example: `Value="this"`. -* Nonliterals always use the `@` prefix, even when it's optional. Examples: `Title="@title"`, where `title` is a string-typed variable. `Count="@ct"`, where `ct` is a number-typed variable. -* Literals, outside of Razor expressions, always avoid `@`. Example: `IsFixed="true"`. +Whitespace removal might affect the rendered output when using a CSS rule, such as `white-space: pre`. To disable this performance optimization and preserve the whitespace, take one of the following actions: -`Pages/ParameterParent2.razor`: +* Add the `@preservewhitespace true` directive at the top of the Razor file (`.razor`) to apply the preference to a specific component. +* Add the `@preservewhitespace true` directive inside an `_Imports.razor` file to apply the preference to a subdirectory or to the entire project. -:::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Pages/index/ParameterParent2.razor"::: +In most cases, no action is required, as apps typically continue to behave normally (but faster). If stripping whitespace causes a rendering problem for a particular component, use `@preservewhitespace true` in that component to disable this optimization. -> [!NOTE] -> When assigning a C# member to a component parameter, prefix the member with the `@` symbol and never prefix the parameter's HTML attribute. -> -> Correct: -> -> ```razor -> <ParameterChild Title="@title" /> -> ``` -> -> Incorrect: -> -> ```razor -> <ParameterChild @Title="title" /> -> ``` +## Generic type parameter support -Unlike in Razor pages (`.cshtml`), Blazor can't perform asynchronous work in a Razor expression while rendering a component. This is because Blazor is designed for rendering interactive UIs. In an interactive UI, the screen must always display something, so it doesn't make sense to block the rendering flow. Instead, asynchronous work is performed during one of the [asynchronous lifecycle events](xref:blazor/components/lifecycle). After each asynchronous lifecycle event, the component may render again. The following Razor syntax is **not** supported: +The [`@typeparam`][11] directive declares a [generic type parameter](/dotnet/csharp/programming-guide/generics/generic-type-parameters) for the generated component class: ```razor -<ParameterChild Title="@await ..." /> +@typeparam TItem ``` -The code in the preceding example generates a *compiler error* when the app is built: +In the following example, the `ListGenericTypeItems1` component is generically typed as `TExample`. -> The 'await' operator can only be used within an async method. Consider marking this method with the 'async' modifier and changing its return type to 'Task'. +`Shared/ListGenericTypeItems1.razor`: -To obtain a value for the `Title` parameter in the preceding example asynchronously, the component can use the [`OnInitializedAsync` lifecycle event](xref:blazor/components/lifecycle#component-initialization-oninitializedasync), as the following example demonstrates: +:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Shared/index/ListGenericTypeItems1.razor"::: -```razor -<ParameterChild Title="@title" /> +The following `GenericTypeExample1` component renders two `ListGenericTypeItems1` components: -@code { - private string? title; - - protected override async Task OnInitializedAsync() - { - title = await ...; - } -} -``` +* String or integer data is assigned to the `ExampleList` parameter of each component. +* Type `string` or `int` that matches the type of the assigned data is set for the type parameter (`TExample`) of each component. -For more information, see <xref:blazor/components/lifecycle>. +`Pages/GenericTypeExample1.razor`: -Use of an explicit Razor expression to concatenate text with an expression result for assignment to a parameter is **not** supported. The following example seeks to concatenate the text "`Set by `" with an object's property value. Although this syntax is supported in a Razor page (`.cshtml`), it isn't valid for assignment to the child's `Title` parameter in a component. The following Razor syntax is **not** supported: +:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Pages/index/GenericTypeExample1.razor"::: -```razor -<ParameterChild Title="Set by @(panelData.Title)" /> -``` +For more information, see the following articles: -The code in the preceding example generates a *compiler error* when the app is built: +* <xref:mvc/views/razor#typeparam> +* <xref:blazor/components/templated-components> -> Component attributes do not support complex content (mixed C# and markup). +:::moniker-end -To support the assignment of a composed value, use a method, field, or property. The following example performs the concatenation of "`Set by `" and an object's property value in the C# method `GetTitle`: +:::moniker range="< aspnetcore-5.0" -`Pages/ParameterParent3.razor`: +Blazor apps are built using *Razor components*, informally known as *Blazor components*. A component is a self-contained portion of user interface (UI) with processing logic to enable dynamic behavior. Components can be nested, reused, shared among projects, and [used in MVC and Razor Pages apps](xref:blazor/components/prerendering-and-integration). -:::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Pages/index/ParameterParent3.razor"::: +## Component classes -For more information, see <xref:mvc/views/razor>. +Components are implemented using a combination of C# and HTML markup in [Razor](xref:mvc/views/razor) component files with the `.razor` file extension. -> [!WARNING] -> Providing initial values for component parameters is supported, but don't create a component that writes to its own parameters after the component is rendered for the first time. For more information, see the [Overwritten parameters](#overwritten-parameters) section of this article. +By default, <xref:Microsoft.AspNetCore.Components.ComponentBase> is the base class for components described by Razor component files. <xref:Microsoft.AspNetCore.Components.ComponentBase> implements the lowest abstraction of components, the <xref:Microsoft.AspNetCore.Components.IComponent> interface. <xref:Microsoft.AspNetCore.Components.ComponentBase> defines component properties and methods for basic functionality, for example, to process a set of built-in component lifecycle events. -Component parameters should be declared as *auto-properties*, meaning that they shouldn't contain custom logic in their `get` or `set` accessors. For example, the following `StartData` property is an auto-property: +[`ComponentBase` in `dotnet/aspnetcore` reference source](https://github.com/dotnet/aspnetcore/blob/main/src/Components/Components/src/ComponentBase.cs): The reference source contains additional remarks on the built-in lifecycle events. However, keep in mind that the internal implementations of component features are subject to change at any time without notice. -```csharp -[Parameter] -public DateTime StartData { get; set; } -``` +[!INCLUDE[](~/includes/aspnetcore-repo-ref-source-links.md)] -Don't place custom logic in the `get` or `set` accessor because component parameters are purely intended for use as a channel for a parent component to flow information to a child component. If a `set` accessor of a child component property contains logic that causes rerendering of the parent component, an infinite rendering loop results. +Developers typically create Razor components from Razor component files (`.razor`) or base their components on <xref:Microsoft.AspNetCore.Components.ComponentBase>, but components can also be built by implementing <xref:Microsoft.AspNetCore.Components.IComponent>. Developer-built components that implement <xref:Microsoft.AspNetCore.Components.IComponent> can take low-level control over rendering at the cost of having to manually trigger rendering with events and lifecycle methods that the developer must create and maintain. -To transform a received parameter value: +### Razor syntax -* Leave the parameter property as an auto-property to represent the supplied raw data. -* Create a different property or method to supply the transformed data based on the parameter property. +Components use [Razor syntax](xref:mvc/views/razor). Two Razor features are extensively used by components, *directives* and *directive attributes*. These are reserved keywords prefixed with `@` that appear in Razor markup: -Override [`OnParametersSetAsync`](xref:blazor/components/lifecycle#after-parameters-are-set-onparameterssetasync) to transform a received parameter each time new data is received. +* [Directives](xref:mvc/views/razor#directives): Change the way component markup is parsed or functions. For example, the [`@page`][9] directive specifies a routable component with a route template and can be reached directly by a user's request in the browser at a specific URL. +* [Directive attributes](xref:mvc/views/razor#directive-attributes): Change the way a component element is parsed or functions. For example, the [`@bind`][10] directive attribute for an `<input>` element binds data to the element's value. -Writing an initial value to a component parameter is supported because initial value assignments don't interfere with the Blazor's automatic component rendering. The following assignment of the current local <xref:System.DateTime> with <xref:System.DateTime.Now?displayProperty=nameWithType> to `StartData` is valid syntax in a component: +Directives and directive attributes used in components are explained further in this article and other articles of the Blazor documentation set. For general information on Razor syntax, see <xref:mvc/views/razor>. -```csharp -[Parameter] -public DateTime StartData { get; set; } = DateTime.Now; -``` +### Names -After the initial assignment of <xref:System.DateTime.Now?displayProperty=nameWithType>, do **not** assign a value to `StartData` in developer code. For more information, see the [Overwritten parameters](#overwritten-parameters) section of this article. +A component's name must start with an uppercase character: -Apply the [`[EditorRequired]` attribute](xref:Microsoft.AspNetCore.Components.EditorRequiredAttribute) to specify a required component parameter. If a parameter value isn't provided, editors or build tools may display warnings to the user. This attribute is only valid on properties also marked with the [`[Parameter]` attribute](xref:Microsoft.AspNetCore.Components.ParameterAttribute). The <xref:Microsoft.AspNetCore.Components.EditorRequiredAttribute> is enforced at design-time and when the app is built. The attribute isn't enforced at runtime, and it doesn't guarantee a non-`null` parameter value. +* `ProductDetail.razor` is valid. +* `productDetail.razor` is invalid. -```csharp -[Parameter] -[EditorRequired] -public string? Title { get; set; } -``` +Common Blazor naming conventions used throughout the Blazor documentation include: -Single-line attribute lists are also supported: +* Component file paths use Pascal case† and appear before showing component code examples. Paths indicate typical folder locations. For example, `Pages/ProductDetail.razor` indicates that the `ProductDetail` component has a file name of `ProductDetail.razor` and resides in the `Pages` folder of the app. +* Component file paths for routable components match their URLs with hyphens appearing for spaces between words in a component's route template. For example, a `ProductDetail` component with a route template of `/product-detail` (`@page "/product-detail"`) is requested in a browser at the relative URL `/product-detail`. -```csharp -[Parameter, EditorRequired] -public string? Title { get; set; } -``` +†Pascal case (upper camel case) is a naming convention without spaces and punctuation and with the first letter of each word capitalized, including the first word. -[`Tuples`](/dotnet/csharp/language-reference/builtin-types/value-tuples) ([API documentation](xref:System.Tuple)) are supported for component parameters and [`RenderFragment`](#child-content-render-fragments) types. The following component parameter example passes three values in a `Tuple`: +### Routing -`Shared/RenderTupleChild.razor`: +Routing in Blazor is achieved by providing a route template to each accessible component in the app with an [`@page`][9] directive. When a Razor file with an [`@page`][9] directive is compiled, the generated class is given a <xref:Microsoft.AspNetCore.Mvc.RouteAttribute> specifying the route template. At runtime, the router searches for component classes with a <xref:Microsoft.AspNetCore.Mvc.RouteAttribute> and renders whichever component has a route template that matches the requested URL. -```csharp -<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> +The following `HelloWorld` component uses a route template of `/hello-world`. The rendered webpage for the component is reached at the relative URL `/hello-world`. When running a Blazor app locally with the default protocol, host, and port, the `HelloWorld` component is requested in the browser at `https://localhost:5001/hello-world`. Components that produce webpages usually reside in the `Pages` folder, but you can use any folder to hold components, including within nested folders. -@code { - [Parameter] - public Tuple<int, string, bool>? Data { get; set; } -} -``` +`Pages/HelloWorld.razor`: -`Pages/RenderTupleParent.razor`: +:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Pages/index/HelloWorld.razor"::: -```csharp -@page "/render-tuple-parent" +The preceding component loads in the browser at `/hello-world` regardless of whether or not you add the component to the app's UI navigation. Optionally, components can be added to the `NavMenu` component so that a link to the component appears in the app's UI-based navigation. -<h1>Render <code>Tuple</code> Parent</h1> +For the preceding `HelloWorld` component, you can add a `NavLink` component to the `NavMenu` component in the `Shared` folder. For more information, including descriptions of the `NavLink` and `NavMenu` components, see <xref:blazor/fundamentals/routing>. -<RenderTupleChild Data="@data" /> +### Markup -@code { - private Tuple<int, string, bool> data = new(999, "I aim to misbehave.", true); -} -``` - -Only ***unnamed tuples*** are supported for C# 7.0 or later in Razor components. [Named tuples](/dotnet/csharp/language-reference/builtin-types/value-tuples#tuple-field-names) support in Razor components is planned for a future ASP.NET Core release. For more information, see [Blazor Transpiler issue with named Tuples (dotnet/aspnetcore #28982)](https://github.com/dotnet/aspnetcore/issues/28982). +A component's UI is defined using [Razor syntax](xref:mvc/views/razor), which consists of Razor markup, C#, and HTML. When an app is compiled, the HTML markup and C# rendering logic are converted into a component class. The name of the generated class matches the name of the file. -Quote ©2005 [Universal Pictures](https://www.uphe.com): [Serenity](https://www.uphe.com/movies/serenity-2005) ([Nathan Fillion](https://www.imdb.com/name/nm0277213/)) +Members of the component class are defined in one or more [`@code`][1] blocks. In [`@code`][1] blocks, component state is specified and processed with C#: -## Route parameters +* Property and field initializers. +* Parameter values from arguments passed by parent components and route parameters. +* Methods for user event handling, lifecycle events, and custom component logic. -Components can specify route parameters in the route template of the [`@page`][9] directive. The [Blazor router](xref:blazor/fundamentals/routing) uses route parameters to populate corresponding component parameters. +Component members are used in rendering logic using C# expressions that start with the `@` symbol. For example, a C# field is rendered by prefixing `@` to the field name. The following `Markup` component evaluates and renders: -Optional route parameters are supported. In the following example, the `text` optional parameter assigns the value of the route segment to the component's `Text` property. If the segment isn't present, the value of `Text` is set to "`fantastic`" in the [`OnInitialized` lifecycle method](xref:blazor/components/lifecycle#component-initialization-oninitializedasync). +* `headingFontStyle` for the CSS property value `font-style` of the heading element. +* `headingText` for the content of the heading element. -`Pages/RouteParameter.razor`: +`Pages/Markup.razor`: -:::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Pages/index/RouteParameter.razor" highlight="1,6-7"::: +:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Pages/index/Markup.razor"::: -For information on catch-all route parameters (`{*pageRoute}`), which capture paths across multiple folder boundaries, see <xref:blazor/fundamentals/routing#catch-all-route-parameters>. +> [!NOTE] +> Examples throughout the Blazor documentation specify the [`private` access modifier](/dotnet/csharp/language-reference/keywords/private) for private members. Private members are scoped to a component's class. However, C# assumes the `private` access modifier when no access modifier is present, so explicitly marking members "`private`" in your own code is optional. For more information on access modifiers, see [Access Modifiers (C# Programming Guide)](/dotnet/csharp/programming-guide/classes-and-structs/access-modifiers). -## Child content render fragments +The Blazor framework processes a component internally as a [*render tree*](https://developer.mozilla.org/docs/Web/Performance/How_browsers_work#render), which is the combination of a component's [Document Object Model (DOM)](https://developer.mozilla.org/docs/Web/API/Document_Object_Model/Introduction) and [Cascading Style Sheet Object Model (CSSOM)](https://developer.mozilla.org/docs/Web/API/CSS_Object_Model). After the component is initially rendered, the component's render tree is regenerated in response to events. Blazor compares the new render tree against the previous render tree and applies any modifications to the browser's DOM for display. For more information, see <xref:blazor/components/rendering>. -Components can set the content of another component. The assigning component provides the content between the child component's opening and closing tags. +Components are ordinary [C# classes](/dotnet/csharp/programming-guide/classes-and-structs/classes) and can be placed anywhere within a project. Components that produce webpages usually reside in the `Pages` folder. Non-page components are frequently placed in the `Shared` folder or a custom folder added to the project. -In the following example, the `RenderFragmentChild` component has a `ChildContent` component parameter that represents a segment of the UI to render as a <xref:Microsoft.AspNetCore.Components.RenderFragment>. The position of `ChildContent` in the component's Razor markup is where the content is rendered in the final HTML output. +Razor syntax for C# control structures, directives, and directive attributes are lowercase (examples: [`@if`](xref:mvc/views/razor#conditionals-if-else-if-else-and-switch), [`@code`](xref:mvc/views/razor#code), [`@bind`](xref:mvc/views/razor#bind)). Property names are uppercase (example: `@Body` for <xref:Microsoft.AspNetCore.Components.LayoutComponentBase.Body?displayProperty=nameWithType>). -`Shared/RenderFragmentChild.razor`: +### Asynchronous methods (`async`) don't support returning `void` -:::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Shared/index/RenderFragmentChild.razor"::: +The Blazor framework doesn't track `void`-returning asynchronous methods (`async`). As a result, exceptions aren't caught if `void` is returned. Always return a <xref:System.Threading.Tasks.Task> from asynchronous methods. -> [!IMPORTANT] -> The property receiving the <xref:Microsoft.AspNetCore.Components.RenderFragment> content must be named `ChildContent` by convention. -> -> [Event callbacks](xref:blazor/components/event-handling#eventcallback) aren't supported for <xref:Microsoft.AspNetCore.Components.RenderFragment>. +### Nested components -The following `RenderFragmentParent` component provides content for rendering the `RenderFragmentChild` by placing the content inside the child component's opening and closing tags. +Components can include other components by declaring them using HTML syntax. The markup for using a component looks like an HTML tag where the name of the tag is the component type. -`Pages/RenderFragmentParent.razor`: +Consider the following `Heading` component, which can be used by other components to display a heading. -:::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Pages/index/RenderFragmentParent.razor"::: - -Due to the way that Blazor renders child content, rendering components inside a [`for`](/dotnet/csharp/language-reference/keywords/for) loop requires a local index variable if the incrementing loop variable is used in the `RenderFragmentChild` component's content. The following example can be added to the preceding `RenderFragmentParent` component: - -```razor -<h1>Three children with an index variable</h1> +`Shared/Heading.razor`: -@for (int c = 0; c < 3; c++) -{ - var current = c; +:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Shared/index/Heading.razor"::: - <RenderFragmentChild> - Count: @current - </RenderFragmentChild> -} -``` +The following markup in the `HeadingExample` component renders the preceding `Heading` component at the location where the `<Heading />` tag appears. -Alternatively, use a [`foreach`](/dotnet/csharp/language-reference/keywords/foreach-in) loop with <xref:System.Linq.Enumerable.Range%2A?displayProperty=nameWithType> instead of a [`for`](/dotnet/csharp/language-reference/keywords/for) loop. The following example can be added to the preceding `RenderFragmentParent` component: +`Pages/HeadingExample.razor`: -```razor -<h1>Second example of three children with an index variable</h1> +:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Pages/index/HeadingExample.razor"::: -@foreach (var c in Enumerable.Range(0,3)) -{ - <RenderFragmentChild> - Count: @c - </RenderFragmentChild> -} -``` +If a component contains an HTML element with an uppercase first letter that doesn't match a component name within the same namespace, a warning is emitted indicating that the element has an unexpected name. Adding an [`@using`][2] directive for the component's namespace makes the component available, which resolves the warning. For more information, see the [Namespaces](#namespaces) section. -Render fragments are used to render child content throughout Blazor apps and are described with examples in the following articles and article sections: +The `Heading` component example shown in this section doesn't have an [`@page`][9] directive, so the `Heading` component isn't directly accessible to a user via a direct request in the browser. However, any component with an [`@page`][9] directive can be nested in another component. If the `Heading` component was directly accessible by including `@page "/heading"` at the top of its Razor file, then the component would be rendered for browser requests at both `/heading` and `/heading-example`. -* [Blazor layouts](xref:blazor/components/layouts) -* [Pass data across a component hierarchy](xref:blazor/components/cascading-values-and-parameters#pass-data-across-a-component-hierarchy) -* [Templated components](xref:blazor/components/templated-components) -* [Global exception handling](xref:blazor/fundamentals/handle-errors#global-exception-handling) +### Namespaces -> [!NOTE] -> Blazor framework's [built-in Razor components](xref:blazor/components/built-in-components) use the same `ChildContent` component parameter convention to set their content. You can see the components that set child content by searching for the component parameter property name `ChildContent` in the [API documentation (filters API with the search term "ChildContent")](/dotnet/api/?term=ChildContent). +Typically, a component's namespace is derived from the app's root namespace and the component's location (folder) within the app. If the app's root namespace is `BlazorSample` and the `Counter` component resides in the `Pages` folder: -## Render fragments for reusable rendering logic +* The `Counter` component's namespace is `BlazorSample.Pages`. +* The fully qualified type name of the component is `BlazorSample.Pages.Counter`. -You can factor out child components purely as a way of reusing rendering logic. In any component's `@code` block, define a <xref:Microsoft.AspNetCore.Components.RenderFragment> and render the fragment from any location as many times as needed: +For custom folders that hold components, add an [`@using`][2] directive to the parent component or to the app's `_Imports.razor` file. The following example makes components in the `Components` folder available: ```razor -<h1>Hello, world!</h1> - -@RenderWelcomeInfo +@using BlazorSample.Components +``` -<p>Render the welcome info a second time:</p> +> [!NOTE] +> [`@using`][2] directives in the `_Imports.razor` file are only applied to Razor files (`.razor`), not C# files (`.cs`). -@RenderWelcomeInfo +Components can also be referenced using their fully qualified names, which doesn't require an [`@using`][2] directive. The following example directly references the `ProductDetail` component in the `Components` folder of the app: -@code { - private RenderFragment RenderWelcomeInfo = __builder => - { - <p>Welcome to your new app!</p> - }; -} +```razor +<BlazorSample.Components.ProductDetail /> ``` -For more information, see [Reuse rendering logic](xref:blazor/performance#define-reusable-renderfragments-in-code). - -## Overwritten parameters - -The Blazor framework generally imposes safe parent-to-child parameter assignment: +The namespace of a component authored with Razor is based on the following (in priority order): -* Parameters aren't overwritten unexpectedly. -* Side effects are minimized. For example, additional renders are avoided because they may create infinite rendering loops. +* The [`@namespace`][8] directive in the Razor file's markup (for example, `@namespace BlazorSample.CustomNamespace`). +* The project's `RootNamespace` in the project file (for example, `<RootNamespace>BlazorSample</RootNamespace>`). +* The project name, taken from the project file's file name (`.csproj`), and the path from the project root to the component. For example, the framework resolves `{PROJECT ROOT}/Pages/Index.razor` with a project namespace of `BlazorSample` (`BlazorSample.csproj`) to the namespace `BlazorSample.Pages` for the `Index` component. `{PROJECT ROOT}` is the project root path. Components follow C# name binding rules. For the `Index` component in this example, the components in scope are all of the components: + * In the same folder, `Pages`. + * The components in the project's root that don't explicitly specify a different namespace. -A child component receives new parameter values that possibly overwrite existing values when the parent component rerenders. Accidentally overwriting parameter values in a child component often occurs when developing the component with one or more data-bound parameters and the developer writes directly to a parameter in the child: +The following are **not** supported: -* The child component is rendered with one or more parameter values from the parent component. -* The child writes directly to the value of a parameter. -* The parent component rerenders and overwrites the value of the child's parameter. +* The [`global::`](/dotnet/csharp/language-reference/operators/namespace-alias-qualifier) qualification. +* Importing components with aliased [`using`](/dotnet/csharp/language-reference/keywords/using-statement) statements. For example, `@using Foo = Bar` isn't supported. +* Partially-qualified names. For example, you can't add `@using BlazorSample` to a component and then reference the `NavMenu` component in the app's `Shared` folder (`Shared/NavMenu.razor`) with `<Shared.NavMenu></Shared.NavMenu>`. -The potential for overwriting parameter values extends into the child component's property `set` accessors, too. +### Partial class support -> [!IMPORTANT] -> Our general guidance is not to create components that directly write to their own parameters after the component is rendered for the first time. +Components are generated as [C# partial classes](/dotnet/csharp/programming-guide/classes-and-structs/partial-classes-and-methods) and are authored using either of the following approaches: -Consider the following `Expander` component that: +* A single file contains C# code defined in one or more [`@code`][1] blocks, HTML markup, and Razor markup. Blazor project templates define their components using this single-file approach. +* HTML and Razor markup are placed in a Razor file (`.razor`). C# code is placed in a code-behind file defined as a partial class (`.cs`). -* Renders child content. -* Toggles showing child content with a component parameter (`Expanded`). +The following example shows the default `Counter` component with an [`@code`][1] block in an app generated from a Blazor project template. Markup and C# code are in the same file. This is the most common approach taken in component authoring. -After the following `Expander` component demonstrates an overwritten parameter, a modified `Expander` component is shown to demonstrate the correct approach for this scenario. The following examples can be placed in a local sample app to experience the behaviors described. +`Pages/Counter.razor`: -`Shared/Expander.razor`: +:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Pages/Counter.razor"::: -:::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Shared/index/BadExpander.razor"::: +The following `Counter` component splits HTML and Razor markup from C# code using a code-behind file with a partial class: -The `Expander` component is added to the following `ExpanderExample` parent component that may call <xref:Microsoft.AspNetCore.Components.ComponentBase.StateHasChanged%2A>: +`Pages/CounterPartialClass.razor`: -* Calling <xref:Microsoft.AspNetCore.Components.ComponentBase.StateHasChanged%2A> in developer code notifies a component that its state has changed and typically triggers component rerendering to update the UI. <xref:Microsoft.AspNetCore.Components.ComponentBase.StateHasChanged%2A> is covered in more detail later in <xref:blazor/components/lifecycle> and <xref:blazor/components/rendering>. -* The button's `@onclick` directive attribute attaches an event handler to the button's `onclick` event. Event handling is covered in more detail later in <xref:blazor/components/event-handling>. +```razor +@page "/counter-partial-class" -`Pages/ExpanderExample.razor`: +<h1>Counter</h1> -:::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Pages/index/ExpanderExample.razor"::: +<p>Current count: @currentCount</p> -Initially, the `Expander` components behave independently when their `Expanded` properties are toggled. The child components maintain their states as expected. +<button class="btn btn-primary" @onclick="IncrementCount">Click me</button> +``` -If <xref:Microsoft.AspNetCore.Components.ComponentBase.StateHasChanged%2A> is called in a parent component, the Blazor framework rerenders child components if their parameters might have changed: +`Pages/CounterPartialClass.razor.cs`: -* For a group of parameter types that Blazor explicitly checks, Blazor rerenders a child component if it detects that any of the parameters have changed. -* For unchecked parameter types, Blazor rerenders the child component *regardless of whether or not the parameters have changed*. Child content falls into this category of parameter types because child content is of type <xref:Microsoft.AspNetCore.Components.RenderFragment>, which is a delegate that refers to other mutable objects. +```csharp +namespace BlazorSample.Pages +{ + public partial class CounterPartialClass + { + private int currentCount = 0; -For the `ExpanderExample` component: + void IncrementCount() + { + currentCount++; + } + } +} +``` -* The first `Expander` component sets child content in a potentially mutable <xref:Microsoft.AspNetCore.Components.RenderFragment>, so a call to <xref:Microsoft.AspNetCore.Components.ComponentBase.StateHasChanged%2A> in the parent component automatically rerenders the component and potentially overwrites the value of `Expanded` to its initial value of `true`. -* The second `Expander` component doesn't set child content. Therefore, a potentially mutable <xref:Microsoft.AspNetCore.Components.RenderFragment> doesn't exist. A call to <xref:Microsoft.AspNetCore.Components.ComponentBase.StateHasChanged%2A> in the parent component doesn't automatically rerender the child component, so the component's `Expanded` value isn't overwritten. +[`@using`][2] directives in the `_Imports.razor` file are only applied to Razor files (`.razor`), not C# files (`.cs`). Add namespaces to a partial class file as needed. -To maintain state in the preceding scenario, use a *private field* in the `Expander` component to maintain its toggled state. +Typical namespaces used by components: -The following revised `Expander` component: +```csharp +using System.Net.Http; +using Microsoft.AspNetCore.Components.Forms; +using Microsoft.AspNetCore.Components.Routing; +using Microsoft.AspNetCore.Components.Web; +using Microsoft.JSInterop; +``` -* Accepts the `Expanded` component parameter value from the parent. -* Assigns the component parameter value to a *private field* (`expanded`) in the [`OnInitialized` event](xref:blazor/components/lifecycle#component-initialization-oninitializedasync). -* Uses the private field to maintain its internal toggle state, which demonstrates how to avoid writing directly to a parameter. +Typical namespaces also include the namespace of the app and the namespace corresponding to the app's `Shared` folder: -> [!NOTE] -> The advice in this section extends to similar logic in component parameter `set` accessors, which can result in similar undesirable side effects. +```csharp +using BlazorSample; +using BlazorSample.Shared; +``` -`Shared/Expander.razor`: +### Specify a base class -:::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Shared/index/Expander.razor"::: +The [`@inherits`][6] directive is used to specify a base class for a component. The following example shows how a component can inherit a base class to provide the component's properties and methods. The `BlazorRocksBase` base class derives from <xref:Microsoft.AspNetCore.Components.ComponentBase>. -For two-way parent-child binding examples, see <xref:blazor/components/data-binding#binding-with-component-parameters>. For additional information, see [Blazor Two Way Binding Error (dotnet/aspnetcore #24599)](https://github.com/dotnet/aspnetcore/issues/24599). +`Pages/BlazorRocks.razor`: -For more information on change detection, including information on the exact types that Blazor checks, see <xref:blazor/components/rendering#rendering-conventions-for-componentbase>. +:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Pages/index/BlazorRocks.razor"::: -## Attribute splatting and arbitrary parameters +`BlazorRocksBase.cs`: -Components can capture and render additional attributes in addition to the component's declared parameters. Additional attributes can be captured in a dictionary and then *splatted* onto an element when the component is rendered using the [`@attributes`][3] Razor directive attribute. This scenario is useful for defining a component that produces a markup element that supports a variety of customizations. For example, it can be tedious to define attributes separately for an `<input>` that supports many parameters. +:::code language="csharp" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/BlazorRocksBase.cs"::: -In the following `Splat` component: +## Component parameters -* The first `<input>` element (`id="useIndividualParams"`) uses individual component parameters. -* The second `<input>` element (`id="useAttributesDict"`) uses attribute splatting. +*Component parameters* pass data to components and are defined using public [C# properties](/dotnet/csharp/programming-guide/classes-and-structs/properties) on the component class with the [`[Parameter]` attribute](xref:Microsoft.AspNetCore.Components.ParameterAttribute). In the following example, a built-in reference type (<xref:System.String?displayProperty=fullName>) and a user-defined reference type (`PanelBody`) are passed as component parameters. -`Pages/Splat.razor`: +`PanelBody.cs`: -:::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Pages/index/Splat.razor"::: +:::code language="csharp" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/PanelBody.cs"::: -The rendered `<input>` elements in the webpage are identical: +`Shared/ParameterChild.razor`: -```html -<input id="useIndividualParams" - maxlength="10" - placeholder="Input placeholder text" - required="required" - size="50"> +:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Shared/index/ParameterChild.razor"::: -<input id="useAttributesDict" - maxlength="10" - placeholder="Input placeholder text" - required="required" - size="50"> -``` +> [!WARNING] +> Providing initial values for component parameters is supported, but don't create a component that writes to its own parameters after the component is rendered for the first time. For more information, see the [Overwritten parameters](#overwritten-parameters) section of this article. -To accept arbitrary attributes, define a [component parameter](#component-parameters) with the <xref:Microsoft.AspNetCore.Components.ParameterAttribute.CaptureUnmatchedValues> property set to `true`: +The `Title` and `Body` component parameters of the `ParameterChild` component are set by arguments in the HTML tag that renders the instance of the component. The following `ParameterParent` component renders two `ParameterChild` components: -```razor -@code { - [Parameter(CaptureUnmatchedValues = true)] - public Dictionary<string, object>? InputAttributes { get; set; } -} -``` +* The first `ParameterChild` component is rendered without supplying parameter arguments. +* The second `ParameterChild` component receives values for `Title` and `Body` from the `ParameterParent` component, which uses an [explicit C# expression](xref:mvc/views/razor#explicit-razor-expressions) to set the values of the `PanelBody`'s properties. -The <xref:Microsoft.AspNetCore.Components.ParameterAttribute.CaptureUnmatchedValues> property on [`[Parameter]`](xref:Microsoft.AspNetCore.Components.ParameterAttribute) allows the parameter to match all attributes that don't match any other parameter. A component can only define a single parameter with <xref:Microsoft.AspNetCore.Components.ParameterAttribute.CaptureUnmatchedValues>. The property type used with <xref:Microsoft.AspNetCore.Components.ParameterAttribute.CaptureUnmatchedValues> must be assignable from [`Dictionary<string, object>`](xref:System.Collections.Generic.Dictionary%602) with string keys. Use of [`IEnumerable<KeyValuePair<string, object>>`](xref:System.Collections.Generic.IEnumerable%601) or [`IReadOnlyDictionary<string, object>`](xref:System.Collections.Generic.IReadOnlyDictionary%602) are also options in this scenario. +`Pages/ParameterParent.razor`: -The position of [`@attributes`][3] relative to the position of element attributes is important. When [`@attributes`][3] are splatted on the element, the attributes are processed from right to left (last to first). Consider the following example of a parent component that consumes a child component: +:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Pages/index/ParameterParent.razor"::: -`Shared/AttributeOrderChild1.razor`: +The following rendered HTML markup from the `ParameterParent` component shows `ParameterChild` component default values when the `ParameterParent` component doesn't supply component parameter values. When the `ParameterParent` component provides component parameter values, they replace the `ParameterChild` component's default values. -:::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Shared/index/AttributeOrderChild1.razor"::: +> [!NOTE] +> For clarity, rendered CSS style classes aren't shown in the following rendered HTML markup. -`Pages/AttributeOrderParent1.razor`: +```html +<h1>Child component (without attribute values)</h1> -:::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Pages/index/AttributeOrderParent1.razor"::: +<div> + <div>Set By Child</div> + <div>Set by child.</div> +</div> -The `AttributeOrderChild1` component's `extra` attribute is set to the right of [`@attributes`][3]. The `AttributeOrderParent1` component's rendered `<div>` contains `extra="5"` when passed through the additional attribute because the attributes are processed right to left (last to first): +<h1>Child component (with attribute values)</h1> -```html -<div extra="5" /> +<div> + <div>Set by Parent</div> + <div>Set by parent.</div> +</div> ``` -In the following example, the order of `extra` and [`@attributes`][3] is reversed in the child component's `<div>`: +Assign a C# field, property, or result of a method to a component parameter as an HTML attribute value using [Razor's reserved `@` symbol](xref:mvc/views/razor#razor-syntax). The following `ParameterParent2` component displays four instances of the preceding `ParameterChild` component and sets their `Title` parameter values to: -`Shared/AttributeOrderChild2.razor`: +* The value of the `title` field. +* The result of the `GetTitle` C# method. +* The current local date in long format with <xref:System.DateTime.ToLongDateString%2A>, which uses an [implicit C# expression](xref:mvc/views/razor#implicit-razor-expressions). +* The `panelData` object's `Title` property. -:::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Shared/index/AttributeOrderChild2.razor"::: +The `@` prefix is required for string parameters. Otherwise, the framework assumes that a string literal is set. -`Pages/AttributeOrderParent2.razor`: +Outside of string parameters, we recommend use the use of the `@` prefix for nonliterals, even when they aren't strictly required. -:::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Pages/index/AttributeOrderParent2.razor"::: +We don't recommend the use of the `@` prefix for literals (for example, boolean values), keywords (for example, `this`), or `null`, but you can choose to use them if you wish. For example, `IsFixed="@true"` is uncommon but supported. -The `<div>` in the parent component's rendered webpage contains `extra="10"` when passed through the additional attribute: +Quotes around parameter attribute values are optional in most cases per the HTML5 specification. For example, `Value=this` is supported, instead of `Value="this"`. However, we recommend using quotes because it's easier to remember and widely adopted across web-based technologies. -```html -<div extra="10" /> -``` +Throughout the documentation, code examples: -## Capture references to components +* Always use quotes. Example: `Value="this"`. +* Nonliterals always use the `@` prefix, even when it's optional. Examples: `Title="@title"`, where `title` is a string-typed variable. `Count="@ct"`, where `ct` is a number-typed variable. +* Literals, outside of Razor expressions, always avoid `@`. Example: `IsFixed="true"`. -Component references provide a way to reference a component instance for issuing commands. To capture a component reference: +`Pages/ParameterParent2.razor`: -* Add an [`@ref`][4] attribute to the child component. -* Define a field with the same type as the child component. +:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Pages/index/ParameterParent2.razor"::: -When the component is rendered, the field is populated with the component instance. You can then invoke .NET methods on the instance. +> [!NOTE] +> When assigning a C# member to a component parameter, prefix the member with the `@` symbol and never prefix the parameter's HTML attribute. +> +> Correct: +> +> ```razor +> <ParameterChild Title="@title" /> +> ``` +> +> Incorrect: +> +> ```razor +> <ParameterChild @Title="title" /> +> ``` -Consider the following `ReferenceChild` component that logs a message when its `ChildMethod` is called. +Unlike in Razor pages (`.cshtml`), Blazor can't perform asynchronous work in a Razor expression while rendering a component. This is because Blazor is designed for rendering interactive UIs. In an interactive UI, the screen must always display something, so it doesn't make sense to block the rendering flow. Instead, asynchronous work is performed during one of the [asynchronous lifecycle events](xref:blazor/components/lifecycle). After each asynchronous lifecycle event, the component may render again. The following Razor syntax is **not** supported: -`Shared/ReferenceChild.razor`: +```razor +<ParameterChild Title="@await ..." /> +``` -:::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Shared/index/ReferenceChild.razor"::: +The code in the preceding example generates a *compiler error* when the app is built: -A component reference is only populated after the component is rendered and its output includes `ReferenceChild`'s element. Until the component is rendered, there's nothing to reference. +> The 'await' operator can only be used within an async method. Consider marking this method with the 'async' modifier and changing its return type to 'Task'. -To manipulate component references after the component has finished rendering, use the [`OnAfterRender` or `OnAfterRenderAsync` methods](xref:blazor/components/lifecycle#after-component-render-onafterrenderasync). +To obtain a value for the `Title` parameter in the preceding example asynchronously, the component can use the [`OnInitializedAsync` lifecycle event](xref:blazor/components/lifecycle#component-initialization-oninitializedasync), as the following example demonstrates: -To use a reference variable with an event handler, use a lambda expression or assign the event handler delegate in the [`OnAfterRender` or `OnAfterRenderAsync` methods](xref:blazor/components/lifecycle#after-component-render-onafterrenderasync). This ensures that the reference variable is assigned before the event handler is assigned. +```razor +<ParameterChild Title="@title" /> -The following lambda approach uses the preceding `ReferenceChild` component. +@code { + private string title; + + protected override async Task OnInitializedAsync() + { + title = await ...; + } +} +``` -`Pages/ReferenceParent1.razor`: +For more information, see <xref:blazor/components/lifecycle>. -:::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Pages/index/ReferenceParent1.razor"::: +Use of an explicit Razor expression to concatenate text with an expression result for assignment to a parameter is **not** supported. The following example seeks to concatenate the text "`Set by `" with an object's property value. Although this syntax is supported in a Razor page (`.cshtml`), it isn't valid for assignment to the child's `Title` parameter in a component. The following Razor syntax is **not** supported: -The following delegate approach uses the preceding `ReferenceChild` component. +```razor +<ParameterChild Title="Set by @(panelData.Title)" /> +``` -`Pages/ReferenceParent2.razor`: +The code in the preceding example generates a *compiler error* when the app is built: -:::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Pages/index/ReferenceParent2.razor"::: +> Component attributes do not support complex content (mixed C# and markup). -While capturing component references use a similar syntax to [capturing element references](xref:blazor/js-interop/call-javascript-from-dotnet#capture-references-to-elements), capturing component references isn't a JavaScript interop feature. Component references aren't passed to JavaScript code. Component references are only used in .NET code. +To support the assignment of a composed value, use a method, field, or property. The following example performs the concatenation of "`Set by `" and an object's property value in the C# method `GetTitle`: -> [!IMPORTANT] -> Do **not** use component references to mutate the state of child components. Instead, use normal declarative component parameters to pass data to child components. Use of component parameters result in child components that rerender at the correct times automatically. For more information, see the [component parameters](#component-parameters) section and the <xref:blazor/components/data-binding> article. +`Pages/ParameterParent3.razor`: -## Synchronization context +:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Pages/index/ParameterParent3.razor"::: -Blazor uses a synchronization context (<xref:System.Threading.SynchronizationContext>) to enforce a single logical thread of execution. A component's [lifecycle methods](xref:blazor/components/lifecycle) and event callbacks raised by Blazor are executed on the synchronization context. +For more information, see <xref:mvc/views/razor>. -Blazor Server's synchronization context attempts to emulate a single-threaded environment so that it closely matches the WebAssembly model in the browser, which is single threaded. At any given point in time, work is performed on exactly one thread, which yields the impression of a single logical thread. No two operations execute concurrently. +> [!WARNING] +> Providing initial values for component parameters is supported, but don't create a component that writes to its own parameters after the component is rendered for the first time. For more information, see the [Overwritten parameters](#overwritten-parameters) section of this article. -### Avoid thread-blocking calls +Component parameters should be declared as *auto-properties*, meaning that they shouldn't contain custom logic in their `get` or `set` accessors. For example, the following `StartData` property is an auto-property: -Generally, don't call the following methods in components. The following methods block the execution thread and thus block the app from resuming work until the underlying <xref:System.Threading.Tasks.Task> is complete: +```csharp +[Parameter] +public DateTime StartData { get; set; } +``` -* <xref:System.Threading.Tasks.Task%601.Result%2A> -* <xref:System.Threading.Tasks.Task.Wait%2A> -* <xref:System.Threading.Tasks.Task.WaitAny%2A> -* <xref:System.Threading.Tasks.Task.WaitAll%2A> -* <xref:System.Threading.Thread.Sleep%2A> -* <xref:System.Runtime.CompilerServices.TaskAwaiter.GetResult%2A> +Don't place custom logic in the `get` or `set` accessor because component parameters are purely intended for use as a channel for a parent component to flow information to a child component. If a `set` accessor of a child component property contains logic that causes rerendering of the parent component, an infinite rendering loop results. -> [!NOTE] -> Blazor documentation examples that use the thread-blocking methods mentioned in this section are only using the methods for demonstration purposes, not as recommended coding guidance. For example, a few component code demonstrations simulate a long-running process by calling <xref:System.Threading.Thread.Sleep%2A?displayProperty=nameWithType>. +To transform a received parameter value: -### Invoke component methods externally to update state +* Leave the parameter property as an auto-property to represent the supplied raw data. +* Create a different property or method to supply the transformed data based on the parameter property. -In the event a component must be updated based on an external event, such as a timer or other notification, use the `InvokeAsync` method, which dispatches code execution to Blazor's synchronization context. For example, consider the following *notifier service* that can notify any listening component about updated state. The `Update` method can be called from anywhere in the app. +Override [`OnParametersSetAsync`](xref:blazor/components/lifecycle#after-parameters-are-set-onparameterssetasync) to transform a received parameter each time new data is received. -`TimerService.cs`: +Writing an initial value to a component parameter is supported because initial value assignments don't interfere with the Blazor's automatic component rendering. The following assignment of the current local <xref:System.DateTime> with <xref:System.DateTime.Now?displayProperty=nameWithType> to `StartData` is valid syntax in a component: -:::code language="csharp" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/TimerService.cs"::: +```csharp +[Parameter] +public DateTime StartData { get; set; } = DateTime.Now; +``` -`NotifierService.cs`: +After the initial assignment of <xref:System.DateTime.Now?displayProperty=nameWithType>, do **not** assign a value to `StartData` in developer code. For more information, see the [Overwritten parameters](#overwritten-parameters) section of this article. -:::code language="csharp" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/NotifierService.cs"::: +## Route parameters -Register the services: +Components can specify route parameters in the route template of the [`@page`][9] directive. The [Blazor router](xref:blazor/fundamentals/routing) uses route parameters to populate corresponding component parameters. -* In a Blazor WebAssembly app, register the services as singletons in `Program.cs`: +`Pages/RouteParameter.razor`: - ```csharp - builder.Services.AddSingleton<NotifierService>(); - builder.Services.AddSingleton<TimerService>(); - ``` +:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Pages/index/RouteParameter.razor" highlight="2,7-8"::: -* In a Blazor Server app, register the services as scoped in `Program.cs`: +Optional route parameters aren't supported, so two [`@page`][9] directives are applied in the preceding example. The first [`@page`][9] directive permits navigation to the component without a route parameter. The second [`@page`][9] directive receives the `{text}` route parameter and assigns the value to the `Text` property. - ```csharp - builder.Services.AddScoped<NotifierService>(); - builder.Services.AddScoped<TimerService>(); - ``` +For information on catch-all route parameters (`{*pageRoute}`), which capture paths across multiple folder boundaries, see <xref:blazor/fundamentals/routing#catch-all-route-parameters>. -Use the `NotifierService` to update a component. +## Child content render fragments -`Pages/ReceiveNotifications.razor`: +Components can set the content of another component. The assigning component provides the content between the child component's opening and closing tags. -:::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Pages/index/ReceiveNotifications.razor"::: +In the following example, the `RenderFragmentChild` component has a `ChildContent` component parameter that represents a segment of the UI to render as a <xref:Microsoft.AspNetCore.Components.RenderFragment>. The position of `ChildContent` in the component's Razor markup is where the content is rendered in the final HTML output. -In the preceding example: +`Shared/RenderFragmentChild.razor`: -* `NotifierService` invokes the component's `OnNotify` method outside of Blazor's synchronization context. `InvokeAsync` is used to switch to the correct context and queue a render. For more information, see <xref:blazor/components/rendering>. -* The component implements <xref:System.IDisposable>. The `OnNotify` delegate is unsubscribed in the `Dispose` method, which is called by the framework when the component is disposed. For more information, see <xref:blazor/components/lifecycle#component-disposal-with-idisposable-and-iasyncdisposable>. +:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Shared/index/RenderFragmentChild.razor"::: > [!IMPORTANT] -> If a Razor component defines an event that's triggered from a background thread, the component might be required to capture and restore the execution context (<xref:System.Threading.ExecutionContext>) at the time the handler is registered. For more information, see [Calling `InvokeAsync(StateHasChanged)` causes page to fallback to default culture (dotnet/aspnetcore #28521)](https://github.com/dotnet/aspnetcore/issues/28521). +> The property receiving the <xref:Microsoft.AspNetCore.Components.RenderFragment> content must be named `ChildContent` by convention. +> +> [Event callbacks](xref:blazor/components/event-handling#eventcallback) aren't supported for <xref:Microsoft.AspNetCore.Components.RenderFragment>. -## Use `@key` to control the preservation of elements and components +The following `RenderFragmentParent` component provides content for rendering the `RenderFragmentChild` by placing the content inside the child component's opening and closing tags. -When rendering a list of elements or components and the elements or components subsequently change, Blazor must decide which of the previous elements or components are retained and how model objects should map to them. Normally, this process is automatic and sufficient for general rendering, but there are often cases where controlling the process using the [`@key`][5] directive attribute is required. +`Pages/RenderFragmentParent.razor`: -Consider the following example that demonstrates a collection mapping problem that's solved by using [`@key`][5]. +:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Pages/index/RenderFragmentParent.razor"::: -For the following `Details` and `People` components: +Due to the way that Blazor renders child content, rendering components inside a [`for`](/dotnet/csharp/language-reference/keywords/for) loop requires a local index variable if the incrementing loop variable is used in the `RenderFragmentChild` component's content. The following example can be added to the preceding `RenderFragmentParent` component: -* The `Details` component receives data (`Data`) from the parent `People` component, which is displayed in an `<input>` element. Any given displayed `<input>` element can receive the focus of the page from the user when they select one of the `<input>` elements. -* The `People` component creates a list of person objects for display using the `Details` component. Every three seconds, a new person is added to the collection. +```razor +<h1>Three children with an index variable</h1> -This demonstration allows you to: +@for (int c = 0; c < 3; c++) +{ + var current = c; -* Select an `<input>` from among several rendered `Details` components. -* Study the behavior of the page's focus as the people collection automatically grows. + <RenderFragmentChild> + Count: @current + </RenderFragmentChild> +} +``` -`Shared/Details.razor`: +Alternatively, use a [`foreach`](/dotnet/csharp/language-reference/keywords/foreach-in) loop with <xref:System.Linq.Enumerable.Range%2A?displayProperty=nameWithType> instead of a [`for`](/dotnet/csharp/language-reference/keywords/for) loop. The following example can be added to the preceding `RenderFragmentParent` component: -:::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Shared/index/Details.razor"::: +```razor +<h1>Second example of three children with an index variable</h1> -In the following `People` component, each iteration of adding a person in `OnTimerCallback` results in Blazor rebuilding the entire collection. The page's focus remains on the *same index* position of `<input>` elements, so the focus shifts each time a person is added. *Shifting the focus away from what the user selected isn't desirable behavior.* After demonstrating the poor behavior with the following component, the [`@key`][5] directive attribute is used to improve the user's experience. +@foreach (var c in Enumerable.Range(0,3)) +{ + <RenderFragmentChild> + Count: @c + </RenderFragmentChild> +} +``` -`Pages/People.razor`: +Render fragments are used to render child content throughout Blazor apps and are described with examples in the following articles and article sections: -:::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Pages/index/People.razor"::: +* [Blazor layouts](xref:blazor/components/layouts) +* [Pass data across a component hierarchy](xref:blazor/components/cascading-values-and-parameters#pass-data-across-a-component-hierarchy) +* [Templated components](xref:blazor/components/templated-components) +* [Global exception handling](xref:blazor/fundamentals/handle-errors#global-exception-handling) -The contents of the `people` collection changes with inserted, deleted, or re-ordered entries. Rerendering can lead to visible behavior differences. For example, each time a person is inserted into the `people` collection, the user's focus is lost. +> [!NOTE] +> Blazor framework's [built-in Razor components](xref:blazor/components/built-in-components) use the same `ChildContent` component parameter convention to set their content. You can see the components that set child content by searching for the component parameter property name `ChildContent` in the [API documentation (filters API with the search term "ChildContent")](/dotnet/api/?term=ChildContent). -The mapping process of elements or components to a collection can be controlled with the [`@key`][5] directive attribute. Use of [`@key`][5] guarantees the preservation of elements or components based on the key's value. If the `Details` component in the preceding example is keyed on the `person` item, Blazor ignores rerendering `Details` components that haven't changed. +## Render fragments for reusable rendering logic -To modify the `People` component to use the [`@key`][5] directive attribute with the `people` collection, update the `<Details>` element to the following: +You can factor out child components purely as a way of reusing rendering logic. In any component's `@code` block, define a <xref:Microsoft.AspNetCore.Components.RenderFragment> and render the fragment from any location as many times as needed: ```razor -<Details @key="person" Data="@person.Data" /> -``` +<h1>Hello, world!</h1> -When the `people` collection changes, the association between `Details` instances and `person` instances is retained. When a `Person` is inserted at the beginning of the collection, one new `Details` instance is inserted at that corresponding position. Other instances are left unchanged. Therefore, the user's focus isn't lost as people are added to the collection. +@RenderWelcomeInfo -Other collection updates exhibit the same behavior when the [`@key`][5] directive attribute is used: +<p>Render the welcome info a second time:</p> -* If an instance is deleted from the collection, only the corresponding component instance is removed from the UI. Other instances are left unchanged. -* If collection entries are re-ordered, the corresponding component instances are preserved and re-ordered in the UI. +@RenderWelcomeInfo -> [!IMPORTANT] -> Keys are local to each container element or component. Keys aren't compared globally across the document. +@code { + private RenderFragment RenderWelcomeInfo = __builder => + { + <p>Welcome to your new app!</p> + }; +} +``` -### When to use `@key` +For more information, see [Reuse rendering logic](xref:blazor/performance#define-reusable-renderfragments-in-code). -Typically, it makes sense to use [`@key`][5] whenever a list is rendered (for example, in a [`foreach`](/dotnet/csharp/language-reference/keywords/foreach-in) block) and a suitable value exists to define the [`@key`][5]. +## Overwritten parameters -You can also use [`@key`][5] to preserve an element or component subtree when an object doesn't change, as the following examples show. +The Blazor framework generally imposes safe parent-to-child parameter assignment: -Example 1: +* Parameters aren't overwritten unexpectedly. +* Side effects are minimized. For example, additional renders are avoided because they may create infinite rendering loops. -```razor -<li @key="person"> - <input value="@person.Data" /> -</li> -``` +A child component receives new parameter values that possibly overwrite existing values when the parent component rerenders. Accidentally overwriting parameter values in a child component often occurs when developing the component with one or more data-bound parameters and the developer writes directly to a parameter in the child: -Example 2: +* The child component is rendered with one or more parameter values from the parent component. +* The child writes directly to the value of a parameter. +* The parent component rerenders and overwrites the value of the child's parameter. -```razor -<div @key="person"> - @* other HTML elements *@ -</div> -``` +The potential for overwriting parameter values extends into the child component's property `set` accessors, too. -If an `person` instance changes, the [`@key`][5] attribute directive forces Blazor to: +> [!IMPORTANT] +> Our general guidance is not to create components that directly write to their own parameters after the component is rendered for the first time. -* Discard the entire `<li>` or `<div>` and their descendants. -* Rebuild the subtree within the UI with new elements and components. +Consider the following `Expander` component that: -This is useful to guarantee that no UI state is preserved when the collection changes within a subtree. +* Renders child content. +* Toggles showing child content with a component parameter (`Expanded`). -### Scope of `@key` +After the following `Expander` component demonstrates an overwritten parameter, a modified `Expander` component is shown to demonstrate the correct approach for this scenario. The following examples can be placed in a local sample app to experience the behaviors described. -The [`@key`][5] attribute directive is scoped to its own siblings within its parent. +`Shared/Expander.razor`: -Consider the following example. The `first` and `second` keys are compared against each other within the same scope of the outer `<div>` element: +:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Shared/index/BadExpander.razor"::: -```razor -<div> - <div @key="first">...</div> - <div @key="second">...</div> -</div> -``` +The `Expander` component is added to the following `ExpanderExample` parent component that may call <xref:Microsoft.AspNetCore.Components.ComponentBase.StateHasChanged%2A>: -The following example demonstrates `first` and `second` keys in their own scopes, unrelated to each other and without influence on each other. Each [`@key`][5] scope only applies to its parent `<div>` element, not across the parent `<div>` elements: +* Calling <xref:Microsoft.AspNetCore.Components.ComponentBase.StateHasChanged%2A> in developer code notifies a component that its state has changed and typically triggers component rerendering to update the UI. <xref:Microsoft.AspNetCore.Components.ComponentBase.StateHasChanged%2A> is covered in more detail later in <xref:blazor/components/lifecycle> and <xref:blazor/components/rendering>. +* The button's `@onclick` directive attribute attaches an event handler to the button's `onclick` event. Event handling is covered in more detail later in <xref:blazor/components/event-handling>. -```razor -<div> - <div @key="first">...</div> -</div> -<div> - <div @key="second">...</div> -</div> -``` +`Pages/ExpanderExample.razor`: -For the `Details` component shown earlier, the following examples render `person` data within the same [`@key`][5] scope and demonstrate typical use cases for [`@key`][5]: +:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Pages/index/ExpanderExample.razor"::: -```razor -<div> - @foreach (var person in people) - { - <Details @key="person" Data="@person.Data" /> - } -</div> -``` +Initially, the `Expander` components behave independently when their `Expanded` properties are toggled. The child components maintain their states as expected. -```razor -@foreach (var person in people) -{ - <div @key="person"> - <Details Data="@person.Data" /> - </div> -} -``` +If <xref:Microsoft.AspNetCore.Components.ComponentBase.StateHasChanged%2A> is called in a parent component, the Blazor framework rerenders child components if their parameters might have changed: -```razor -<ol> - @foreach (var person in people) - { - <li @key="person"> - <Details Data="@person.Data" /> - </li> - } -</ol> -``` +* For a group of parameter types that Blazor explicitly checks, Blazor rerenders a child component if it detects that any of the parameters have changed. +* For unchecked parameter types, Blazor rerenders the child component *regardless of whether or not the parameters have changed*. Child content falls into this category of parameter types because child content is of type <xref:Microsoft.AspNetCore.Components.RenderFragment>, which is a delegate that refers to other mutable objects. -The following examples only scope [`@key`][5] to the `<div>` or `<li>` element that surrounds each `Details` component instance. Therefore, `person` data for each member of the `people` collection is **not** keyed on each `person` instance across the rendered `Details` components. Avoid the following patterns when using [`@key`][5]: +For the `ExpanderExample` component: -```razor -@foreach (var person in people) -{ - <div> - <Details @key="person" Data="@person.Data" /> - </div> -} -``` +* The first `Expander` component sets child content in a potentially mutable <xref:Microsoft.AspNetCore.Components.RenderFragment>, so a call to <xref:Microsoft.AspNetCore.Components.ComponentBase.StateHasChanged%2A> in the parent component automatically rerenders the component and potentially overwrites the value of `Expanded` to its initial value of `true`. +* The second `Expander` component doesn't set child content. Therefore, a potentially mutable <xref:Microsoft.AspNetCore.Components.RenderFragment> doesn't exist. A call to <xref:Microsoft.AspNetCore.Components.ComponentBase.StateHasChanged%2A> in the parent component doesn't automatically rerender the child component, so the component's `Expanded` value isn't overwritten. -```razor -<ol> - @foreach (var person in people) - { - <li> - <Details @key="person" Data="@person.Data" /> - </li> - } -</ol> -``` +To maintain state in the preceding scenario, use a *private field* in the `Expander` component to maintain its toggled state. -### When not to use `@key` +The following revised `Expander` component: -There's a performance cost when rendering with [`@key`][5]. The performance cost isn't large, but only specify [`@key`][5] if preserving the element or component benefits the app. +* Accepts the `Expanded` component parameter value from the parent. +* Assigns the component parameter value to a *private field* (`expanded`) in the [`OnInitialized` event](xref:blazor/components/lifecycle#component-initialization-oninitializedasync). +* Uses the private field to maintain its internal toggle state, which demonstrates how to avoid writing directly to a parameter. -Even if [`@key`][5] isn't used, Blazor preserves child element and component instances as much as possible. The only advantage to using [`@key`][5] is control over *how* model instances are mapped to the preserved component instances, instead of Blazor selecting the mapping. +> [!NOTE] +> The advice in this section extends to similar logic in component parameter `set` accessors, which can result in similar undesirable side effects. -### Values to use for `@key` +`Shared/Expander.razor`: -Generally, it makes sense to supply one of the following values for [`@key`][5]: +:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Shared/index/Expander.razor"::: -* Model object instances. For example, the `Person` instance (`person`) was used in the earlier example. This ensures preservation based on object reference equality. -* Unique identifiers. For example, unique identifiers can be based on primary key values of type `int`, `string`, or `Guid`. +For additional information, see [Blazor Two Way Binding Error (dotnet/aspnetcore #24599)](https://github.com/dotnet/aspnetcore/issues/24599). -Ensure that values used for [`@key`][5] don't clash. If clashing values are detected within the same parent element, Blazor throws an exception because it can't deterministically map old elements or components to new elements or components. Only use distinct values, such as object instances or primary key values. +For more information on change detection, including information on the exact types that Blazor checks, see <xref:blazor/components/rendering#rendering-conventions-for-componentbase>. -## Apply an attribute +## Attribute splatting and arbitrary parameters -Attributes can be applied to components with the [`@attribute`][7] directive. The following example applies the [`[Authorize]` attribute](xref:Microsoft.AspNetCore.Authorization.AuthorizeAttribute) to the component's class: +Components can capture and render additional attributes in addition to the component's declared parameters. Additional attributes can be captured in a dictionary and then *splatted* onto an element when the component is rendered using the [`@attributes`][3] Razor directive attribute. This scenario is useful for defining a component that produces a markup element that supports a variety of customizations. For example, it can be tedious to define attributes separately for an `<input>` that supports many parameters. -```razor -@page "/" -@attribute [Authorize] -``` +In the following `Splat` component: -## Conditional HTML element attributes +* The first `<input>` element (`id="useIndividualParams"`) uses individual component parameters. +* The second `<input>` element (`id="useAttributesDict"`) uses attribute splatting. -HTML element attribute properties are conditionally set based on the .NET value. If the value is `false` or `null`, the property isn't set. If the value is `true`, the property is set. +`Pages/Splat.razor`: -In the following example, `IsCompleted` determines if the `<input>` element's `checked` property is set. +:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Pages/index/Splat.razor"::: -`Pages/ConditionalAttribute.razor`: +The rendered `<input>` elements in the webpage are identical: -:::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Pages/index/ConditionalAttribute.razor"::: +```html +<input id="useIndividualParams" + maxlength="10" + placeholder="Input placeholder text" + required="required" + size="50"> -For more information, see <xref:mvc/views/razor>. +<input id="useAttributesDict" + maxlength="10" + placeholder="Input placeholder text" + required="required" + size="50"> +``` -> [!WARNING] -> Some HTML attributes, such as [`aria-pressed`](https://developer.mozilla.org/docs/Web/Accessibility/ARIA/Roles/button_role#Toggle_buttons), don't function properly when the .NET type is a `bool`. In those cases, use a `string` type instead of a `bool`. +To accept arbitrary attributes, define a [component parameter](#component-parameters) with the <xref:Microsoft.AspNetCore.Components.ParameterAttribute.CaptureUnmatchedValues> property set to `true`: -## Raw HTML +```razor +@code { + [Parameter(CaptureUnmatchedValues = true)] + public Dictionary<string, object> InputAttributes { get; set; } +} +``` -Strings are normally rendered using DOM text nodes, which means that any markup they may contain is ignored and treated as literal text. To render raw HTML, wrap the HTML content in a <xref:Microsoft.AspNetCore.Components.MarkupString> value. The value is parsed as HTML or SVG and inserted into the DOM. +The <xref:Microsoft.AspNetCore.Components.ParameterAttribute.CaptureUnmatchedValues> property on [`[Parameter]`](xref:Microsoft.AspNetCore.Components.ParameterAttribute) allows the parameter to match all attributes that don't match any other parameter. A component can only define a single parameter with <xref:Microsoft.AspNetCore.Components.ParameterAttribute.CaptureUnmatchedValues>. The property type used with <xref:Microsoft.AspNetCore.Components.ParameterAttribute.CaptureUnmatchedValues> must be assignable from [`Dictionary<string, object>`](xref:System.Collections.Generic.Dictionary%602) with string keys. Use of [`IEnumerable<KeyValuePair<string, object>>`](xref:System.Collections.Generic.IEnumerable%601) or [`IReadOnlyDictionary<string, object>`](xref:System.Collections.Generic.IReadOnlyDictionary%602) are also options in this scenario. -> [!WARNING] -> Rendering raw HTML constructed from any untrusted source is a **security risk** and should **always** be avoided. +The position of [`@attributes`][3] relative to the position of element attributes is important. When [`@attributes`][3] are splatted on the element, the attributes are processed from right to left (last to first). Consider the following example of a parent component that consumes a child component: -The following example shows using the <xref:Microsoft.AspNetCore.Components.MarkupString> type to add a block of static HTML content to the rendered output of a component. +`Shared/AttributeOrderChild1.razor`: -`Pages/MarkupStringExample.razor`: +:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Shared/index/AttributeOrderChild1.razor"::: -:::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Pages/index/MarkupStringExample.razor"::: +`Pages/AttributeOrderParent1.razor`: -## Razor templates +:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Pages/index/AttributeOrderParent1.razor"::: -Render fragments can be defined using Razor template syntax to define a UI snippet. Razor templates use the following format: +The `AttributeOrderChild1` component's `extra` attribute is set to the right of [`@attributes`][3]. The `AttributeOrderParent1` component's rendered `<div>` contains `extra="5"` when passed through the additional attribute because the attributes are processed right to left (last to first): -```razor -@<{HTML tag}>...</{HTML tag}> +```html +<div extra="5" /> ``` -The following example illustrates how to specify <xref:Microsoft.AspNetCore.Components.RenderFragment> and <xref:Microsoft.AspNetCore.Components.RenderFragment%601> values and render templates directly in a component. Render fragments can also be passed as arguments to [templated components](xref:blazor/components/templated-components). +In the following example, the order of `extra` and [`@attributes`][3] is reversed in the child component's `<div>`: -`Pages/RazorTemplate.razor`: +`Shared/AttributeOrderChild2.razor`: -:::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Pages/index/RazorTemplate.razor"::: +:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Shared/index/AttributeOrderChild2.razor"::: -Rendered output of the preceding code: +`Pages/AttributeOrderParent2.razor`: + +:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Pages/index/AttributeOrderParent2.razor"::: + +The `<div>` in the parent component's rendered webpage contains `extra="10"` when passed through the additional attribute: ```html -<p>The time is 4/19/2021 8:54:46 AM.</p> -<p>Pet: Nutty Rex</p> +<div extra="10" /> ``` -## Static assets +## Capture references to components -Blazor follows the convention of ASP.NET Core apps for static assets. Static assets are located in the project's [`web root` (`wwwroot`) folder](xref:fundamentals/index#web-root) or folders under the `wwwroot` folder. +Component references provide a way to reference a component instance for issuing commands. To capture a component reference: -Use a base-relative path (`/`) to refer to the web root for a static asset. In the following example, `logo.png` is physically located in the `{PROJECT ROOT}/wwwroot/images` folder. `{PROJECT ROOT}` is the app's project root. +* Add an [`@ref`][4] attribute to the child component. +* Define a field with the same type as the child component. -```razor -<img alt="Company logo" src="/images/logo.png" /> -``` +When the component is rendered, the field is populated with the component instance. You can then invoke .NET methods on the instance. -Components do **not** support tilde-slash notation (`~/`). +Consider the following `ReferenceChild` component that logs a message when its `ChildMethod` is called. -For information on setting an app's base path, see <xref:blazor/host-and-deploy/index#app-base-path>. +`Shared/ReferenceChild.razor`: -## Tag Helpers aren't supported in components +:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Shared/index/ReferenceChild.razor"::: -[`Tag Helpers`](xref:mvc/views/tag-helpers/intro) aren't supported in components. To provide Tag Helper-like functionality in Blazor, create a component with the same functionality as the Tag Helper and use the component instead. +A component reference is only populated after the component is rendered and its output includes `ReferenceChild`'s element. Until the component is rendered, there's nothing to reference. -## Scalable Vector Graphics (SVG) images +To manipulate component references after the component has finished rendering, use the [`OnAfterRender` or `OnAfterRenderAsync` methods](xref:blazor/components/lifecycle#after-component-render-onafterrenderasync). -Since Blazor renders HTML, browser-supported images, including [Scalable Vector Graphics (SVG) images (`.svg`)](https://developer.mozilla.org/docs/Web/SVG), are supported via the `<img>` tag: +To use a reference variable with an event handler, use a lambda expression or assign the event handler delegate in the [`OnAfterRender` or `OnAfterRenderAsync` methods](xref:blazor/components/lifecycle#after-component-render-onafterrenderasync). This ensures that the reference variable is assigned before the event handler is assigned. -```html -<img alt="Example image" src="image.svg" /> -``` +The following lambda approach uses the preceding `ReferenceChild` component. -Similarly, SVG images are supported in the CSS rules of a stylesheet file (`.css`): +`Pages/ReferenceParent1.razor`: -```css -.element-class { - background-image: url("image.svg"); -} -``` +:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Pages/index/ReferenceParent1.razor"::: -Blazor supports the [`<foreignObject>`](https://developer.mozilla.org/docs/Web/SVG/Element/foreignObject) element to display arbitrary HTML within an SVG. The markup can represent arbitrary HTML, a <xref:Microsoft.AspNetCore.Components.RenderFragment>, or a Razor component. +The following delegate approach uses the preceding `ReferenceChild` component. -The following example demonstrates: +`Pages/ReferenceParent2.razor`: -* Display of a `string` (`@message`). -* Two-way binding with an `<input>` element and a `value` field. -* A `Robot` component. +:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Pages/index/ReferenceParent2.razor"::: -```razor -<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> +While capturing component references use a similar syntax to [capturing element references](xref:blazor/js-interop/call-javascript-from-dotnet#capture-references-to-elements), capturing component references isn't a JavaScript interop feature. Component references aren't passed to JavaScript code. Component references are only used in .NET code. -<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> +> [!IMPORTANT] +> Do **not** use component references to mutate the state of child components. Instead, use normal declarative component parameters to pass data to child components. Use of component parameters result in child components that rerender at the correct times automatically. For more information, see the [component parameters](#component-parameters) section and the <xref:blazor/components/data-binding> article. -<svg xmlns="http://www.w3.org/2000/svg"> - <foreignObject> - <Robot /> - </foreignObject> -</svg> +## Synchronization context -@code { - private string message = "Lorem ipsum dolor sit amet, consectetur adipiscing " + - "elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua."; +Blazor uses a synchronization context (<xref:System.Threading.SynchronizationContext>) to enforce a single logical thread of execution. A component's [lifecycle methods](xref:blazor/components/lifecycle) and event callbacks raised by Blazor are executed on the synchronization context. - private string? value; -} -``` +Blazor Server's synchronization context attempts to emulate a single-threaded environment so that it closely matches the WebAssembly model in the browser, which is single threaded. At any given point in time, work is performed on exactly one thread, which yields the impression of a single logical thread. No two operations execute concurrently. -## Whitespace rendering behavior +### Avoid thread-blocking calls -Unless the [`@preservewhitespace`](xref:mvc/views/razor#preservewhitespace) directive is used with a value of `true`, extra whitespace is removed by default if: +Generally, don't call the following methods in components. The following methods block the execution thread and thus block the app from resuming work until the underlying <xref:System.Threading.Tasks.Task> is complete: -* Leading or trailing within an element. -* Leading or trailing within a <xref:Microsoft.AspNetCore.Components.RenderFragment>/<xref:Microsoft.AspNetCore.Components.RenderFragment%601> parameter (for example, child content passed to another component). -* It precedes or follows a C# code block, such as `@if` or `@foreach`. +* <xref:System.Threading.Tasks.Task%601.Result%2A> +* <xref:System.Threading.Tasks.Task.Wait%2A> +* <xref:System.Threading.Tasks.Task.WaitAny%2A> +* <xref:System.Threading.Tasks.Task.WaitAll%2A> +* <xref:System.Threading.Thread.Sleep%2A> +* <xref:System.Runtime.CompilerServices.TaskAwaiter.GetResult%2A> -Whitespace removal might affect the rendered output when using a CSS rule, such as `white-space: pre`. To disable this performance optimization and preserve the whitespace, take one of the following actions: +> [!NOTE] +> Blazor documentation examples that use the thread-blocking methods mentioned in this section are only using the methods for demonstration purposes, not as recommended coding guidance. For example, a few component code demonstrations simulate a long-running process by calling <xref:System.Threading.Thread.Sleep%2A?displayProperty=nameWithType>. -* Add the `@preservewhitespace true` directive at the top of the Razor file (`.razor`) to apply the preference to a specific component. -* Add the `@preservewhitespace true` directive inside an `_Imports.razor` file to apply the preference to a subdirectory or to the entire project. +### Invoke component methods externally to update state -In most cases, no action is required, as apps typically continue to behave normally (but faster). If stripping whitespace causes a rendering problem for a particular component, use `@preservewhitespace true` in that component to disable this optimization. +In the event a component must be updated based on an external event, such as a timer or other notification, use the `InvokeAsync` method, which dispatches code execution to Blazor's synchronization context. For example, consider the following *notifier service* that can notify any listening component about updated state. The `Update` method can be called from anywhere in the app. -## Generic type parameter support +`TimerService.cs`: -The [`@typeparam`][11] directive declares a [generic type parameter](/dotnet/csharp/programming-guide/generics/generic-type-parameters) for the generated component class: +:::code language="csharp" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/TimerService.cs"::: -```razor -@typeparam TItem -``` +`NotifierService.cs`: -C# syntax with [`where`](/dotnet/csharp/language-reference/keywords/where-generic-type-constraint) type constraints is supported: +:::code language="csharp" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/NotifierService.cs"::: -```razor -@typeparam TEntity where TEntity : IEntity -``` +Register the services: -In the following example, the `ListGenericTypeItems1` component is generically typed as `TExample`. +* In a Blazor WebAssembly app, register the services as singletons in `Program.cs`: -`Shared/ListGenericTypeItems1.razor`: + ```csharp + builder.Services.AddSingleton<NotifierService>(); + builder.Services.AddSingleton<TimerService>(); + ``` -:::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Shared/index/ListGenericTypeItems1.razor"::: +* In a Blazor Server app, register the services as scoped in `Startup.ConfigureServices`: -The following `GenericTypeExample1` component renders two `ListGenericTypeItems1` components: + ```csharp + services.AddScoped<NotifierService>(); + services.AddScoped<TimerService>(); + ``` -* String or integer data is assigned to the `ExampleList` parameter of each component. -* Type `string` or `int` that matches the type of the assigned data is set for the type parameter (`TExample`) of each component. +Use the `NotifierService` to update a component. -`Pages/GenericTypeExample1.razor`: +`Pages/ReceiveNotifications.razor`: -:::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Pages/index/GenericTypeExample1.razor"::: +:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Pages/index/ReceiveNotifications.razor"::: -For more information, see <xref:mvc/views/razor#typeparam>. For an example of generic typing with templated components, see <xref:blazor/components/templated-components>. +In the preceding example: -## Cascaded generic type support +* `NotifierService` invokes the component's `OnNotify` method outside of Blazor's synchronization context. `InvokeAsync` is used to switch to the correct context and queue a render. For more information, see <xref:blazor/components/rendering>. +* The component implements <xref:System.IDisposable>. The `OnNotify` delegate is unsubscribed in the `Dispose` method, which is called by the framework when the component is disposed. For more information, see <xref:blazor/components/lifecycle#component-disposal-with-idisposable-and-iasyncdisposable>. -An ancestor component can cascade a type parameter by name to descendants using the [`[CascadingTypeParameter]` attribute](xref:Microsoft.AspNetCore.Components.CascadingTypeParameterAttribute). This attribute allows a generic type inference to use the specified type parameter automatically with descendants that have a type parameter with the same name. +> [!IMPORTANT] +> If a Razor component defines an event that's triggered from a background thread, the component might be required to capture and restore the execution context (<xref:System.Threading.ExecutionContext>) at the time the handler is registered. For more information, see [Calling `InvokeAsync(StateHasChanged)` causes page to fallback to default culture (dotnet/aspnetcore #28521)](https://github.com/dotnet/aspnetcore/issues/28521). -By adding `@attribute [CascadingTypeParameter(...)]` to a component, the specified generic type argument is automatically used by descendants that: +## Use `@key` to control the preservation of elements and components -* Are nested as child content for the component in the same `.razor` document. -* Also declare a [`@typeparam`](xref:mvc/views/razor#typeparam) with the exact same name. -* Don't have another value explicitly supplied or implicitly inferred for the type parameter. If another value is supplied or inferred, it takes precedence over the cascaded generic type. +When rendering a list of elements or components and the elements or components subsequently change, Blazor must decide which of the previous elements or components are retained and how model objects should map to them. Normally, this process is automatic and sufficient for general rendering, but there are often cases where controlling the process using the [`@key`][5] directive attribute is required. -When receiving a cascaded type parameter, components obtain the parameter value from the closest ancestor that has a <xref:Microsoft.AspNetCore.Components.CascadingTypeParameterAttribute> with a matching name. Cascaded generic type parameters are overridden within a particular subtree. +Consider the following example that demonstrates a collection mapping problem that's solved by using [`@key`][5]. -Matching is only performed by name. Therefore, we recommend avoiding a cascaded generic type parameter with a generic name, for example `T` or `TItem`. If a developer opts into cascading a type parameter, they're implicitly promising that its name is unique enough not to clash with other cascaded type parameters from unrelated components. +For the following `Details` and `People` components: -Generic types can be cascaded to child components in either of the following approaches with ancestor (parent) components, which are demonstrated in the following two sub-sections: +* The `Details` component receives data (`Data`) from the parent `People` component, which is displayed in an `<input>` element. Any given displayed `<input>` element can receive the focus of the page from the user when they select one of the `<input>` elements. +* The `People` component creates a list of person objects for display using the `Details` component. Every three seconds, a new person is added to the collection. -* Explicitly set the cascaded generic type. -* Infer the cascaded generic type. +This demonstration allows you to: -The following subsections provide examples of the preceding approaches using the following two `ListDisplay` components. The components receive and render list data and are generically typed as `TExample`. These components are for demonstration purposes and only differ in the color of text that the list is rendered. If you wish to experiment with the components in the following sub-sections in a local test app, add the following two components to the app first. +* Select an `<input>` from among several rendered `Details` components. +* Study the behavior of the page's focus as the people collection automatically grows. -`Shared/ListDisplay1.razor`: +`Shared/Details.razor`: -```razor -@typeparam TExample +:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Shared/index/Details.razor"::: -@if (ExampleList is not null) -{ - <ul style="color:blue"> - @foreach (var item in ExampleList) - { - <li>@item</li> - } - </ul> -} +In the following `People` component, each iteration of adding a person in `OnTimerCallback` results in Blazor rebuilding the entire collection. The page's focus remains on the *same index* position of `<input>` elements, so the focus shifts each time a person is added. *Shifting the focus away from what the user selected isn't desirable behavior.* After demonstrating the poor behavior with the following component, the [`@key`][5] directive attribute is used to improve the user's experience. -@code { - [Parameter] - public IEnumerable<TExample>? ExampleList { get; set; } -} -``` +`Pages/People.razor`: -`Shared/ListDisplay2.razor`: +:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Pages/index/People.razor"::: -```razor -@typeparam TExample +The contents of the `people` collection changes with inserted, deleted, or re-ordered entries. Rerendering can lead to visible behavior differences. For example, each time a person is inserted into the `people` collection, the user's focus is lost. -@if (ExampleList is not null) -{ - <ul style="color:red"> - @foreach (var item in ExampleList) - { - <li>@item</li> - } - </ul> -} +The mapping process of elements or components to a collection can be controlled with the [`@key`][5] directive attribute. Use of [`@key`][5] guarantees the preservation of elements or components based on the key's value. If the `Details` component in the preceding example is keyed on the `person` item, Blazor ignores rerendering `Details` components that haven't changed. -@code { - [Parameter] - public IEnumerable<TExample>? ExampleList { get; set; } -} +To modify the `People` component to use the [`@key`][5] directive attribute with the `people` collection, update the `<Details>` element to the following: + +```razor +<Details @key="person" Data="@person.Data" /> ``` -### Explicit generic types based on ancestor components +When the `people` collection changes, the association between `Details` instances and `person` instances is retained. When a `Person` is inserted at the beginning of the collection, one new `Details` instance is inserted at that corresponding position. Other instances are left unchanged. Therefore, the user's focus isn't lost as people are added to the collection. -The demonstration in this section cascades a type explicitly for `TExample`. +Other collection updates exhibit the same behavior when the [`@key`][5] directive attribute is used: -> [!NOTE] -> This section uses the two `ListDisplay` components in the [Cascaded generic type support](#cascaded-generic-type-support) section. +* If an instance is deleted from the collection, only the corresponding component instance is removed from the UI. Other instances are left unchanged. +* If collection entries are re-ordered, the corresponding component instances are preserved and re-ordered in the UI. -The following `ListGenericTypeItems2` component receives data and cascades a generic type parameter named `TExample` to its descendent components. In the upcoming parent component, the `ListGenericTypeItems2` component is used to display list data with the preceding `ListDisplay` component. +> [!IMPORTANT] +> Keys are local to each container element or component. Keys aren't compared globally across the document. -`Shared/ListGenericTypeItems2.razor`: +### When to use `@key` -```razor -@attribute [CascadingTypeParameter(nameof(TExample))] -@typeparam TExample +Typically, it makes sense to use [`@key`][5] whenever a list is rendered (for example, in a [`foreach`](/dotnet/csharp/language-reference/keywords/foreach-in) block) and a suitable value exists to define the [`@key`][5]. -<h2>List Generic Type Items 2</h2> +You can also use [`@key`][5] to preserve an element or component subtree when an object doesn't change, as the following examples show. -@ChildContent +Example 1: -@code { - [Parameter] - public RenderFragment? ChildContent { get; set; } -} +```razor +<li @key="person"> + <input value="@person.Data" /> +</li> ``` -The following `GenericTypeExample2` parent component sets the child content (<xref:Microsoft.AspNetCore.Components.RenderFragment>) of two `ListGenericTypeItems2` components specifying the `ListGenericTypeItems2` types (`TExample`), which are cascaded to child components. `ListDisplay` components are rendered with the list item data shown in the example. String data is used with the first `ListGenericTypeItems2` component, and integer data is used with the second `ListGenericTypeItems2` component. - -`Pages/GenericTypeExample2.razor`: +Example 2: ```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> +<div @key="person"> + @* other HTML elements *@ +</div> ``` -Specifying the type explicitly also allows the use of [cascading values and parameters](xref:blazor/components/cascading-values-and-parameters) to provide data to child components, as the following demonstration shows. +If an `person` instance changes, the [`@key`][5] attribute directive forces Blazor to: -`Shared/ListDisplay3.razor`: +* Discard the entire `<li>` or `<div>` and their descendants. +* Rebuild the subtree within the UI with new elements and components. -```razor -@typeparam TExample +This is useful to guarantee that no UI state is preserved when the collection changes within a subtree. -@if (ExampleList is not null) -{ - <ul style="color:blue"> - @foreach (var item in ExampleList) - { - <li>@item</li> - } - </ul> -} +### Scope of `@key` -@code { - [CascadingParameter] - protected IEnumerable<TExample>? ExampleList { get; set; } -} -``` +The [`@key`][5] attribute directive is scoped to its own siblings within its parent. -`Shared/ListDisplay4.razor`: +Consider the following example. The `first` and `second` keys are compared against each other within the same scope of the outer `<div>` element: ```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; } -} +<div> + <div @key="first">...</div> + <div @key="second">...</div> +</div> ``` -`Shared/ListGenericTypeItems3.razor`: +The following example demonstrates `first` and `second` keys in their own scopes, unrelated to each other and without influence on each other. Each [`@key`][5] scope only applies to its parent `<div>` element, not across the parent `<div>` elements: ```razor -@attribute [CascadingTypeParameter(nameof(TExample))] -@typeparam TExample +<div> + <div @key="first">...</div> +</div> +<div> + <div @key="second">...</div> +</div> +``` -<h2>List Generic Type Items 3</h2> +For the `Details` component shown earlier, the following examples render `person` data within the same [`@key`][5] scope and demonstrate typical use cases for [`@key`][5]: -@ChildContent +```razor +<div> + @foreach (var person in people) + { + <Details @key="person" Data="@person.Data" /> + } +</div> +``` -@if (ExampleList is not null) +```razor +@foreach (var person in people) { - <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; } + <div @key="person"> + <Details Data="@person.Data" /> + </div> } ``` -When cascading the data in the following example, the type must be provided to the `ListGenericTypeItems3` component. - -`Pages/GenericTypeExample3.razor`: - ```razor -@page "/generic-type-example-3" - -<h1>Generic Type Example 3</h1> - -<CascadingValue Value="@stringData"> - <ListGenericTypeItems3 TExample="string"> - <ListDisplay3 /> - <ListDisplay4 /> - </ListGenericTypeItems3> -</CascadingValue> +<ol> + @foreach (var person in people) + { + <li @key="person"> + <Details Data="@person.Data" /> + </li> + } +</ol> +``` -<CascadingValue Value="@integerData"> - <ListGenericTypeItems3 TExample="int"> - <ListDisplay3 /> - <ListDisplay4 /> - </ListGenericTypeItems3> -</CascadingValue> +The following examples only scope [`@key`][5] to the `<div>` or `<li>` element that surrounds each `Details` component instance. Therefore, `person` data for each member of the `people` collection is **not** keyed on each `person` instance across the rendered `Details` components. Avoid the following patterns when using [`@key`][5]: -@code { - private List<string> stringData = new() { "Item 1", "Item 2" }; - private List<int> integerData = new() { 1, 2, 3 }; +```razor +@foreach (var person in people) +{ + <div> + <Details @key="person" Data="@person.Data" /> + </div> } ``` -When multiple generic types are cascaded, values for all generic types in the set must be passed. In the following example, `TItem`, `TValue`, and `TEdit` are `GridColumn` generic types, but the parent component that places `GridColumn` doesn't specify the `TItem` type: - ```razor -<GridColumn TValue="string" TEdit="@TextEdit" /> +<ol> + @foreach (var person in people) + { + <li> + <Details @key="person" Data="@person.Data" /> + </li> + } +</ol> ``` -The preceding example generates a compile-time error that the `GridColumn` component is missing the `TItem` type parameter. Valid code specifies all of the types: +### When not to use `@key` -```razor -<GridColumn TValue="string" TEdit="@TextEdit" TItem="@User" /> -``` +There's a performance cost when rendering with [`@key`][5]. The performance cost isn't large, but only specify [`@key`][5] if preserving the element or component benefits the app. -### Infer generic types based on ancestor components +Even if [`@key`][5] isn't used, Blazor preserves child element and component instances as much as possible. The only advantage to using [`@key`][5] is control over *how* model instances are mapped to the preserved component instances, instead of Blazor selecting the mapping. -The demonstration in this section cascades a type inferred for `TExample`. +### Values to use for `@key` -> [!NOTE] -> This section uses the two `ListDisplay` components in the [Cascaded generic type support](#cascaded-generic-type-support) section. +Generally, it makes sense to supply one of the following values for [`@key`][5]: -`Shared/ListGenericTypeItems4.razor`: +* Model object instances. For example, the `Person` instance (`person`) was used in the earlier example. This ensures preservation based on object reference equality. +* Unique identifiers. For example, unique identifiers can be based on primary key values of type `int`, `string`, or `Guid`. -```razor -@attribute [CascadingTypeParameter(nameof(TExample))] -@typeparam TExample +Ensure that values used for [`@key`][5] don't clash. If clashing values are detected within the same parent element, Blazor throws an exception because it can't deterministically map old elements or components to new elements or components. Only use distinct values, such as object instances or primary key values. -<h2>List Generic Type Items 4</h2> +## Apply an attribute -@ChildContent +Attributes can be applied to components with the [`@attribute`][7] directive. The following example applies the [`[Authorize]` attribute](xref:Microsoft.AspNetCore.Authorization.AuthorizeAttribute) to the component's class: -@if (ExampleList is not null) -{ - <ul style="color:green"> - @foreach(var item in ExampleList) - { - <li>@item</li> - } - </ul> +```razor +@page "/" +@attribute [Authorize] +``` - <p> - Type of <code>TExample</code>: @typeof(TExample) - </p> -} +## Conditional HTML element attributes -@code { - [Parameter] - public IEnumerable<TExample>? ExampleList { get; set; } +HTML element attribute properties are conditionally set based on the .NET value. If the value is `false` or `null`, the property isn't set. If the value is `true`, the property is set. - [Parameter] - public RenderFragment? ChildContent { get; set; } -} -``` +In the following example, `IsCompleted` determines if the `<input>` element's `checked` property is set. -The following `GenericTypeExample4` component with inferred cascaded types provides different data for display. +`Pages/ConditionalAttribute.razor`: -`Pages/GenericTypeExample4.razor`: +:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Pages/index/ConditionalAttribute.razor"::: -```razor -@page "/generic-type-example-4" +For more information, see <xref:mvc/views/razor>. -<h1>Generic Type Example 4</h1> +> [!WARNING] +> Some HTML attributes, such as [`aria-pressed`](https://developer.mozilla.org/docs/Web/Accessibility/ARIA/Roles/button_role#Toggle_buttons), don't function properly when the .NET type is a `bool`. In those cases, use a `string` type instead of a `bool`. -<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> +## Raw HTML -<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> -``` +Strings are normally rendered using DOM text nodes, which means that any markup they may contain is ignored and treated as literal text. To render raw HTML, wrap the HTML content in a <xref:Microsoft.AspNetCore.Components.MarkupString> value. The value is parsed as HTML or SVG and inserted into the DOM. -The following `GenericTypeExample5` component with inferred cascaded types provides the same data for display. The following example directly assigns the data to the components. +> [!WARNING] +> Rendering raw HTML constructed from any untrusted source is a **security risk** and should **always** be avoided. -`Pages/GenericTypeExample5.razor`: +The following example shows using the <xref:Microsoft.AspNetCore.Components.MarkupString> type to add a block of static HTML content to the rendered output of a component. -```razor -@page "/generic-type-example-5" +`Pages/MarkupStringExample.razor`: -<h1>Generic Type Example 5</h1> +:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Pages/index/MarkupStringExample.razor"::: -<ListGenericTypeItems4 ExampleList="@stringData"> - <ListDisplay1 ExampleList="@stringData" /> - <ListDisplay2 ExampleList="@stringData" /> -</ListGenericTypeItems4> +## Razor templates -<ListGenericTypeItems4 ExampleList="@integerData"> - <ListDisplay1 ExampleList="@integerData" /> - <ListDisplay2 ExampleList="@integerData" /> -</ListGenericTypeItems4> +Render fragments can be defined using Razor template syntax to define a UI snippet. Razor templates use the following format: -@code { - private List<string> stringData = new() { "Item 1", "Item 2" }; - private List<int> integerData = new() { 1, 2, 3 }; -} +```razor +@<{HTML tag}>...</{HTML tag}> ``` -## Render Razor components from JavaScript +The following example illustrates how to specify <xref:Microsoft.AspNetCore.Components.RenderFragment> and <xref:Microsoft.AspNetCore.Components.RenderFragment%601> values and render templates directly in a component. Render fragments can also be passed as arguments to [templated components](xref:blazor/components/templated-components). -Razor components can be dynamically-rendered from JavaScript (JS) for existing JS apps. +`Pages/RazorTemplate.razor`: -To render a Razor component from JS, register the component as a root component for JS rendering and assign the component an identifier: +:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Pages/index/RazorTemplate.razor"::: -* In a Blazor Server app, modify the call to <xref:Microsoft.Extensions.DependencyInjection.ComponentServiceCollectionExtensions.AddServerSideBlazor%2A> in `Program.cs`: +Rendered output of the preceding code: - ```csharp - builder.Services.AddServerSideBlazor(options => - { - options.RootComponents.RegisterForJavaScript<Counter>(identifier: "counter"); - }); - ``` - - > [!NOTE] - > The preceding code example requires a namespace for the app's components (for example, `using BlazorSample.Pages;`) in the `Program.cs` file. +```html +<p>The time is 4/19/2021 8:54:46 AM.</p> +<p>Pet: Nutty Rex</p> +``` -* In a Blazor WebAssembly app, call <xref:Microsoft.AspNetCore.Components.Web.JSComponentConfigurationExtensions.RegisterForJavaScript%2A> on <xref:Microsoft.AspNetCore.Components.WebAssembly.Hosting.WebAssemblyHostBuilder.RootComponents> in `Program.cs`: +## Static assets - ```csharp - builder.RootComponents.RegisterForJavaScript<Counter>(identifier: "counter"); - ``` - - > [!NOTE] - > The preceding code example requires a namespace for the app's components (for example, `using BlazorSample.Pages;`) in the `Program.cs` file. +Blazor follows the convention of ASP.NET Core apps for static assets. Static assets are located in the project's [`web root` (`wwwroot`) folder](xref:fundamentals/index#web-root) or folders under the `wwwroot` folder. -Load Blazor into the JS app (`blazor.server.js` or `blazor.webassembly.js`). Render the component from JS into a container element using the registered identifier, passing component parameters as needed: +Use a base-relative path (`/`) to refer to the web root for a static asset. In the following example, `logo.png` is physically located in the `{PROJECT ROOT}/wwwroot/images` folder. `{PROJECT ROOT}` is the app's project root. -```javascript -let containerElement = document.getElementById('my-counter'); -await Blazor.rootComponents.add(containerElement, 'counter', { incrementAmount: 10 }); +```razor +<img alt="Company logo" src="/images/logo.png" /> ``` -## Blazor custom elements +Components do **not** support tilde-slash notation (`~/`). -Use Blazor custom elements to dynamically render Razor components from other SPA frameworks, such as Angular or React. +For information on setting an app's base path, see <xref:blazor/host-and-deploy/index#app-base-path>. -Blazor custom elements: +## Tag Helpers aren't supported in components -* Use standard HTML interfaces to implement custom HTML elements. -* Eliminate the need to manually manage the state and lifecycle of root Razor components using JavaScript APIs. -* Are useful for gradually introducing Razor components into existing projects written in other SPA frameworks. +[`Tag Helpers`](xref:mvc/views/tag-helpers/intro) aren't supported in components. To provide Tag Helper-like functionality in Blazor, create a component with the same functionality as the Tag Helper and use the component instead. -### Blazor Server registration +## Scalable Vector Graphics (SVG) images -To register a root component as a custom element in a Blazor Server app, modify the call to <xref:Microsoft.Extensions.DependencyInjection.ComponentServiceCollectionExtensions.AddServerSideBlazor%2A> in `Program.cs`. The following example registers the `Counter` component with the custom HTML element `my-counter`: +Since Blazor renders HTML, browser-supported images, including [Scalable Vector Graphics (SVG) images (`.svg`)](https://developer.mozilla.org/docs/Web/SVG), are supported via the `<img>` tag: -```csharp -builder.Services.AddServerSideBlazor(options => -{ - options.RootComponents.RegisterCustomElement<Counter>("my-counter"); -}); +```html +<img alt="Example image" src="image.svg" /> ``` -> [!NOTE] -> The preceding code example requires a namespace for the app's components (for example, `using BlazorSample.Pages;`) in the `Program.cs` file. - -### Blazor WebAssembly registration - -To register a root component as a custom element in a Blazor WebAssembly app, call `RegisterCustomElement` on <xref:Microsoft.AspNetCore.Components.WebAssembly.Hosting.WebAssemblyHostBuilder.RootComponents> in `Program.cs`. The following example registers the `Counter` component with the custom HTML element `my-counter`: +Similarly, SVG images are supported in the CSS rules of a stylesheet file (`.css`): -```csharp -builder.RootComponents.RegisterCustomElement<Counter>("my-counter"); +```css +.element-class { + background-image: url("image.svg"); +} ``` -> [!NOTE] -> The preceding code example requires a namespace for the app's components (for example, `using BlazorSample.Pages;`) in the `Program.cs` file. +## Whitespace rendering behavior -### Use the registered custom element +Whitespace is retained in a component's source markup. Whitespace-only text renders in the browser's DOM even when there's no visual effect. -Use the custom element with any web framework. For example, the preceding `my-counter` custom HTML element is used in a React app with the following markup: +Consider the following component markup: -```html -<my-counter increment-amount={incrementAmount}></my-counter> +```razor +<ul> + @foreach (var item in Items) + { + <li> + @item.Text + </li> + } +</ul> ``` -For a complete example of how to create custom elements with Blazor, see the [`CustomElementsComponent` component](https://github.com/dotnet/aspnetcore/blob/main/src/Components/test/testassets/BasicTestApp/CustomElementsComponent.razor) in the reference source. - -[!INCLUDE[](~/includes/aspnetcore-repo-ref-source-links.md)] +The preceding example renders the following unnecessary whitespace: -## Generate Angular and React components +* Outside of the `@foreach` code block. +* Around the `<li>` element. +* Around the `@item.Text` output. -Generate framework-specific JavaScript (JS) components from Razor components for web frameworks, such as Angular or React. This capability isn't included with .NET, but is enabled by the support for rendering Razor components from JS. The [JS component generation sample on GitHub](https://github.com/aspnet/samples/tree/main/samples/aspnetcore/blazor/JSComponentGeneration) demonstrates how to generate Angular and React components from Razor components. See the GitHub sample app's `README.md` file for additional information. +A list of 100 items results in over 400 areas of whitespace. None of the extra whitespace visually affects the rendered output. -> [!WARNING] -> The Angular and React component features are currently **experimental, unsupported, and subject to change or be removed at any time**. We welcome your feedback on how well this particular approach meets your requirements. +When rendering static HTML for components, whitespace inside a tag isn't preserved. For example, view the rendered output of the following `<img>` tag in a component Razor file (`.razor`): -## `QuickGrid` component +```razor +<img alt="Example image" src="img.png" /> +``` -The `QuickGrid` component is an experimental Razor component for quickly and efficiently displaying data in tabular form. `QuickGrid` provides a simple and convenient data grid component for common grid rendering scenarios and serves as a reference architecture and performance baseline for building data grid components. `QuickGrid` is highly optimized and uses advanced techniques to achieve optimal rendering performance. +Whitespace isn't preserved from the preceding markup: -To get started with `QuickGrid`: +```razor +<img alt="Example image" src="img.png" /> +``` -1. Add package reference for [`Microsoft.AspNetCore.Components.QuickGrid`](https://www.nuget.org/packages/Microsoft.AspNetCore.Components.QuickGrid). If using the .NET CLI to add the package reference, include the `--prerelease` option when you execute the [`dotnet add package` command](/dotnet/core/tools/dotnet-add-package). +## Generic type parameter support - [!INCLUDE[](~/includes/package-reference.md)] +The [`@typeparam`][11] directive declares a [generic type parameter](/dotnet/csharp/programming-guide/generics/generic-type-parameters) for the generated component class: -1. Add the following component to render a grid. +```razor +@typeparam TItem +``` - `Pages/QuickGridExample.razor`: +In the following example, the `ListGenericTypeItems1` component is generically typed as `TExample`. - ```razor - @page "/quickgrid-example" - @using Microsoft.AspNetCore.Components.QuickGrid +`Shared/ListGenericTypeItems1.razor`: - <QuickGrid Items="@people"> - <PropertyColumn Property="@(p => p.PersonId)" Sortable="true" /> - <PropertyColumn Property="@(p => p.Name)" Sortable="true" /> - <PropertyColumn Property="@(p => p.BirthDate)" Format="yyyy-MM-dd" Sortable="true" /> - </QuickGrid> +:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Shared/index/ListGenericTypeItems1.razor"::: - @code { - private record Person(int PersonId, string Name, DateOnly BirthDate); +The following `GenericTypeExample1` component renders two `ListGenericTypeItems1` components: - private IQueryable<Person> people = new[] - { - new Person(10895, "Jean Martin", new DateOnly(1985, 3, 16)), - new Person(10944, "António Langa", new DateOnly(1991, 12, 1)), - new Person(11203, "Julie Smith", new DateOnly(1958, 10, 10)), - new Person(11205, "Nur Sari", new DateOnly(1922, 4, 27)), - new Person(11898, "Jose Hernandez", new DateOnly(2011, 5, 3)), - new Person(12130, "Kenji Sato", new DateOnly(2004, 1, 9)), - }.AsQueryable(); - } - ``` +* String or integer data is assigned to the `ExampleList` parameter of each component. +* Type `string` or `int` that matches the type of the assigned data is set for the type parameter (`TExample`) of each component. -1. Access the component in a browser at the relative path `/quickgrid-example`. +`Pages/GenericTypeExample1.razor`: -For various `QuickGrid` demonstrations, see the [**QuickGrid for Blazor** app](https://aspnet.github.io/quickgridsamples/). The demo site is built using Blazor WebAssembly and is hosted on GitHub Pages. The site loads fast thanks to static prerendering using the community-maintained [`BlazorWasmPrerendering.Build` GitHub project](https://github.com/jsakamoto/BlazorWasmPreRendering.Build). +:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Pages/index/GenericTypeExample1.razor"::: -There aren't current plans to extend `QuickGrid` with features that full-blown commercial grids tend to offer, for example, hierarchical rows, drag-to-reorder columns, or Excel-like range selections. If you require advanced features that you don't wish to develop on your own, continue using third-party grids. +For more information, see the following articles: -> [!WARNING] -> `QuickGrid` is currently **prerelease**, **experimental**, and **unsupported** by Microsoft. `QuickGrid` is ***not*** recommended for use in production apps. +* <xref:mvc/views/razor#typeparam> +* <xref:blazor/components/templated-components> :::moniker-end diff --git a/aspnetcore/blazor/components/layouts.md b/aspnetcore/blazor/components/layouts.md index e9571bd0c60e..8bedc9c27c95 100644 --- a/aspnetcore/blazor/components/layouts.md +++ b/aspnetcore/blazor/components/layouts.md @@ -5,14 +5,14 @@ description: Learn how to create reusable layout components for Blazor apps. monikerRange: '>= aspnetcore-3.1' ms.author: riande ms.custom: mvc -ms.date: 11/09/2021 +ms.date: 11/08/2022 uid: blazor/components/layouts --- # ASP.NET Core Blazor layouts This article explains how to create reusable layout components for Blazor apps. -:::moniker range=">= aspnetcore-6.0 < aspnetcore-7.0" +:::moniker range=">= aspnetcore-7.0" Some app elements, such as menus, copyright messages, and company logos, are usually part of app's overall presentation. Placing a copy of the markup for these elements into all of the components of an app isn't efficient. Every time that one of these elements is updated, every component that uses the element must be updated. This approach is costly to maintain and can lead to inconsistent content if an update is missed. *Layouts* solve these problems. @@ -35,7 +35,7 @@ The following `DoctorWhoLayout` component shows the Razor template of a layout c `Shared/DoctorWhoLayout.razor`: -:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Shared/layouts/DoctorWhoLayout.razor" highlight="1,13"::: +:::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Shared/layouts/DoctorWhoLayout.razor" highlight="1,13"::: ### `MainLayout` component @@ -43,7 +43,7 @@ In an app created from a [Blazor project template](xref:blazor/project-structure `Shared/MainLayout.razor`: -:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Shared/layouts/MainLayout.razor"::: +:::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Shared/layouts/MainLayout.razor"::: [Blazor's CSS isolation feature](xref:blazor/components/css-isolation) applies isolated CSS styles to the `MainLayout` component. By convention, the styles are provided by the accompanying stylesheet of the same name, `Shared/MainLayout.razor.css`. The ASP.NET Core framework implementation of the stylesheet is available for inspection in the ASP.NET Core reference source (`dotnet/aspnetcore` GitHub repository): @@ -62,7 +62,7 @@ The content of the following `Episodes` component is inserted into the `DoctorWh `Pages/Episodes.razor`: -:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Pages/layouts/Episodes.razor" highlight="2"::: +:::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Pages/layouts/Episodes.razor" highlight="2"::: The following rendered HTML markup is produced by the preceding `DoctorWhoLayout` and `Episodes` component. Extraneous markup doesn't appear in order to focus on the content provided by the two components involved: @@ -130,7 +130,7 @@ Specify the default app layout in the `App` component's <xref:Microsoft.AspNetCo `App.razor`: -:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/layouts/App1.razor" highlight="3"::: +:::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/layouts/App1.razor" highlight="3"::: For more information on the <xref:Microsoft.AspNetCore.Components.Routing.Router> component, see <xref:blazor/fundamentals/routing>. @@ -166,13 +166,13 @@ The following `DoctorWhoLayout` component is a modified version of the example s `Shared/DoctorWhoLayout.razor`: -:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Shared/layouts/DoctorWhoLayout2.razor" highlight="2,12"::: +:::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Shared/layouts/DoctorWhoLayout2.razor" highlight="2,12"::: The `ProductionsLayout` component contains the top-level layout elements, where the header (`<header>...</header>`) and footer (`<footer>...</footer>`) elements now reside. The `DoctorWhoLayout` with the `Episodes` component is rendered where `@Body` appears. `Shared/ProductionsLayout.razor`: -:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Shared/layouts/ProductionsLayout.razor" highlight="13"::: +:::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Shared/layouts/ProductionsLayout.razor" highlight="13"::: The following rendered HTML markup is produced by the preceding nested layout. Extraneous markup doesn't appear in order to focus on the nested content provided by the three components involved: @@ -232,7 +232,7 @@ When routable components are integrated into a Razor Pages app, the app's shared :::moniker-end -:::moniker range=">= aspnetcore-5.0 < aspnetcore-6.0" +:::moniker range=">= aspnetcore-6.0 < aspnetcore-7.0" Some app elements, such as menus, copyright messages, and company logos, are usually part of app's overall presentation. Placing a copy of the markup for these elements into all of the components of an app isn't efficient. Every time that one of these elements is updated, every component that uses the element must be updated. This approach is costly to maintain and can lead to inconsistent content if an update is missed. *Layouts* solve these problems. @@ -255,7 +255,7 @@ The following `DoctorWhoLayout` component shows the Razor template of a layout c `Shared/DoctorWhoLayout.razor`: -:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Shared/layouts/DoctorWhoLayout.razor" highlight="1,13"::: +:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Shared/layouts/DoctorWhoLayout.razor" highlight="1,13"::: ### `MainLayout` component @@ -263,7 +263,7 @@ In an app created from a [Blazor project template](xref:blazor/project-structure `Shared/MainLayout.razor`: -:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Shared/layouts/MainLayout.razor"::: +:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Shared/layouts/MainLayout.razor"::: [Blazor's CSS isolation feature](xref:blazor/components/css-isolation) applies isolated CSS styles to the `MainLayout` component. By convention, the styles are provided by the accompanying stylesheet of the same name, `Shared/MainLayout.razor.css`. The ASP.NET Core framework implementation of the stylesheet is available for inspection in the ASP.NET Core reference source (`dotnet/aspnetcore` GitHub repository): @@ -282,7 +282,7 @@ The content of the following `Episodes` component is inserted into the `DoctorWh `Pages/Episodes.razor`: -:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Pages/layouts/Episodes.razor" highlight="2"::: +:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Pages/layouts/Episodes.razor" highlight="2"::: The following rendered HTML markup is produced by the preceding `DoctorWhoLayout` and `Episodes` component. Extraneous markup doesn't appear in order to focus on the content provided by the two components involved: @@ -350,9 +350,7 @@ Specify the default app layout in the `App` component's <xref:Microsoft.AspNetCo `App.razor`: -:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/layouts/App1.razor" highlight="3"::: - -[!INCLUDE[](~/blazor/includes/prefer-exact-matches.md)] +:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/layouts/App1.razor" highlight="3"::: For more information on the <xref:Microsoft.AspNetCore.Components.Routing.Router> component, see <xref:blazor/fundamentals/routing>. @@ -365,7 +363,7 @@ To set a layout for arbitrary Razor template content, specify the layout with a `App.razor`: ```razor -<Router AppAssembly="@typeof(Program).Assembly"> +<Router AppAssembly="@typeof(App).Assembly"> <Found Context="routeData"> <RouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)" /> </Found> @@ -378,8 +376,6 @@ To set a layout for arbitrary Razor template content, specify the layout with a </Router> ``` -[!INCLUDE[](~/blazor/includes/prefer-exact-matches.md)] - ## Nested layouts A component can reference a layout that in turn references another layout. For example, nested layouts are used to create a multi-level menu structures. @@ -390,13 +386,13 @@ The following `DoctorWhoLayout` component is a modified version of the example s `Shared/DoctorWhoLayout.razor`: -:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Shared/layouts/DoctorWhoLayout2.razor" highlight="2,12"::: +:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Shared/layouts/DoctorWhoLayout2.razor" highlight="2,12"::: The `ProductionsLayout` component contains the top-level layout elements, where the header (`<header>...</header>`) and footer (`<footer>...</footer>`) elements now reside. The `DoctorWhoLayout` with the `Episodes` component is rendered where `@Body` appears. `Shared/ProductionsLayout.razor`: -:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Shared/layouts/ProductionsLayout.razor" highlight="13"::: +:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Shared/layouts/ProductionsLayout.razor" highlight="13"::: The following rendered HTML markup is produced by the preceding nested layout. Extraneous markup doesn't appear in order to focus on the nested content provided by the three components involved: @@ -456,7 +452,7 @@ When routable components are integrated into a Razor Pages app, the app's shared :::moniker-end -:::moniker range="< aspnetcore-5.0" +:::moniker range=">= aspnetcore-5.0 < aspnetcore-6.0" Some app elements, such as menus, copyright messages, and company logos, are usually part of app's overall presentation. Placing a copy of the markup for these elements into all of the components of an app isn't efficient. Every time that one of these elements is updated, every component that uses the element must be updated. This approach is costly to maintain and can lead to inconsistent content if an update is missed. *Layouts* solve these problems. @@ -479,7 +475,7 @@ The following `DoctorWhoLayout` component shows the Razor template of a layout c `Shared/DoctorWhoLayout.razor`: -:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Shared/layouts/DoctorWhoLayout.razor" highlight="1,13"::: +:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Shared/layouts/DoctorWhoLayout.razor" highlight="1,13"::: ### `MainLayout` component @@ -487,7 +483,14 @@ In an app created from a [Blazor project template](xref:blazor/project-structure `Shared/MainLayout.razor`: -:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Shared/layouts/MainLayout.razor"::: +:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Shared/layouts/MainLayout.razor"::: + +[Blazor's CSS isolation feature](xref:blazor/components/css-isolation) applies isolated CSS styles to the `MainLayout` component. By convention, the styles are provided by the accompanying stylesheet of the same name, `Shared/MainLayout.razor.css`. The ASP.NET Core framework implementation of the stylesheet is available for inspection in the ASP.NET Core reference source (`dotnet/aspnetcore` GitHub repository): + +* [Blazor Server `MainLayout.razor.css`](https://github.com/dotnet/aspnetcore/blob/main/src/ProjectTemplates/Web.ProjectTemplates/content/BlazorServerWeb-CSharp/Shared/MainLayout.razor.css) +* [Blazor WebAssembly `MainLayout.razor.css`](https://github.com/dotnet/aspnetcore/blob/main/src/ProjectTemplates/Web.ProjectTemplates/content/ComponentsWebAssembly-CSharp/Client/Shared/MainLayout.razor.css) + +[!INCLUDE[](~/includes/aspnetcore-repo-ref-source-links.md)] ## Apply a layout @@ -499,7 +502,7 @@ The content of the following `Episodes` component is inserted into the `DoctorWh `Pages/Episodes.razor`: -:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Pages/layouts/Episodes.razor" highlight="2"::: +:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Pages/layouts/Episodes.razor" highlight="2"::: The following rendered HTML markup is produced by the preceding `DoctorWhoLayout` and `Episodes` component. Extraneous markup doesn't appear in order to focus on the content provided by the two components involved: @@ -558,7 +561,8 @@ Specifying a layout in `_Imports.razor` overrides a layout specified as the rout > [!WARNING] > Do **not** add a Razor `@layout` directive to the root `_Imports.razor` file, which results in an infinite loop of layouts. To control the default app layout, specify the layout in the `Router` component. For more information, see the following [Apply a default layout to an app](#apply-a-default-layout-to-an-app) section. -The [`@layout`](xref:mvc/views/razor#layout) Razor directive only applies a layout to routable Razor components with an [`@page`](xref:mvc/views/razor#page) directive. +> [!NOTE] +> The [`@layout`](xref:mvc/views/razor#layout) Razor directive only applies a layout to routable Razor components with an [`@page`](xref:mvc/views/razor#page) directive. ### Apply a default layout to an app @@ -566,7 +570,9 @@ Specify the default app layout in the `App` component's <xref:Microsoft.AspNetCo `App.razor`: -:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/layouts/App1.razor" highlight="3"::: +:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/layouts/App1.razor" highlight="3"::: + +[!INCLUDE[](~/blazor/includes/prefer-exact-matches.md)] For more information on the <xref:Microsoft.AspNetCore.Components.Routing.Router> component, see <xref:blazor/fundamentals/routing>. @@ -592,6 +598,8 @@ To set a layout for arbitrary Razor template content, specify the layout with a </Router> ``` +[!INCLUDE[](~/blazor/includes/prefer-exact-matches.md)] + ## Nested layouts A component can reference a layout that in turn references another layout. For example, nested layouts are used to create a multi-level menu structures. @@ -602,13 +610,13 @@ The following `DoctorWhoLayout` component is a modified version of the example s `Shared/DoctorWhoLayout.razor`: -:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Shared/layouts/DoctorWhoLayout2.razor" highlight="2,12"::: +:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Shared/layouts/DoctorWhoLayout2.razor" highlight="2,12"::: The `ProductionsLayout` component contains the top-level layout elements, where the header (`<header>...</header>`) and footer (`<footer>...</footer>`) elements now reside. The `DoctorWhoLayout` with the `Episodes` component is rendered where `@Body` appears. `Shared/ProductionsLayout.razor`: -:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Shared/layouts/ProductionsLayout.razor" highlight="13"::: +:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Shared/layouts/ProductionsLayout.razor" highlight="13"::: The following rendered HTML markup is produced by the preceding nested layout. Extraneous markup doesn't appear in order to focus on the nested content provided by the three components involved: @@ -668,7 +676,7 @@ When routable components are integrated into a Razor Pages app, the app's shared :::moniker-end -:::moniker range=">= aspnetcore-7.0" +:::moniker range="< aspnetcore-5.0" Some app elements, such as menus, copyright messages, and company logos, are usually part of app's overall presentation. Placing a copy of the markup for these elements into all of the components of an app isn't efficient. Every time that one of these elements is updated, every component that uses the element must be updated. This approach is costly to maintain and can lead to inconsistent content if an update is missed. *Layouts* solve these problems. @@ -691,7 +699,7 @@ The following `DoctorWhoLayout` component shows the Razor template of a layout c `Shared/DoctorWhoLayout.razor`: -:::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Shared/layouts/DoctorWhoLayout.razor" highlight="1,13"::: +:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Shared/layouts/DoctorWhoLayout.razor" highlight="1,13"::: ### `MainLayout` component @@ -699,14 +707,7 @@ In an app created from a [Blazor project template](xref:blazor/project-structure `Shared/MainLayout.razor`: -:::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Shared/layouts/MainLayout.razor"::: - -[Blazor's CSS isolation feature](xref:blazor/components/css-isolation) applies isolated CSS styles to the `MainLayout` component. By convention, the styles are provided by the accompanying stylesheet of the same name, `Shared/MainLayout.razor.css`. The ASP.NET Core framework implementation of the stylesheet is available for inspection in the ASP.NET Core reference source (`dotnet/aspnetcore` GitHub repository): - -* [Blazor Server `MainLayout.razor.css`](https://github.com/dotnet/aspnetcore/blob/main/src/ProjectTemplates/Web.ProjectTemplates/content/BlazorServerWeb-CSharp/Shared/MainLayout.razor.css) -* [Blazor WebAssembly `MainLayout.razor.css`](https://github.com/dotnet/aspnetcore/blob/main/src/ProjectTemplates/Web.ProjectTemplates/content/ComponentsWebAssembly-CSharp/Client/Shared/MainLayout.razor.css) - -[!INCLUDE[](~/includes/aspnetcore-repo-ref-source-links.md)] +:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Shared/layouts/MainLayout.razor"::: ## Apply a layout @@ -718,7 +719,7 @@ The content of the following `Episodes` component is inserted into the `DoctorWh `Pages/Episodes.razor`: -:::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Pages/layouts/Episodes.razor" highlight="2"::: +:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Pages/layouts/Episodes.razor" highlight="2"::: The following rendered HTML markup is produced by the preceding `DoctorWhoLayout` and `Episodes` component. Extraneous markup doesn't appear in order to focus on the content provided by the two components involved: @@ -777,8 +778,7 @@ Specifying a layout in `_Imports.razor` overrides a layout specified as the rout > [!WARNING] > Do **not** add a Razor `@layout` directive to the root `_Imports.razor` file, which results in an infinite loop of layouts. To control the default app layout, specify the layout in the `Router` component. For more information, see the following [Apply a default layout to an app](#apply-a-default-layout-to-an-app) section. -> [!NOTE] -> The [`@layout`](xref:mvc/views/razor#layout) Razor directive only applies a layout to routable Razor components with an [`@page`](xref:mvc/views/razor#page) directive. +The [`@layout`](xref:mvc/views/razor#layout) Razor directive only applies a layout to routable Razor components with an [`@page`](xref:mvc/views/razor#page) directive. ### Apply a default layout to an app @@ -786,7 +786,7 @@ Specify the default app layout in the `App` component's <xref:Microsoft.AspNetCo `App.razor`: -:::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/layouts/App1.razor" highlight="3"::: +:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/layouts/App1.razor" highlight="3"::: For more information on the <xref:Microsoft.AspNetCore.Components.Routing.Router> component, see <xref:blazor/fundamentals/routing>. @@ -799,7 +799,7 @@ To set a layout for arbitrary Razor template content, specify the layout with a `App.razor`: ```razor -<Router AppAssembly="@typeof(App).Assembly"> +<Router AppAssembly="@typeof(Program).Assembly"> <Found Context="routeData"> <RouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)" /> </Found> @@ -822,13 +822,13 @@ The following `DoctorWhoLayout` component is a modified version of the example s `Shared/DoctorWhoLayout.razor`: -:::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Shared/layouts/DoctorWhoLayout2.razor" highlight="2,12"::: +:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Shared/layouts/DoctorWhoLayout2.razor" highlight="2,12"::: The `ProductionsLayout` component contains the top-level layout elements, where the header (`<header>...</header>`) and footer (`<footer>...</footer>`) elements now reside. The `DoctorWhoLayout` with the `Episodes` component is rendered where `@Body` appears. `Shared/ProductionsLayout.razor`: -:::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Shared/layouts/ProductionsLayout.razor" highlight="13"::: +:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Shared/layouts/ProductionsLayout.razor" highlight="13"::: The following rendered HTML markup is produced by the preceding nested layout. Extraneous markup doesn't appear in order to focus on the nested content provided by the three components involved: diff --git a/aspnetcore/blazor/components/lifecycle.md b/aspnetcore/blazor/components/lifecycle.md index 83ad97b74996..0ef9c28e7e24 100644 --- a/aspnetcore/blazor/components/lifecycle.md +++ b/aspnetcore/blazor/components/lifecycle.md @@ -5,14 +5,14 @@ description: Learn about the ASP.NET Core Razor component lifecycle and how to u monikerRange: '>= aspnetcore-3.1' ms.author: riande ms.custom: mvc -ms.date: 11/09/2021 +ms.date: 11/08/2022 uid: blazor/components/lifecycle --- # ASP.NET Core Razor component lifecycle This article explains the ASP.NET Core Razor component lifecycle and how to use lifecycle events. -:::moniker range=">= aspnetcore-6.0 < aspnetcore-7.0" +:::moniker range=">= aspnetcore-7.0" The Razor component processes Razor component lifecycle events in a set of synchronous and asynchronous lifecycle methods. The lifecycle methods can be overridden to perform additional operations in components during component initialization and rendering. @@ -77,7 +77,7 @@ Although [route parameter matching is case insensitive](xref:blazor/fundamentals `Pages/SetParamsAsync.razor`: -:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Pages/lifecycle/SetParamsAsync.razor"::: +:::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Pages/lifecycle/SetParamsAsync.razor"::: ## Component initialization (`OnInitialized{Async}`) @@ -87,7 +87,7 @@ For a synchronous operation, override <xref:Microsoft.AspNetCore.Components.Comp `Pages/OnInit.razor`: -:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Pages/lifecycle/OnInit.razor" highlight="8"::: +:::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Pages/lifecycle/OnInit.razor" highlight="8"::: To perform an asynchronous operation, override <xref:Microsoft.AspNetCore.Components.ComponentBase.OnInitializedAsync%2A> and use the [`await`](/dotnet/csharp/language-reference/operators/await) operator: @@ -132,7 +132,7 @@ For the following example component, navigate to the component's page at a URL: > [!NOTE] > In a component route, it isn't possible to both constrain a <xref:System.DateTime> parameter with the [route constraint `datetime`](xref:blazor/fundamentals/routing#route-constraints) and [make the parameter optional](xref:blazor/fundamentals/routing#route-parameters). Therefore, the following `OnParamsSet` component uses two [`@page`](xref:mvc/views/razor#page) directives to handle routing with and without a supplied date segment in the URL. -:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Pages/lifecycle/OnParamsSet.razor"::: +:::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Pages/lifecycle/OnParamsSet.razor"::: Asynchronous work when applying parameters and property values must occur during the <xref:Microsoft.AspNetCore.Components.ComponentBase.OnParametersSetAsync%2A> lifecycle event: @@ -158,7 +158,7 @@ The `firstRender` parameter for <xref:Microsoft.AspNetCore.Components.ComponentB `Pages/AfterRender.razor`: -:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Pages/lifecycle/AfterRender.razor"::: +:::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Pages/lifecycle/AfterRender.razor"::: Asynchronous work immediately after rendering must occur during the <xref:Microsoft.AspNetCore.Components.ComponentBase.OnAfterRenderAsync%2A> lifecycle event: @@ -174,7 +174,7 @@ protected override async Task OnAfterRenderAsync(bool firstRender) Even if you return a <xref:System.Threading.Tasks.Task> from <xref:Microsoft.AspNetCore.Components.ComponentBase.OnAfterRenderAsync%2A>, the framework doesn't schedule a further render cycle for your component once that task completes. This is to avoid an infinite render loop. This is different from the other lifecycle methods, which schedule a further render cycle once a returned <xref:System.Threading.Tasks.Task> completes. -<xref:Microsoft.AspNetCore.Components.ComponentBase.OnAfterRender%2A> and <xref:Microsoft.AspNetCore.Components.ComponentBase.OnAfterRenderAsync%2A> *aren't called during the prerendering process on the server*. The methods are called when the component is rendered and interactive after prerendering. When the app prerenders: +<xref:Microsoft.AspNetCore.Components.ComponentBase.OnAfterRender%2A> and <xref:Microsoft.AspNetCore.Components.ComponentBase.OnAfterRenderAsync%2A> *aren't called during the prerendering process on the server*. The methods are called when the component is rendered interactively after prerendering. When the app prerenders: 1. The component executes on the server to produce some static HTML markup in the HTTP response. During this phase, <xref:Microsoft.AspNetCore.Components.ComponentBase.OnAfterRender%2A> and <xref:Microsoft.AspNetCore.Components.ComponentBase.OnAfterRenderAsync%2A> aren't called. 1. When the Blazor script (`blazor.webassembly.js` or `blazor.server.js`) start in the browser, the component is restarted in an interactive rendering mode. After a component is restarted, <xref:Microsoft.AspNetCore.Components.ComponentBase.OnAfterRender%2A> and <xref:Microsoft.AspNetCore.Components.ComponentBase.OnAfterRenderAsync%2A> **are** called because the app isn't in the prerendering phase any longer. @@ -197,7 +197,7 @@ In the `FetchData` component of the Blazor templates, <xref:Microsoft.AspNetCore `Pages/FetchData.razor` in the Blazor Server template: -:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_Server/Pages/lifecycle/FetchData.razor" highlight="9,21,25"::: +:::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_Server/Pages/lifecycle/FetchData.razor" highlight="9,21,25"::: ## Handle errors @@ -216,7 +216,7 @@ The following code demonstrates an updated `WeatherForecastService` in a templat `WeatherForecastService.cs`: -:::code language="csharp" source="~/../blazor-samples/6.0/BlazorSample_Server/lifecycle/WeatherForecastService.cs"::: +:::code language="csharp" source="~/../blazor-samples/7.0/BlazorSample_Server/lifecycle/WeatherForecastService.cs"::: For more information on the <xref:Microsoft.AspNetCore.Mvc.TagHelpers.ComponentTagHelper.RenderMode>, see <xref:blazor/fundamentals/signalr#render-mode-blazor-server>. @@ -265,7 +265,7 @@ If a single object requires disposal, a lambda can be used to dispose of the obj `Pages/CounterWithTimerDisposal1.razor`: -:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Pages/lifecycle/CounterWithTimerDisposal1.razor" highlight="3,11,28"::: +:::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Pages/lifecycle/CounterWithTimerDisposal1.razor" highlight="3,11,28"::: > [!NOTE] > In the preceding example, the call to <xref:Microsoft.AspNetCore.Components.ComponentBase.StateHasChanged%2A> is wrapped by a call to <xref:Microsoft.AspNetCore.Components.ComponentBase.InvokeAsync%2A?displayProperty=nameWithType> because the callback is invoked outside of Blazor's synchronization context. For more information, see <xref:blazor/components/rendering#receiving-a-call-from-something-external-to-the-blazor-rendering-and-event-handling-system>. @@ -274,7 +274,7 @@ If the object is created in a lifecycle method, such as [`OnInitialized`/`OnInit `Pages/CounterWithTimerDisposal2.razor`: -:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Pages/lifecycle/CounterWithTimerDisposal2.razor" highlight="15,29"::: +:::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Pages/lifecycle/CounterWithTimerDisposal2.razor" highlight="15,29"::: For more information, see: @@ -468,7 +468,7 @@ In the following example: `Pages/BackgroundWork.razor`: -:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Pages/lifecycle/BackgroundWork.razor"::: +:::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Pages/lifecycle/BackgroundWork.razor"::: ## Blazor Server reconnection events @@ -476,7 +476,7 @@ The component lifecycle events covered in this article operate separately from [ :::moniker-end -:::moniker range=">= aspnetcore-5.0 < aspnetcore-6.0" +:::moniker range=">= aspnetcore-6.0 < aspnetcore-7.0" The Razor component processes Razor component lifecycle events in a set of synchronous and asynchronous lifecycle methods. The lifecycle methods can be overridden to perform additional operations in components during component initialization and rendering. @@ -541,7 +541,7 @@ Although [route parameter matching is case insensitive](xref:blazor/fundamentals `Pages/SetParamsAsync.razor`: -:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Pages/lifecycle/SetParamsAsync.razor"::: +:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Pages/lifecycle/SetParamsAsync.razor"::: ## Component initialization (`OnInitialized{Async}`) @@ -551,7 +551,7 @@ For a synchronous operation, override <xref:Microsoft.AspNetCore.Components.Comp `Pages/OnInit.razor`: -:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Pages/lifecycle/OnInit.razor" highlight="8"::: +:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Pages/lifecycle/OnInit.razor" highlight="8"::: To perform an asynchronous operation, override <xref:Microsoft.AspNetCore.Components.ComponentBase.OnInitializedAsync%2A> and use the [`await`](/dotnet/csharp/language-reference/operators/await) operator: @@ -596,7 +596,7 @@ For the following example component, navigate to the component's page at a URL: > [!NOTE] > In a component route, it isn't possible to both constrain a <xref:System.DateTime> parameter with the [route constraint `datetime`](xref:blazor/fundamentals/routing#route-constraints) and [make the parameter optional](xref:blazor/fundamentals/routing#route-parameters). Therefore, the following `OnParamsSet` component uses two [`@page`](xref:mvc/views/razor#page) directives to handle routing with and without a supplied date segment in the URL. -:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Pages/lifecycle/OnParamsSet.razor"::: +:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Pages/lifecycle/OnParamsSet.razor"::: Asynchronous work when applying parameters and property values must occur during the <xref:Microsoft.AspNetCore.Components.ComponentBase.OnParametersSetAsync%2A> lifecycle event: @@ -622,7 +622,7 @@ The `firstRender` parameter for <xref:Microsoft.AspNetCore.Components.ComponentB `Pages/AfterRender.razor`: -:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Pages/lifecycle/AfterRender.razor"::: +:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Pages/lifecycle/AfterRender.razor"::: Asynchronous work immediately after rendering must occur during the <xref:Microsoft.AspNetCore.Components.ComponentBase.OnAfterRenderAsync%2A> lifecycle event: @@ -661,7 +661,7 @@ In the `FetchData` component of the Blazor templates, <xref:Microsoft.AspNetCore `Pages/FetchData.razor` in the Blazor Server template: -:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_Server/Pages/lifecycle/FetchData.razor" highlight="9,21,25"::: +:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_Server/Pages/lifecycle/FetchData.razor" highlight="9,21,25"::: ## Handle errors @@ -680,7 +680,7 @@ The following code demonstrates an updated `WeatherForecastService` in a templat `WeatherForecastService.cs`: -:::code language="csharp" source="~/../blazor-samples/5.0/BlazorSample_Server/lifecycle/WeatherForecastService.cs"::: +:::code language="csharp" source="~/../blazor-samples/6.0/BlazorSample_Server/lifecycle/WeatherForecastService.cs"::: For more information on the <xref:Microsoft.AspNetCore.Mvc.TagHelpers.ComponentTagHelper.RenderMode>, see <xref:blazor/fundamentals/signalr#render-mode-blazor-server>. @@ -729,7 +729,7 @@ If a single object requires disposal, a lambda can be used to dispose of the obj `Pages/CounterWithTimerDisposal1.razor`: -:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Pages/lifecycle/CounterWithTimerDisposal1.razor" highlight="3,11,28"::: +:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Pages/lifecycle/CounterWithTimerDisposal1.razor" highlight="3,11,28"::: > [!NOTE] > In the preceding example, the call to <xref:Microsoft.AspNetCore.Components.ComponentBase.StateHasChanged%2A> is wrapped by a call to <xref:Microsoft.AspNetCore.Components.ComponentBase.InvokeAsync%2A?displayProperty=nameWithType> because the callback is invoked outside of Blazor's synchronization context. For more information, see <xref:blazor/components/rendering#receiving-a-call-from-something-external-to-the-blazor-rendering-and-event-handling-system>. @@ -738,7 +738,7 @@ If the object is created in a lifecycle method, such as [`OnInitialized`/`OnInit `Pages/CounterWithTimerDisposal2.razor`: -:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Pages/lifecycle/CounterWithTimerDisposal2.razor" highlight="15,29"::: +:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Pages/lifecycle/CounterWithTimerDisposal2.razor" highlight="15,29"::: For more information, see: @@ -807,8 +807,9 @@ Always unsubscribe event handlers from .NET events. The following [Blazor form]( </EditForm> @code { - // ... - private EventHandler<FieldChangedEventArgs> fieldChanged; + ... + + private EventHandler<FieldChangedEventArgs>? fieldChanged; protected override void OnInitialized() { @@ -816,7 +817,7 @@ Always unsubscribe event handlers from .NET events. The following [Blazor form]( fieldChanged = (_, __) => { - // ... + ... }; editContext.OnFieldChanged += fieldChanged; @@ -840,7 +841,7 @@ Always unsubscribe event handlers from .NET events. The following [Blazor form]( </EditForm> @code { - // ... + ... protected override void OnInitialized() { @@ -850,7 +851,7 @@ Always unsubscribe event handlers from .NET events. The following [Blazor form]( private void HandleFieldChanged(object sender, FieldChangedEventArgs e) { - // ... + ... } public void Dispose() @@ -885,10 +886,10 @@ When [anonymous functions](/dotnet/csharp/programming-guide/statements-expressio * Anonymous lambda expression approach (explicit disposal not required): ```csharp - private ValidationMessageStore messageStore; + private ValidationMessageStore? messageStore; [CascadingParameter] - private EditContext CurrentEditContext { get; set; } + private EditContext? CurrentEditContext { get; set; } protected override void OnInitialized() { @@ -931,7 +932,7 @@ In the following example: `Pages/BackgroundWork.razor`: -:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Pages/lifecycle/BackgroundWork.razor"::: +:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Pages/lifecycle/BackgroundWork.razor"::: ## Blazor Server reconnection events @@ -939,7 +940,7 @@ The component lifecycle events covered in this article operate separately from [ :::moniker-end -:::moniker range="< aspnetcore-5.0" +:::moniker range=">= aspnetcore-5.0 < aspnetcore-6.0" The Razor component processes Razor component lifecycle events in a set of synchronous and asynchronous lifecycle methods. The lifecycle methods can be overridden to perform additional operations in components during component initialization and rendering. @@ -1004,7 +1005,7 @@ Although [route parameter matching is case insensitive](xref:blazor/fundamentals `Pages/SetParamsAsync.razor`: -:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Pages/lifecycle/SetParamsAsync.razor"::: +:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Pages/lifecycle/SetParamsAsync.razor"::: ## Component initialization (`OnInitialized{Async}`) @@ -1014,7 +1015,7 @@ For a synchronous operation, override <xref:Microsoft.AspNetCore.Components.Comp `Pages/OnInit.razor`: -:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Pages/lifecycle/OnInit.razor" highlight="8"::: +:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Pages/lifecycle/OnInit.razor" highlight="8"::: To perform an asynchronous operation, override <xref:Microsoft.AspNetCore.Components.ComponentBase.OnInitializedAsync%2A> and use the [`await`](/dotnet/csharp/language-reference/operators/await) operator: @@ -1056,7 +1057,10 @@ For the following example component, navigate to the component's page at a URL: `Pages/OnParamsSet.razor`: -:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Pages/lifecycle/OnParamsSet.razor"::: +> [!NOTE] +> In a component route, it isn't possible to both constrain a <xref:System.DateTime> parameter with the [route constraint `datetime`](xref:blazor/fundamentals/routing#route-constraints) and [make the parameter optional](xref:blazor/fundamentals/routing#route-parameters). Therefore, the following `OnParamsSet` component uses two [`@page`](xref:mvc/views/razor#page) directives to handle routing with and without a supplied date segment in the URL. + +:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Pages/lifecycle/OnParamsSet.razor"::: Asynchronous work when applying parameters and property values must occur during the <xref:Microsoft.AspNetCore.Components.ComponentBase.OnParametersSetAsync%2A> lifecycle event: @@ -1082,7 +1086,7 @@ The `firstRender` parameter for <xref:Microsoft.AspNetCore.Components.ComponentB `Pages/AfterRender.razor`: -:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Pages/lifecycle/AfterRender.razor"::: +:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Pages/lifecycle/AfterRender.razor"::: Asynchronous work immediately after rendering must occur during the <xref:Microsoft.AspNetCore.Components.ComponentBase.OnAfterRenderAsync%2A> lifecycle event: @@ -1121,7 +1125,7 @@ In the `FetchData` component of the Blazor templates, <xref:Microsoft.AspNetCore `Pages/FetchData.razor` in the Blazor Server template: -:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_Server/Pages/lifecycle/FetchData.razor" highlight="9,21,25"::: +:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_Server/Pages/lifecycle/FetchData.razor" highlight="9,21,25"::: ## Handle errors @@ -1140,7 +1144,7 @@ The following code demonstrates an updated `WeatherForecastService` in a templat `WeatherForecastService.cs`: -:::code language="csharp" source="~/../blazor-samples/3.1/BlazorSample_Server/lifecycle/WeatherForecastService.cs"::: +:::code language="csharp" source="~/../blazor-samples/5.0/BlazorSample_Server/lifecycle/WeatherForecastService.cs"::: For more information on the <xref:Microsoft.AspNetCore.Mvc.TagHelpers.ComponentTagHelper.RenderMode>, see <xref:blazor/fundamentals/signalr#render-mode-blazor-server>. @@ -1189,7 +1193,7 @@ If a single object requires disposal, a lambda can be used to dispose of the obj `Pages/CounterWithTimerDisposal1.razor`: -:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Pages/lifecycle/CounterWithTimerDisposal1.razor" highlight="3,11,28"::: +:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Pages/lifecycle/CounterWithTimerDisposal1.razor" highlight="3,11,28"::: > [!NOTE] > In the preceding example, the call to <xref:Microsoft.AspNetCore.Components.ComponentBase.StateHasChanged%2A> is wrapped by a call to <xref:Microsoft.AspNetCore.Components.ComponentBase.InvokeAsync%2A?displayProperty=nameWithType> because the callback is invoked outside of Blazor's synchronization context. For more information, see <xref:blazor/components/rendering#receiving-a-call-from-something-external-to-the-blazor-rendering-and-event-handling-system>. @@ -1198,7 +1202,7 @@ If the object is created in a lifecycle method, such as [`OnInitialized`/`OnInit `Pages/CounterWithTimerDisposal2.razor`: -:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Pages/lifecycle/CounterWithTimerDisposal2.razor" highlight="15,29"::: +:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Pages/lifecycle/CounterWithTimerDisposal2.razor" highlight="15,29"::: For more information, see: @@ -1272,7 +1276,7 @@ Always unsubscribe event handlers from .NET events. The following [Blazor form]( protected override void OnInitialized() { - editContext = new EditContext(model); + editContext = new(model); fieldChanged = (_, __) => { @@ -1304,7 +1308,7 @@ Always unsubscribe event handlers from .NET events. The following [Blazor form]( protected override void OnInitialized() { - editContext = new EditContext(model); + editContext = new(model); editContext.OnFieldChanged += HandleFieldChanged; } @@ -1337,7 +1341,7 @@ When [anonymous functions](/dotnet/csharp/programming-guide/statements-expressio protected override void OnInitialized() { - editContext = new EditContext(starship); + editContext = new(starship); editContext.OnFieldChanged += (s, e) => HandleFieldChanged((editContext)s, e); } ``` @@ -1354,7 +1358,7 @@ When [anonymous functions](/dotnet/csharp/programming-guide/statements-expressio { ... - messageStore = new ValidationMessageStore(CurrentEditContext); + messageStore = new(CurrentEditContext); CurrentEditContext.OnValidationRequested += (s, e) => messageStore.Clear(); CurrentEditContext.OnFieldChanged += (s, e) => @@ -1391,7 +1395,7 @@ In the following example: `Pages/BackgroundWork.razor`: -:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Pages/lifecycle/BackgroundWork.razor"::: +:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Pages/lifecycle/BackgroundWork.razor"::: ## Blazor Server reconnection events @@ -1399,7 +1403,7 @@ The component lifecycle events covered in this article operate separately from [ :::moniker-end -:::moniker range=">= aspnetcore-7.0" +:::moniker range="< aspnetcore-5.0" The Razor component processes Razor component lifecycle events in a set of synchronous and asynchronous lifecycle methods. The lifecycle methods can be overridden to perform additional operations in components during component initialization and rendering. @@ -1464,7 +1468,7 @@ Although [route parameter matching is case insensitive](xref:blazor/fundamentals `Pages/SetParamsAsync.razor`: -:::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Pages/lifecycle/SetParamsAsync.razor"::: +:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Pages/lifecycle/SetParamsAsync.razor"::: ## Component initialization (`OnInitialized{Async}`) @@ -1474,7 +1478,7 @@ For a synchronous operation, override <xref:Microsoft.AspNetCore.Components.Comp `Pages/OnInit.razor`: -:::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Pages/lifecycle/OnInit.razor" highlight="8"::: +:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Pages/lifecycle/OnInit.razor" highlight="8"::: To perform an asynchronous operation, override <xref:Microsoft.AspNetCore.Components.ComponentBase.OnInitializedAsync%2A> and use the [`await`](/dotnet/csharp/language-reference/operators/await) operator: @@ -1516,10 +1520,7 @@ For the following example component, navigate to the component's page at a URL: `Pages/OnParamsSet.razor`: -> [!NOTE] -> In a component route, it isn't possible to both constrain a <xref:System.DateTime> parameter with the [route constraint `datetime`](xref:blazor/fundamentals/routing#route-constraints) and [make the parameter optional](xref:blazor/fundamentals/routing#route-parameters). Therefore, the following `OnParamsSet` component uses two [`@page`](xref:mvc/views/razor#page) directives to handle routing with and without a supplied date segment in the URL. - -:::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Pages/lifecycle/OnParamsSet.razor"::: +:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Pages/lifecycle/OnParamsSet.razor"::: Asynchronous work when applying parameters and property values must occur during the <xref:Microsoft.AspNetCore.Components.ComponentBase.OnParametersSetAsync%2A> lifecycle event: @@ -1545,7 +1546,7 @@ The `firstRender` parameter for <xref:Microsoft.AspNetCore.Components.ComponentB `Pages/AfterRender.razor`: -:::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Pages/lifecycle/AfterRender.razor"::: +:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Pages/lifecycle/AfterRender.razor"::: Asynchronous work immediately after rendering must occur during the <xref:Microsoft.AspNetCore.Components.ComponentBase.OnAfterRenderAsync%2A> lifecycle event: @@ -1561,7 +1562,7 @@ protected override async Task OnAfterRenderAsync(bool firstRender) Even if you return a <xref:System.Threading.Tasks.Task> from <xref:Microsoft.AspNetCore.Components.ComponentBase.OnAfterRenderAsync%2A>, the framework doesn't schedule a further render cycle for your component once that task completes. This is to avoid an infinite render loop. This is different from the other lifecycle methods, which schedule a further render cycle once a returned <xref:System.Threading.Tasks.Task> completes. -<xref:Microsoft.AspNetCore.Components.ComponentBase.OnAfterRender%2A> and <xref:Microsoft.AspNetCore.Components.ComponentBase.OnAfterRenderAsync%2A> *aren't called during the prerendering process on the server*. The methods are called when the component is rendered interactively after prerendering. When the app prerenders: +<xref:Microsoft.AspNetCore.Components.ComponentBase.OnAfterRender%2A> and <xref:Microsoft.AspNetCore.Components.ComponentBase.OnAfterRenderAsync%2A> *aren't called during the prerendering process on the server*. The methods are called when the component is rendered and interactive after prerendering. When the app prerenders: 1. The component executes on the server to produce some static HTML markup in the HTTP response. During this phase, <xref:Microsoft.AspNetCore.Components.ComponentBase.OnAfterRender%2A> and <xref:Microsoft.AspNetCore.Components.ComponentBase.OnAfterRenderAsync%2A> aren't called. 1. When the Blazor script (`blazor.webassembly.js` or `blazor.server.js`) start in the browser, the component is restarted in an interactive rendering mode. After a component is restarted, <xref:Microsoft.AspNetCore.Components.ComponentBase.OnAfterRender%2A> and <xref:Microsoft.AspNetCore.Components.ComponentBase.OnAfterRenderAsync%2A> **are** called because the app isn't in the prerendering phase any longer. @@ -1584,7 +1585,7 @@ In the `FetchData` component of the Blazor templates, <xref:Microsoft.AspNetCore `Pages/FetchData.razor` in the Blazor Server template: -:::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_Server/Pages/lifecycle/FetchData.razor" highlight="9,21,25"::: +:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_Server/Pages/lifecycle/FetchData.razor" highlight="9,21,25"::: ## Handle errors @@ -1603,7 +1604,7 @@ The following code demonstrates an updated `WeatherForecastService` in a templat `WeatherForecastService.cs`: -:::code language="csharp" source="~/../blazor-samples/7.0/BlazorSample_Server/lifecycle/WeatherForecastService.cs"::: +:::code language="csharp" source="~/../blazor-samples/3.1/BlazorSample_Server/lifecycle/WeatherForecastService.cs"::: For more information on the <xref:Microsoft.AspNetCore.Mvc.TagHelpers.ComponentTagHelper.RenderMode>, see <xref:blazor/fundamentals/signalr#render-mode-blazor-server>. @@ -1652,7 +1653,7 @@ If a single object requires disposal, a lambda can be used to dispose of the obj `Pages/CounterWithTimerDisposal1.razor`: -:::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Pages/lifecycle/CounterWithTimerDisposal1.razor" highlight="3,11,28"::: +:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Pages/lifecycle/CounterWithTimerDisposal1.razor" highlight="3,11,28"::: > [!NOTE] > In the preceding example, the call to <xref:Microsoft.AspNetCore.Components.ComponentBase.StateHasChanged%2A> is wrapped by a call to <xref:Microsoft.AspNetCore.Components.ComponentBase.InvokeAsync%2A?displayProperty=nameWithType> because the callback is invoked outside of Blazor's synchronization context. For more information, see <xref:blazor/components/rendering#receiving-a-call-from-something-external-to-the-blazor-rendering-and-event-handling-system>. @@ -1661,7 +1662,7 @@ If the object is created in a lifecycle method, such as [`OnInitialized`/`OnInit `Pages/CounterWithTimerDisposal2.razor`: -:::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Pages/lifecycle/CounterWithTimerDisposal2.razor" highlight="15,29"::: +:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Pages/lifecycle/CounterWithTimerDisposal2.razor" highlight="15,29"::: For more information, see: @@ -1730,17 +1731,16 @@ Always unsubscribe event handlers from .NET events. The following [Blazor form]( </EditForm> @code { - ... - - private EventHandler<FieldChangedEventArgs>? fieldChanged; + // ... + private EventHandler<FieldChangedEventArgs> fieldChanged; protected override void OnInitialized() { - editContext = new(model); + editContext = new EditContext(model); fieldChanged = (_, __) => { - ... + // ... }; editContext.OnFieldChanged += fieldChanged; @@ -1764,17 +1764,17 @@ Always unsubscribe event handlers from .NET events. The following [Blazor form]( </EditForm> @code { - ... + // ... protected override void OnInitialized() { - editContext = new(model); + editContext = new EditContext(model); editContext.OnFieldChanged += HandleFieldChanged; } private void HandleFieldChanged(object sender, FieldChangedEventArgs e) { - ... + // ... } public void Dispose() @@ -1801,7 +1801,7 @@ When [anonymous functions](/dotnet/csharp/programming-guide/statements-expressio protected override void OnInitialized() { - editContext = new(starship); + editContext = new EditContext(starship); editContext.OnFieldChanged += (s, e) => HandleFieldChanged((editContext)s, e); } ``` @@ -1809,16 +1809,16 @@ When [anonymous functions](/dotnet/csharp/programming-guide/statements-expressio * Anonymous lambda expression approach (explicit disposal not required): ```csharp - private ValidationMessageStore? messageStore; + private ValidationMessageStore messageStore; [CascadingParameter] - private EditContext? CurrentEditContext { get; set; } + private EditContext CurrentEditContext { get; set; } protected override void OnInitialized() { ... - messageStore = new(CurrentEditContext); + messageStore = new ValidationMessageStore(CurrentEditContext); CurrentEditContext.OnValidationRequested += (s, e) => messageStore.Clear(); CurrentEditContext.OnFieldChanged += (s, e) => @@ -1855,7 +1855,7 @@ In the following example: `Pages/BackgroundWork.razor`: -:::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Pages/lifecycle/BackgroundWork.razor"::: +:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Pages/lifecycle/BackgroundWork.razor"::: ## Blazor Server reconnection events diff --git a/aspnetcore/blazor/components/prerendering-and-integration.md b/aspnetcore/blazor/components/prerendering-and-integration.md index c3d78b503ad8..bf2c0174590b 100644 --- a/aspnetcore/blazor/components/prerendering-and-integration.md +++ b/aspnetcore/blazor/components/prerendering-and-integration.md @@ -5,7 +5,7 @@ description: Learn about Razor component integration scenarios for Blazor apps, monikerRange: '>= aspnetcore-3.1' ms.author: riande ms.custom: mvc -ms.date: 11/09/2021 +ms.date: 11/08/2022 uid: blazor/components/prerendering-and-integration zone_pivot_groups: blazor-hosting-models --- @@ -13,7 +13,7 @@ zone_pivot_groups: blazor-hosting-models This article explains Razor component integration scenarios for Blazor apps, including prerendering of Razor components on the server. -:::moniker range=">= aspnetcore-6.0 < aspnetcore-7.0" +:::moniker range=">= aspnetcore-7.0" :::zone pivot="webassembly" @@ -29,10 +29,10 @@ To set up prerendering for a hosted Blazor WebAssembly app: 1. Host the Blazor WebAssembly app in an ASP.NET Core app. A standalone Blazor WebAssembly app can be added to an ASP.NET Core solution, or you can use a hosted Blazor WebAssembly app created from the [Blazor WebAssembly project template](xref:blazor/tooling) with the hosted option: - * Visual Studio: In the **Additional information** dialog, select the **ASP.NET Core hosted** checkbox when creating the Blazor WebAssembly app. In this article's examples, the solution is named `BlazorHosted`. + * Visual Studio: In the **Additional information** dialog, select the **ASP.NET Core Hosted** checkbox when creating the Blazor WebAssembly app. In this article's examples, the solution is named `BlazorHosted`. * Visual Studio Code/.NET CLI command shell: `dotnet new blazorwasm -ho` (use the `-ho|--hosted` option). Use the `-o|--output {LOCATION}` option to create a folder for the solution and set the solution's project namespaces. In this article's examples, the solution is named `BlazorHosted` (`dotnet new blazorwasm -ho -o BlazorHosted`). - For the examples in this article, the client project's namespace is `BlazorHosted.Client`, and the server project's namespace is `BlazorHosted.Server`. + For the examples in this article, the hosted solution's name (assembly name) is `BlazorHosted`. The client project's namespace is `BlazorHosted.Client`, and the server project's namespace is `BlazorHosted.Server`. 1. **Delete** the `wwwroot/index.html` file from the Blazor WebAssembly **:::no-loc text="Client":::** project. @@ -43,14 +43,11 @@ To set up prerendering for a hosted Blazor WebAssembly app: - builder.RootComponents.Add<HeadOutlet>("head::after"); ``` -1. Add `_Host.cshtml` and `_Layout.cshtml` files to the **:::no-loc text="Server":::** project's `Pages` folder. You can obtain the files from a project created from the Blazor Server template using Visual Studio or using the .NET CLI with the `dotnet new blazorserver -o BlazorServer` command in a command shell (the `-o BlazorServer` option creates a folder for the project). After placing the files into the **:::no-loc text="Server":::** project's `Pages` folder, make the following changes to the files. - - > [!IMPORTANT] - > The use of a layout page (`_Layout.cshtml`) with a [Component Tag Helper](xref:mvc/views/tag-helpers/builtin-th/component-tag-helper) for a <xref:Microsoft.AspNetCore.Components.Web.HeadOutlet> component is required to control `<head>` content, such as the page's title (<xref:Microsoft.AspNetCore.Components.Web.PageTitle> component) and other head elements (<xref:Microsoft.AspNetCore.Components.Web.HeadContent> component). For more information, see <xref:blazor/components/control-head-content#control-head-content-during-prerendering>. +1. Add `_Host.cshtml` file to the **:::no-loc text="Server":::** project's `Pages` folder. You can obtain the files from a project created from the Blazor Server template using Visual Studio or using the .NET CLI with the `dotnet new blazorserver -o BlazorServer` command in a command shell (the `-o BlazorServer` option creates a folder for the project). After placing the files into the **:::no-loc text="Server":::** project's `Pages` folder, make the following changes to the files. - Make the following changes to the `_Layout.cshtml` file: + Make the following changes to the `_Host.cshtml` file: - * Update the `Pages` namespace at the top of the file to match the namespace of the **:::no-loc text="Server":::** app's pages. The `{APP NAMESPACE}` placeholder in the following example represents the namespace of the donor app's pages that provided the `_Layout.cshtml` file: + * Update the `Pages` namespace at the top of the file to match the namespace of the **:::no-loc text="Server":::** app's pages. The `{APP NAMESPACE}` placeholder in the following example represents the namespace of the donor app's pages that provided the `_Host.cshtml` file: Delete: @@ -70,7 +67,7 @@ To set up prerendering for a hosted Blazor WebAssembly app: @using BlazorHosted.Client ``` - * Update the stylesheet links to point to the WebAssembly project's stylesheets. In the following example, the client project's namespace is `BlazorHosted.Client`. The `{APP NAMESPACE}` placeholder represents the namespace of the donor app that provided the `_Layout.cshtml` file. Update the Component Tag Helper (`<component>` tag) for the `HeadOutlet` component to prerender the component. + * Update the stylesheet links to point to the WebAssembly project's stylesheets. In the following example, the client project's namespace is `BlazorHosted.Client`. The `{APP NAMESPACE}` placeholder represents the namespace of the donor app that provided the `_Host.cshtml` file. Update the Component Tag Helper (`<component>` tag) for the `HeadOutlet` component to prerender the component. Delete: @@ -105,22 +102,6 @@ To set up prerendering for a hosted Blazor WebAssembly app: <script src="_framework/blazor.webassembly.js"></script> ``` - In the `_Host.cshtml` file: - - * Change the `Pages` namespace to that of the **:::no-loc text="Client":::** project. The `{APP NAMESPACE}` placeholder represents the namespace of the donor app's pages that provided the `_Host.cshtml` file: - - Delete: - - ```diff - - @namespace {APP NAMESPACE}.Pages - ``` - - Add: - - ```razor - @namespace BlazorHosted.Client - ``` - * Update the `render-mode` of the [Component Tag Helper](xref:mvc/views/tag-helpers/builtin-th/component-tag-helper) to prerender the root `App` component with <xref:Microsoft.AspNetCore.Mvc.Rendering.RenderMode.WebAssemblyPrerendered>: Delete: @@ -138,7 +119,7 @@ To set up prerendering for a hosted Blazor WebAssembly app: > [!IMPORTANT] > Prerendering isn't supported for authentication endpoints (`/authentication/` path segment). For more information, see <xref:blazor/security/webassembly/additional-scenarios#support-prerendering-with-authentication>. -1. In endpoint mapping of the **:::no-loc text="Server":::** project in `Program.cs`, change the fallback from the `index.html` file to the `_Host.cshtml` page: +1. In the `Program.cs` file of the **:::no-loc text="Server":::** project, change the fallback endpoint from the `index.html` file to the `_Host.cshtml` page: Delete: @@ -176,9 +157,6 @@ MVC: * `Views/_ViewImports.cshtml` * `Views/_ViewStart.cshtml` -> [!IMPORTANT] -> The use of a layout page (`_Layout.cshtml`) with a [Component Tag Helper](xref:mvc/views/tag-helpers/builtin-th/component-tag-helper) for a <xref:Microsoft.AspNetCore.Components.Web.HeadOutlet> component is required to control `<head>` content, such as the page's title (<xref:Microsoft.AspNetCore.Components.Web.PageTitle> component) and other head elements (<xref:Microsoft.AspNetCore.Components.Web.HeadContent> component). For more information, see <xref:blazor/components/control-head-content#control-head-content-during-prerendering>. - The preceding files can be obtained by generating an app from the ASP.NET Core project templates using: * Visual Studio's new project creation tools. @@ -307,7 +285,7 @@ Import static assets to the **:::no-loc text="Server":::** project from the dono If the donor project is created from an ASP.NET Core project template and the files aren't modified, you can copy the entire `wwwroot` folder from the donor project into the **:::no-loc text="Server":::** project and remove the :::no-loc text="favicon"::: icon file. > [!WARNING] -> Avoid placing the static asset into both the **:::no-loc text="Client":::** and **:::no-loc text="Server":::** `wwwroot` folders. If the same file is present in both folders, an exception is thrown because the static asset in each folder shares the same web root path. Therefore, host a static asset in either `wwwroot` folder, not both. +> Avoid placing the static asset into both the **:::no-loc text="Client":::** and **:::no-loc text="Server":::** `wwwroot` folders. If the same file is present in both folders, an exception is thrown because the static assets share the same web root path. Therefore, host a static asset in either of the `wwwroot` folders, not both. After adopting the preceding configuration, embed Razor components into pages or views of the **:::no-loc text="Server":::** project. Use the guidance in the following sections of this article: @@ -421,34 +399,46 @@ Prerendering can improve [Search Engine Optimization (SEO)](https://developer.mo After [configuring the project](#configuration), use the guidance in the following sections depending on the project's requirements: -* Routable components: For components that are directly routable from user requests. Follow this guidance when visitors should be able to make an HTTP request in their browser for a component with an [`@page`](xref:mvc/views/razor#page) directive. +* For components that are directly routable from user requests. Follow this guidance when visitors should be able to make an HTTP request in their browser for a component with an [`@page`](xref:mvc/views/razor#page) directive. * [Use routable components in a Razor Pages app](#use-routable-components-in-a-razor-pages-app) * [Use routable components in an MVC app](#use-routable-components-in-an-mvc-app) -* [Render components from a page or view](#render-components-from-a-page-or-view): For components that aren't directly routable from user requests. Follow this guidance when the app embeds components into existing pages and views with the [Component Tag Helper](xref:mvc/views/tag-helpers/builtin-th/component-tag-helper). +* For components that aren't directly routable from user requests, see the [Render components from a page or view](#render-components-from-a-page-or-view) section. Follow this guidance when the app embeds components into existing pages and views with the [Component Tag Helper](xref:mvc/views/tag-helpers/builtin-th/component-tag-helper). ## Configuration Use the following guidance to integrate Razor components into pages and views of an existing Razor Pages or MVC app. -> [!IMPORTANT] -> The use of a layout page (`_Layout.cshtml`) with a [Component Tag Helper](xref:mvc/views/tag-helpers/builtin-th/component-tag-helper) for a <xref:Microsoft.AspNetCore.Components.Web.HeadOutlet> component is required to control `<head>` content, such as the page's title (<xref:Microsoft.AspNetCore.Components.Web.PageTitle> component) and other head elements (<xref:Microsoft.AspNetCore.Components.Web.HeadContent> component). For more information, see <xref:blazor/components/control-head-content#control-head-content-during-prerendering>. +1. Add an imports file to the root folder of the project with the following content. Change the `{APP NAMESPACE}` placeholder to the namespace of the project. -1. In the project's layout file: + `_Imports.razor`: - * Add the following `<base>` tag and <xref:Microsoft.AspNetCore.Components.Web.HeadOutlet> component Tag Helper to the `<head>` element in `Pages/Shared/_Layout.cshtml` (Razor Pages) or `Views/Shared/_Layout.cshtml` (MVC): + ```razor + @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 + @using {APP NAMESPACE} + ``` - ```html +1. In the project's layout file (`Pages/Shared/_Layout.cshtml` in Razor Pages apps or `Views/Shared/_Layout.cshtml` in MVC apps): + + * Add the following `<base>` tag and <xref:Microsoft.AspNetCore.Components.Web.HeadOutlet> component Tag Helper to the `<head>` element: + + ```cshtml <base href="~/" /> - <component type="typeof(HeadOutlet)" render-mode="ServerPrerendered" /> + <component type="typeof(Microsoft.AspNetCore.Components.Web.HeadOutlet)" + render-mode="ServerPrerendered" /> ``` The `href` value (the *app base path*) in the preceding example assumes that the app resides at the root URL path (`/`). If the app is a sub-application, follow the guidance in the *App base path* section of the <xref:blazor/host-and-deploy/index#app-base-path> article. - - The <xref:Microsoft.AspNetCore.Components.Web.HeadOutlet> component is used to render head (`<head>`) content for page titles (<xref:Microsoft.AspNetCore.Components.Web.PageTitle> component) and other head elements (<xref:Microsoft.AspNetCore.Components.Web.HeadContent> component) set by Razor components. For more information, see <xref:blazor/components/control-head-content>. - * Add a `<script>` tag for the `blazor.server.js` script immediately before the `Scripts` render section (`@await RenderSectionAsync(...)`) in the app's layout. + The <xref:Microsoft.AspNetCore.Components.Web.HeadOutlet> component is used to render head (`<head>`) content for page titles (<xref:Microsoft.AspNetCore.Components.Web.PageTitle> component) and other head elements (<xref:Microsoft.AspNetCore.Components.Web.HeadContent> component) set by Razor components. For more information, see <xref:blazor/components/control-head-content>. - `Pages/Shared/_Layout.cshtml` (Razor Pages) or `Views/Shared/_Layout.cshtml` (MVC): + * Add a `<script>` tag for the `blazor.server.js` script immediately before the `Scripts` render section (`@await RenderSectionAsync(...)`): ```html <script src="_framework/blazor.server.js"></script> @@ -456,21 +446,8 @@ Use the following guidance to integrate Razor components into pages and views of The framework adds the `blazor.server.js` script to the app. There's no need to manually add a `blazor.server.js` script file to the app. -1. Add an imports file to the root folder of the project with the following content. Change the `{APP NAMESPACE}` placeholder to the namespace of the project. - - `_Imports.razor`: - - ```razor - @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 - @using {APP NAMESPACE} - ``` + > [!NOTE] + > Typically, the layout loads via a `_ViewStart.cshtml` file. 1. Register the Blazor Server services in `Program.cs` where services are registered: @@ -478,9 +455,7 @@ Use the following guidance to integrate Razor components into pages and views of builder.Services.AddServerSideBlazor(); ``` -1. Add the Blazor Hub endpoint to the endpoints of `Program.cs` where routes are mapped. - - Place the following line after the call to `MapRazorPages` (Razor Pages) or `MapControllerRoute` (MVC): +1. Add the Blazor Hub endpoint to the endpoints of `Program.cs` where routes are mapped. Place the following line after the call to `MapRazorPages` (Razor Pages) or `MapControllerRoute` (MVC): ```csharp app.MapBlazorHub(); @@ -567,25 +542,19 @@ To support routable Razor components in Razor Pages apps: </Router> ``` -1. Add a `_Host` page to the project with the following content. +1. Add a `_Host` page to the project with the following content. Replace the `{APP NAMESPACE}` placeholder with the app's namespace. `Pages/_Host.cshtml`: ```cshtml - @page "/blazor" - @namespace {APP NAMESPACE}.Pages.Shared + @page @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers - @{ - Layout = "_Layout"; - } <component type="typeof(App)" render-mode="ServerPrerendered" /> ``` - In this scenario, components use the shared `_Layout.cshtml` file for their layout. - - > [!IMPORTANT] - > The use of a layout page (`_Layout.cshtml`) with a [Component Tag Helper](xref:mvc/views/tag-helpers/builtin-th/component-tag-helper) for a <xref:Microsoft.AspNetCore.Components.Web.HeadOutlet> component is required to control `<head>` content, such as the page's title (<xref:Microsoft.AspNetCore.Components.Web.PageTitle> component) and other head elements (<xref:Microsoft.AspNetCore.Components.Web.HeadContent> component). For more information, see <xref:blazor/components/control-head-content#control-head-content-during-prerendering>. + > [!NOTE] + > The preceding example assumes that the <xref:Microsoft.AspNetCore.Components.Web.HeadOutlet> component and Blazor script (`_framework/blazor.server.js`) are rendered by the app's layout. For more information, see the [Configuration](#configuration) section. <xref:Microsoft.AspNetCore.Mvc.Rendering.RenderMode> configures whether the `App` component: @@ -655,24 +624,18 @@ To support routable Razor components in MVC apps: </Router> ``` -1. Add a `_Host` view to the project with the following content. +1. Add a `_Host` view to the project with the following content. Replace the `{APP NAMESPACE}` placeholder with the app's namespace. `Views/Home/_Host.cshtml`: ```cshtml - @namespace {APP NAMESPACE}.Views.Shared @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers - @{ - Layout = "_Layout"; - } <component type="typeof(App)" render-mode="ServerPrerendered" /> ``` - Components use the shared `_Layout.cshtml` file for their layout. - - > [!IMPORTANT] - > The use of a layout page (`_Layout.cshtml`) with a [Component Tag Helper](xref:mvc/views/tag-helpers/builtin-th/component-tag-helper) for a <xref:Microsoft.AspNetCore.Components.Web.HeadOutlet> component is required to control `<head>` content, such as the page's title (<xref:Microsoft.AspNetCore.Components.Web.PageTitle> component) and other head elements (<xref:Microsoft.AspNetCore.Components.Web.HeadContent> component). For more information, see <xref:blazor/components/control-head-content#control-head-content-during-prerendering>. + > [!NOTE] + > The preceding example assumes that the <xref:Microsoft.AspNetCore.Components.Web.HeadOutlet> component and Blazor script (`_framework/blazor.server.js`) are rendered by the app's layout. For more information, see the [Configuration](#configuration) section. <xref:Microsoft.AspNetCore.Mvc.Rendering.RenderMode> configures whether the `App` component: @@ -758,9 +721,6 @@ The following Razor page renders a `Counter` component: ``` For more information, see <xref:mvc/views/tag-helpers/builtin-th/component-tag-helper>. - -> [!IMPORTANT] -> The use of a layout page (`_Layout.cshtml`) with a [Component Tag Helper](xref:mvc/views/tag-helpers/builtin-th/component-tag-helper) for a <xref:Microsoft.AspNetCore.Components.Web.HeadOutlet> component is required to control `<head>` content, such as the page's title (<xref:Microsoft.AspNetCore.Components.Web.PageTitle> component) and other head elements (<xref:Microsoft.AspNetCore.Components.Web.HeadContent> component). For more information, see <xref:blazor/components/control-head-content#control-head-content-during-prerendering>. ### Render noninteractive components @@ -784,9 +744,6 @@ In the following Razor page, the `Counter` component is statically rendered with ``` For more information, see <xref:mvc/views/tag-helpers/builtin-th/component-tag-helper>. - -> [!IMPORTANT] -> The use of a layout page (`_Layout.cshtml`) with a [Component Tag Helper](xref:mvc/views/tag-helpers/builtin-th/component-tag-helper) for a <xref:Microsoft.AspNetCore.Components.Web.HeadOutlet> component is required to control `<head>` content, such as the page's title (<xref:Microsoft.AspNetCore.Components.Web.PageTitle> component) and other head elements (<xref:Microsoft.AspNetCore.Components.Web.HeadContent> component). For more information, see <xref:blazor/components/control-head-content#control-head-content-during-prerendering>. ## Component namespaces @@ -811,7 +768,7 @@ Without persisting prerendered state, state used during prerendering is lost and To solve these problems, Blazor supports persisting state in a prerendered page using the [Persist Component State Tag Helper](xref:mvc/views/tag-helpers/builtin-th/persist-component-state-tag-helper). Add the Tag Helper's tag, `<persist-component-state />`, inside the closing `</body>` tag. -`Pages/_Layout.cshtml`: +`Pages/_Host.cshtml`: ```cshtml <body> @@ -882,7 +839,7 @@ else "fetchdata", out var restored)) { forecasts = - await WeatherForecastService.GetForecastAsync(DateTime.Now); + await WeatherForecastService.GetForecastAsync(DateOnly.FromDateTime(DateTime.Now)); } else { @@ -926,7 +883,7 @@ By initializing components with the same state used during prerendering, any exp :::zone-end :::zone pivot="server" - + ## Prerendered state size and SignalR message size limit A large prerendered state size may exceed the SignalR circuit message size limit, which results in the following: @@ -956,12 +913,12 @@ To resolve the problem, use ***either*** of the following approaches: :::moniker-end -:::moniker range=">= aspnetcore-5.0 < aspnetcore-6.0" +:::moniker range=">= aspnetcore-6.0 < aspnetcore-7.0" :::zone pivot="webassembly" Razor components can be integrated into Razor Pages and MVC apps in a hosted Blazor WebAssembly [solution](xref:blazor/tooling#visual-studio-solution-file-sln). When the page or view is rendered, components can be prerendered at the same time. - + Prerendering can improve [Search Engine Optimization (SEO)](https://developer.mozilla.org/docs/Glossary/SEO) by rendering content for the initial HTTP response that search engines can use to calculate page rank. ## Solution configuration @@ -979,48 +936,120 @@ To set up prerendering for a hosted Blazor WebAssembly app: 1. **Delete** the `wwwroot/index.html` file from the Blazor WebAssembly **:::no-loc text="Client":::** project. -1. In the **:::no-loc text="Client":::** project, **delete** the following line in `Program.cs`: +1. In the **:::no-loc text="Client":::** project, **delete** the following lines in `Program.cs`: ```diff - builder.RootComponents.Add<App>("#app"); + - builder.RootComponents.Add<HeadOutlet>("head::after"); ``` -1. Add a `Pages/_Host.cshtml` file to the **:::no-loc text="Server":::** project's `Pages` folder. You can obtain a `_Host.cshtml` file from a project created from the Blazor Server template with the `dotnet new blazorserver -o BlazorServer` command in a command shell (the `-o BlazorServer` option creates a folder for the project). After placing the `Pages/_Host.cshtml` file into the **:::no-loc text="Server":::** project of the hosted Blazor WebAssembly solution, make the following changes to the file: +1. Add `_Host.cshtml` and `_Layout.cshtml` files to the **:::no-loc text="Server":::** project's `Pages` folder. You can obtain the files from a project created from the Blazor Server template using Visual Studio or using the .NET CLI with the `dotnet new blazorserver -o BlazorServer` command in a command shell (the `-o BlazorServer` option creates a folder for the project). After placing the files into the **:::no-loc text="Server":::** project's `Pages` folder, make the following changes to the files. - * Provide an [`@using`](xref:mvc/views/razor#using) directive for the **:::no-loc text="Client":::** project (for example, `@using BlazorHosted.Client`). - * Update the stylesheet links to point to the WebAssembly project's stylesheets. In the following example, the client project's namespace is `BlazorHosted.Client`: + > [!IMPORTANT] + > The use of a layout page (`_Layout.cshtml`) with a [Component Tag Helper](xref:mvc/views/tag-helpers/builtin-th/component-tag-helper) for a <xref:Microsoft.AspNetCore.Components.Web.HeadOutlet> component is required to control `<head>` content, such as the page's title (<xref:Microsoft.AspNetCore.Components.Web.PageTitle> component) and other head elements (<xref:Microsoft.AspNetCore.Components.Web.HeadContent> component). For more information, see <xref:blazor/components/control-head-content#control-head-content-during-prerendering>. + + Make the following changes to the `_Layout.cshtml` file: + + * Update the `Pages` namespace at the top of the file to match the namespace of the **:::no-loc text="Server":::** app's pages. The `{APP NAMESPACE}` placeholder in the following example represents the namespace of the donor app's pages that provided the `_Layout.cshtml` file: + + Delete: + + ```diff + - @namespace {APP NAMESPACE}.Pages + ``` + + Add: + + ```razor + @namespace BlazorHosted.Server.Pages + ``` + + * Add an [`@using`](xref:mvc/views/razor#using) directive for the **:::no-loc text="Client":::** project at the top of the file: + + ```razor + @using BlazorHosted.Client + ``` + + * Update the stylesheet links to point to the WebAssembly project's stylesheets. In the following example, the client project's namespace is `BlazorHosted.Client`. The `{APP NAMESPACE}` placeholder represents the namespace of the donor app that provided the `_Layout.cshtml` file. Update the Component Tag Helper (`<component>` tag) for the `HeadOutlet` component to prerender the component. + + Delete: ```diff - <link href="css/site.css" rel="stylesheet" /> - - <link href="_content/BlazorServer/_framework/scoped.styles.css" rel="stylesheet" /> - + <link href="css/app.css" rel="stylesheet" /> - + <link href="BlazorHosted.Client.styles.css" rel="stylesheet" /> + - <link href="{APP NAMESPACE}.styles.css" rel="stylesheet" /> + - <component type="typeof(HeadOutlet)" render-mode="ServerPrerendered" /> + ``` + + Add: + + ```razor + <link href="css/app.css" rel="stylesheet" /> + <link href="BlazorHosted.Client.styles.css" rel="stylesheet" /> + <component type="typeof(HeadOutlet)" render-mode="WebAssemblyPrerendered" /> ``` > [!NOTE] > Leave the `<link>` element that requests the Bootstrap stylesheet (`css/bootstrap/bootstrap.min.css`) in place. + * Update the Blazor script source to use the client-side Blazor WebAssembly script: + + Delete: + + ```diff + - <script src="_framework/blazor.server.js"></script> + ``` + + Add: + + ```html + <script src="_framework/blazor.webassembly.js"></script> + ``` + + In the `_Host.cshtml` file: + + * Change the `Pages` namespace to that of the **:::no-loc text="Client":::** project. The `{APP NAMESPACE}` placeholder represents the namespace of the donor app's pages that provided the `_Host.cshtml` file: + + Delete: + + ```diff + - @namespace {APP NAMESPACE}.Pages + ``` + + Add: + + ```razor + @namespace BlazorHosted.Client + ``` + * Update the `render-mode` of the [Component Tag Helper](xref:mvc/views/tag-helpers/builtin-th/component-tag-helper) to prerender the root `App` component with <xref:Microsoft.AspNetCore.Mvc.Rendering.RenderMode.WebAssemblyPrerendered>: + Delete: + ```diff - <component type="typeof(App)" render-mode="ServerPrerendered" /> - + <component type="typeof(App)" render-mode="WebAssemblyPrerendered" /> ``` - * Update the Blazor script source to use the client-side Blazor WebAssembly script: + Add: - ```diff - - <script src="_framework/blazor.server.js"></script> - + <script src="_framework/blazor.webassembly.js"></script> + ```razor + <component type="typeof(App)" render-mode="WebAssemblyPrerendered" /> ``` -1. In `Startup.Configure` of the **:::no-loc text="Server":::** project, change the fallback from the `index.html` file to the `_Host.cshtml` page. + > [!IMPORTANT] + > Prerendering isn't supported for authentication endpoints (`/authentication/` path segment). For more information, see <xref:blazor/security/webassembly/additional-scenarios#support-prerendering-with-authentication>. - `Startup.cs`: +1. In endpoint mapping of the **:::no-loc text="Server":::** project in `Program.cs`, change the fallback from the `index.html` file to the `_Host.cshtml` page: + + Delete: ```diff - - endpoints.MapFallbackToFile("index.html"); - + endpoints.MapFallbackToPage("/_Host"); + - app.MapFallbackToFile("index.html"); + ``` + + Add: + + ```csharp + app.MapFallbackToPage("/_Host"); ``` 1. If the **:::no-loc text="Client":::** and **:::no-loc text="Server":::** projects use one or more common services during prerendering, factor the service registrations into a method that can be called from both projects. For more information, see <xref:blazor/fundamentals/dependency-injection#register-common-services-in-a-hosted-blazor-webassembly-solution>. @@ -1029,44 +1058,76 @@ To set up prerendering for a hosted Blazor WebAssembly app: ### Configuration for embedding Razor components into pages and views -The following sections and examples in this article for embedding Razor components of the client Blazor WebAssembly app into pages and views of the server app require additional configuration. +The following sections and examples for embedding Razor components from the **:::no-loc text="Client":::** Blazor WebAssembly app into pages and views of the server app require additional configuration. -Use a default Razor Pages or MVC layout file in the **:::no-loc text="Server":::** project. The **:::no-loc text="Server":::** project must have the following files and folders. +The **:::no-loc text="Server":::** project must have the following files and folders. Razor Pages: * `Pages/Shared/_Layout.cshtml` +* `Pages/Shared/_Layout.cshtml.css` * `Pages/_ViewImports.cshtml` * `Pages/_ViewStart.cshtml` MVC: * `Views/Shared/_Layout.cshtml` +* `Views/Shared/_Layout.cshtml.css` * `Views/_ViewImports.cshtml` * `Views/_ViewStart.cshtml` -Obtain the preceding files from an app created from the Razor Pages or MVC project template. For more information, see <xref:tutorials/razor-pages/razor-pages-start> or <xref:tutorials/first-mvc-app/start-mvc>. - -Update the namespaces in the imported `_ViewImports.cshtml` file to match those in use by the **:::no-loc text="Server":::** project receiving the files. +> [!IMPORTANT] +> The use of a layout page (`_Layout.cshtml`) with a [Component Tag Helper](xref:mvc/views/tag-helpers/builtin-th/component-tag-helper) for a <xref:Microsoft.AspNetCore.Components.Web.HeadOutlet> component is required to control `<head>` content, such as the page's title (<xref:Microsoft.AspNetCore.Components.Web.PageTitle> component) and other head elements (<xref:Microsoft.AspNetCore.Components.Web.HeadContent> component). For more information, see <xref:blazor/components/control-head-content#control-head-content-during-prerendering>. -Update the imported layout file (`_Layout.cshtml`) to include the **:::no-loc text="Client":::** project's styles. In the following example, the **:::no-loc text="Client":::** project's namespace is `BlazorHosted.Client`. The `<title>` element can be updated at the same time. +The preceding files can be obtained by generating an app from the ASP.NET Core project templates using: -`Pages/Shared/_Layout.cshtml` (Razor Pages) or `Views/Shared/_Layout.cshtml` (MVC): +* Visual Studio's new project creation tools. +* Opening a command shell and executing `dotnet new webapp -o {APP NAME}` (Razor Pages) or `dotnet new mvc -o {APP NAME}` (MVC). The option `-o|--output` with a value for the `{APP NAME}` placeholder provides a name for the app and creates a folder for the app. + +Update the namespaces in the imported `_ViewImports.cshtml` file to match those in use by the **:::no-loc text="Server":::** project receiving the files. + +`Pages/_ViewImports.cshtml` (Razor Pages): + +```razor +@using BlazorHosted.Server +@namespace BlazorHosted.Server.Pages +@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers +``` + +`Views/_ViewImports.cshtml` (MVC): + +```razor +@using BlazorHosted.Server +@using BlazorHosted.Server.Models +@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers +``` + +Update the imported layout file, which is `Pages/Shared/_Layout.cshtml` for Razor Pages or `Views/Shared/_Layout.cshtml` for MVC. + +First, delete the title and the stylesheet from the donor project, which is `RPDonor.styles.css` in the following example. The `{APP NAME}` placeholder represents the donor project's app name. ```diff -<head> - <meta charset="utf-8" /> - <meta name="viewport" content="width=device-width, initial-scale=1.0" /> -- <title>@ViewData["Title"] - DonorProject -+ @ViewData["Title"] - BlazorHosted - - -+ -+ - +- @ViewData["Title"] - {APP NAME} +- ``` -The imported layout contains `Home` and `Privacy` navigation links. To make the `Home` link point to the hosted Blazor WebAssembly app, change the hyperlink: +Include the **:::no-loc text="Client":::** project's styles in the layout file. In the following example, the **:::no-loc text="Client":::** project's namespace is `BlazorHosted.Client`. The `` element can be updated at the same time. + +Place the following lines in the `<head>` content of the layout file: + +```html +<title>@ViewData["Title"] - BlazorHosted + + + +``` + +The imported layout contains two `Home` (`Index` page) and `Privacy` navigation links. To make the `Home` links point to the hosted Blazor WebAssembly app, change the hyperlinks: + +```diff +- {APP NAME} ++ BlazorHosted +``` ```diff - Home @@ -1075,40 +1136,58 @@ The imported layout contains `Home` and `Privacy` navigation links. To make the In an MVC layout file: +```diff +- {APP NAME} ++ BlazorHosted +``` + ```diff - Home + Home ``` -To make the `Privacy` link lead to a privacy page, add a privacy page to the **:::no-loc text="Server":::** project. +Update the `