From dbf0e75bfaf84fa6f79e551adb3a4897b78f8d07 Mon Sep 17 00:00:00 2001 From: guardrex <1622880+guardrex@users.noreply.github.com> Date: Wed, 8 Dec 2021 07:30:31 -0600 Subject: [PATCH 1/5] Blazor app pool guidance --- aspnetcore/blazor/host-and-deploy/index.md | 18 ++++++++++++++++++ aspnetcore/host-and-deploy/iis/web-config.md | 2 +- 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/aspnetcore/blazor/host-and-deploy/index.md b/aspnetcore/blazor/host-and-deploy/index.md index dd26f2d6c796..643304b84e26 100644 --- a/aspnetcore/blazor/host-and-deploy/index.md +++ b/aspnetcore/blazor/host-and-deploy/index.md @@ -52,6 +52,12 @@ Publish locations: The assets in the folder are deployed to the web server. Deployment might be a manual or automated process depending on the development tools in use. +## IIS Application Pools + +Sharing an app pool among ASP.NET Core apps isn't supported, including for Blazor apps. Use one app pool per app when hosting with IIS, and avoid the use of IIS's [virtual directories](/iis/get-started/planning-your-iis-architecture/understanding-sites-applications-and-virtual-directories-on-iis#virtual-directories) for hosting multiple apps. + +One or more Blazor WebAssembly apps hosted by an ASP.NET Core app, known as a [hosted Blazor WebAssembly solution](xref:blazor/hosting-models#blazor-webassembly), are supported for ***one*** app pool. However, we don't recommend or support assigning a single app pool to multiple hosted Blazor WebAssembly solutions or in sub-app hosting scenarios. For more information, see . + ## App base path The *app base path* is the app's root URL path. Consider the following ASP.NET Core app and Blazor sub-app: @@ -211,6 +217,12 @@ Publish locations: The assets in the folder are deployed to the web server. Deployment might be a manual or automated process depending on the development tools in use. +## IIS Application Pools + +Sharing an app pool among ASP.NET Core apps isn't supported, including for Blazor apps. Use one app pool per app when hosting with IIS, and avoid the use of IIS's [virtual directories](/iis/get-started/planning-your-iis-architecture/understanding-sites-applications-and-virtual-directories-on-iis#virtual-directories) for hosting multiple apps. + +One or more Blazor WebAssembly apps hosted by an ASP.NET Core app, known as a [hosted Blazor WebAssembly solution](xref:blazor/hosting-models#blazor-webassembly), are supported for ***one*** app pool. However, we don't recommend or support assigning a single app pool to multiple hosted Blazor WebAssembly solutions or in sub-app hosting scenarios. For more information, see . + ## App base path The *app base path* is the app's root URL path. Consider the following ASP.NET Core app and Blazor sub-app: @@ -350,6 +362,12 @@ Publish locations: The assets in the folder are deployed to the web server. Deployment might be a manual or automated process depending on the development tools in use. +## IIS Application Pools + +Sharing an app pool among ASP.NET Core apps isn't supported, including for Blazor apps. Use one app pool per app when hosting with IIS, and avoid the use of IIS's [virtual directories](/iis/get-started/planning-your-iis-architecture/understanding-sites-applications-and-virtual-directories-on-iis#virtual-directories) for hosting multiple apps. + +One or more Blazor WebAssembly apps hosted by an ASP.NET Core app, known as a [hosted Blazor WebAssembly solution](xref:blazor/hosting-models#blazor-webassembly), are supported for ***one*** app pool. However, we don't recommend or support assigning a single app pool to multiple hosted Blazor WebAssembly solutions or in sub-app hosting scenarios. For more information, see . + ## App base path The *app base path* is the app's root URL path. Consider the following ASP.NET Core app and Blazor sub-app: diff --git a/aspnetcore/host-and-deploy/iis/web-config.md b/aspnetcore/host-and-deploy/iis/web-config.md index c4bee8a6b974..def9d7db062f 100644 --- a/aspnetcore/host-and-deploy/iis/web-config.md +++ b/aspnetcore/host-and-deploy/iis/web-config.md @@ -90,7 +90,7 @@ The When an app is deployed to [Azure App Service](https://azure.microsoft.com/services/app-service/), the `stdoutLogFile` path is set to `\\?\%home%\LogFiles\stdout`. The path saves stdout logs to the `LogFiles` folder, which is a location automatically created by the service. -For information on IIS sub-application configuration, see . +For information on IIS sub-application configuration, see . ### Attributes of the `aspNetCore` element From 5a207bd7c045c6f1775f322bb7ea1fd769795ea1 Mon Sep 17 00:00:00 2001 From: guardrex <1622880+guardrex@users.noreply.github.com> Date: Thu, 9 Dec 2021 14:14:42 -0600 Subject: [PATCH 2/5] Blazor 6.0 examples to snippet samps + NRT work --- .../cascading-values-and-parameters.md | 32 ++++----- .../blazor/components/class-libraries.md | 4 ++ .../blazor/components/control-head-content.md | 25 +------ aspnetcore/blazor/components/data-binding.md | 5 +- .../blazor/components/dynamiccomponent.md | 66 +++++++++---------- .../ControlHeadContent.razor | 22 +++++++ .../ControlHeadContent.razor | 22 +++++++ 7 files changed, 99 insertions(+), 77 deletions(-) create mode 100644 aspnetcore/blazor/samples/6.0/BlazorSample_Server/Pages/control-head-content/ControlHeadContent.razor create mode 100644 aspnetcore/blazor/samples/6.0/BlazorSample_WebAssembly/Pages/control-head-content/ControlHeadContent.razor diff --git a/aspnetcore/blazor/components/cascading-values-and-parameters.md b/aspnetcore/blazor/components/cascading-values-and-parameters.md index d5c9d4911379..333a8851afb9 100644 --- a/aspnetcore/blazor/components/cascading-values-and-parameters.md +++ b/aspnetcore/blazor/components/cascading-values-and-parameters.md @@ -60,26 +60,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; } } ``` @@ -131,13 +127,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); } @@ -170,25 +166,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 399c68ba7250..a3db074b089a 100644 --- a/aspnetcore/blazor/components/class-libraries.md +++ b/aspnetcore/blazor/components/class-libraries.md @@ -340,6 +340,10 @@ Blazor WebAssembly and RCL projects *automatically* enable browser compatibility When authoring a library, indicate that a particular API isn't supported in browsers by specifying `browser` to : ```csharp +using System.Runtime.Versioning; + +... + [UnsupportedOSPlatform("browser")] private static string GetLoggingDirectory() { diff --git a/aspnetcore/blazor/components/control-head-content.md b/aspnetcore/blazor/components/control-head-content.md index 3e22815fc05a..6ba05dae183b 100644 --- a/aspnetcore/blazor/components/control-head-content.md +++ b/aspnetcore/blazor/components/control-head-content.md @@ -23,30 +23,7 @@ The following example sets the page's title and description using Razor. `Pages/ControlHeadContent.razor`: -```razor -@page "/control-head-content" - -

Control <head> content

- -

- Title: @title -

- -

- Description: @description -

- -@title - - - - - -@code { - private string description = "Description set by component"; - private string title = "Title set by component"; -} -``` +[!code-razor[](~/blazor/samples/6.0/BlazorSample_WebAssembly/Pages/control-head-content/ControlHeadContent.razor?highlight=13,15-17)] ## Control head content during prerendering diff --git a/aspnetcore/blazor/components/data-binding.md b/aspnetcore/blazor/components/data-binding.md index bed8413fb99c..aae830094ec4 100644 --- a/aspnetcore/blazor/components/data-binding.md +++ b/aspnetcore/blazor/components/data-binding.md @@ -98,7 +98,10 @@ Binding supports [`multiple`](https://developer.mozilla.org/docs/Web/HTML/Attrib void SelectedCarsChanged(ChangeEventArgs e) { - SelectedCars = (string[])e.Value; + if (e.Value is not null) + { + SelectedCars = (string[])e.Value; + } } } ``` diff --git a/aspnetcore/blazor/components/dynamiccomponent.md b/aspnetcore/blazor/components/dynamiccomponent.md index d2f7e70a277a..c5aa82db6399 100644 --- a/aspnetcore/blazor/components/dynamiccomponent.md +++ b/aspnetcore/blazor/components/dynamiccomponent.md @@ -41,11 +41,11 @@ Use the propert @code { - private DynamicComponent dc; + private DynamicComponent? dc; private Task Refresh() { - return (dc.Instance as IRefreshable)?.Refresh(); + return (dc?.Instance as IRefreshable)?.Refresh(); } } ``` @@ -138,11 +138,11 @@ In the following example, a Razor component renders a component based on the use } @code { - private Type selectedType; + private Type? selectedType; private void OnDropdownChange(ChangeEventArgs e) { - selectedType = e.Value.ToString().Length > 0 ? + selectedType = e.Value?.ToString()?.Length > 0 ? Type.GetType($"{APP NAMESPACE}.Shared.{e.Value}") : null; } } @@ -162,17 +162,37 @@ The following example configures a component metadata object (`ComponentMetadata `ComponentMetadata.cs`: ```csharp -using System; -using System.Collections.Generic; - public class ComponentMetadata { - public string Name { get; set; } - public Dictionary Parameters { get; set; } + public string? Name { get; set; } + public Dictionary Parameters { get; set; } = + new Dictionary(); +} +``` + +The following `RocketLab` component (`Shared/RocketLab.razor`) has been updated from the preceding example to include a component parameter named `WindowSeat` to specify if the passenger prefers a window seat on their flight: + +`Shared/RocketLab.razor`: + +```razor +

Rocket Lab®

+ +

+ User selected a window seat: @WindowSeat +

+ +

+ Rocket Lab is a trademark of + Rocket Lab USA Inc. +

+ +@code { + [Parameter] + public bool WindowSeat { get; set; } } ``` -In the following example, only the `RocketLab` component has a parameter, which assigns a value for a window seat (`WindowSeat`) on a spaceflight. +In the following example, only the `RocketLab` component's parameter for a window seat (`WindowSeat`) receives the value of the **`Window Seat`** checkbox. `Pages/DynamicComponentExample2.razor`: @@ -234,7 +254,7 @@ In the following example, only the `RocketLab` component has a parameter, which new ComponentMetadata { Name = "SpaceX" } } }; - private Type selectedType; + private Type? selectedType; private bool windowSeat; private bool WindowSeat @@ -249,7 +269,7 @@ In the following example, only the `RocketLab` component has a parameter, which private void OnDropdownChange(ChangeEventArgs e) { - selectedType = e.Value.ToString().Length > 0 ? + selectedType = e.Value?.ToString()?.Length > 0 ? Type.GetType($"{APP NAMESPACE}.Shared.{e.Value}") : null; } } @@ -265,28 +285,6 @@ In the preceding example: * `UnitedLaunchAlliance` (`Shared/UnitedLaunchAlliance.razor`) * `VirginGalactic` (`Shared/VirginGalactic.razor`) -The `RocketLab` component (`Shared/RocketLab.razor`) includes a component parameter named `WindowSeat`: - -`Shared/RocketLab.razor`: - -```razor -

Rocket Lab®

- -

- User selected a window seat: @WindowSeat -

- -

- Rocket Lab is a trademark of - Rocket Lab USA Inc. -

- -@code { - [Parameter] - public bool WindowSeat { get; set; } -} -``` - ## Avoid catch-all parameters Avoid the use of [catch-all parameters](xref:blazor/fundamentals/routing#catch-all-route-parameters). If catch-all parameters are used, every explicit parameter on effectively is a reserved word that you can't pass to a dynamic child. Any new parameters passed to are a breaking change, as they start shadowing child component parameters that happen to have the same name. It's unlikely that the caller always knows a fixed set of parameter names to pass to all possible dynamic children. diff --git a/aspnetcore/blazor/samples/6.0/BlazorSample_Server/Pages/control-head-content/ControlHeadContent.razor b/aspnetcore/blazor/samples/6.0/BlazorSample_Server/Pages/control-head-content/ControlHeadContent.razor new file mode 100644 index 000000000000..9f690a1580a2 --- /dev/null +++ b/aspnetcore/blazor/samples/6.0/BlazorSample_Server/Pages/control-head-content/ControlHeadContent.razor @@ -0,0 +1,22 @@ +@page "/control-head-content" + +

Control <head> content

+ +

+ Title: @title +

+ +

+ Description: @description +

+ +@title + + + + + +@code { + private string description = "Description set by component"; + private string title = "Title set by component"; +} diff --git a/aspnetcore/blazor/samples/6.0/BlazorSample_WebAssembly/Pages/control-head-content/ControlHeadContent.razor b/aspnetcore/blazor/samples/6.0/BlazorSample_WebAssembly/Pages/control-head-content/ControlHeadContent.razor new file mode 100644 index 000000000000..9f690a1580a2 --- /dev/null +++ b/aspnetcore/blazor/samples/6.0/BlazorSample_WebAssembly/Pages/control-head-content/ControlHeadContent.razor @@ -0,0 +1,22 @@ +@page "/control-head-content" + +

Control <head> content

+ +

+ Title: @title +

+ +

+ Description: @description +

+ +@title + + + + + +@code { + private string description = "Description set by component"; + private string title = "Title set by component"; +} From 32559322b31ca51de560371f6e9079451339fad6 Mon Sep 17 00:00:00 2001 From: guardrex <1622880+guardrex@users.noreply.github.com> Date: Fri, 10 Dec 2021 07:39:19 -0600 Subject: [PATCH 3/5] Updates --- .../blazor/components/dynamiccomponent.md | 186 ++---------------- .../blazor/components/event-handling.md | 16 +- aspnetcore/blazor/components/index.md | 35 +--- aspnetcore/blazor/components/lifecycle.md | 15 +- .../prerendering-and-integration.md | 16 +- .../blazor/components/templated-components.md | 6 +- .../blazor/components/virtualization.md | 20 +- .../DynamicComponentExample1.razor | 33 ++++ .../DynamicComponentExample2.razor | 77 ++++++++ .../Pages/index/CounterPartialClass.razor | 9 + .../Pages/index/CounterPartialClass.razor.cs | 12 ++ .../Shared/dynamiccomponent/RocketLab.razor | 6 + .../RocketLabWithWindowSeat.razor | 15 ++ .../Shared/dynamiccomponent/SpaceX.razor | 6 + .../UnitedLaunchAlliance.razor | 6 + .../dynamiccomponent/VirginGalactic.razor | 6 + .../DynamicComponentExample1.razor | 33 ++++ .../DynamicComponentExample2.razor | 77 ++++++++ .../Pages/index/CounterPartialClass.razor | 9 + .../Pages/index/CounterPartialClass.razor.cs | 12 ++ .../Shared/dynamiccomponent/RocketLab.razor | 6 + .../RocketLabWithWindowSeat.razor | 15 ++ .../Shared/dynamiccomponent/SpaceX.razor | 6 + .../UnitedLaunchAlliance.razor | 6 + .../dynamiccomponent/VirginGalactic.razor | 6 + 25 files changed, 402 insertions(+), 232 deletions(-) create mode 100644 aspnetcore/blazor/samples/6.0/BlazorSample_Server/Pages/dynamiccomponent/DynamicComponentExample1.razor create mode 100644 aspnetcore/blazor/samples/6.0/BlazorSample_Server/Pages/dynamiccomponent/DynamicComponentExample2.razor create mode 100644 aspnetcore/blazor/samples/6.0/BlazorSample_Server/Pages/index/CounterPartialClass.razor create mode 100644 aspnetcore/blazor/samples/6.0/BlazorSample_Server/Pages/index/CounterPartialClass.razor.cs create mode 100644 aspnetcore/blazor/samples/6.0/BlazorSample_Server/Shared/dynamiccomponent/RocketLab.razor create mode 100644 aspnetcore/blazor/samples/6.0/BlazorSample_Server/Shared/dynamiccomponent/RocketLabWithWindowSeat.razor create mode 100644 aspnetcore/blazor/samples/6.0/BlazorSample_Server/Shared/dynamiccomponent/SpaceX.razor create mode 100644 aspnetcore/blazor/samples/6.0/BlazorSample_Server/Shared/dynamiccomponent/UnitedLaunchAlliance.razor create mode 100644 aspnetcore/blazor/samples/6.0/BlazorSample_Server/Shared/dynamiccomponent/VirginGalactic.razor create mode 100644 aspnetcore/blazor/samples/6.0/BlazorSample_WebAssembly/Pages/dynamiccomponent/DynamicComponentExample1.razor create mode 100644 aspnetcore/blazor/samples/6.0/BlazorSample_WebAssembly/Pages/dynamiccomponent/DynamicComponentExample2.razor create mode 100644 aspnetcore/blazor/samples/6.0/BlazorSample_WebAssembly/Pages/index/CounterPartialClass.razor create mode 100644 aspnetcore/blazor/samples/6.0/BlazorSample_WebAssembly/Pages/index/CounterPartialClass.razor.cs create mode 100644 aspnetcore/blazor/samples/6.0/BlazorSample_WebAssembly/Shared/dynamiccomponent/RocketLab.razor create mode 100644 aspnetcore/blazor/samples/6.0/BlazorSample_WebAssembly/Shared/dynamiccomponent/RocketLabWithWindowSeat.razor create mode 100644 aspnetcore/blazor/samples/6.0/BlazorSample_WebAssembly/Shared/dynamiccomponent/SpaceX.razor create mode 100644 aspnetcore/blazor/samples/6.0/BlazorSample_WebAssembly/Shared/dynamiccomponent/UnitedLaunchAlliance.razor create mode 100644 aspnetcore/blazor/samples/6.0/BlazorSample_WebAssembly/Shared/dynamiccomponent/VirginGalactic.razor diff --git a/aspnetcore/blazor/components/dynamiccomponent.md b/aspnetcore/blazor/components/dynamiccomponent.md index c5aa82db6399..cd3e203c827e 100644 --- a/aspnetcore/blazor/components/dynamiccomponent.md +++ b/aspnetcore/blazor/components/dynamiccomponent.md @@ -68,85 +68,23 @@ In the following example, a Razor component renders a component based on the use `Shared/RocketLab.razor`: -```razor -

Rocket Lab®

- -

- Rocket Lab is a registered trademark of - Rocket Lab USA Inc. -

-``` +[!code-razor[](~/blazor/samples/6.0/BlazorSample_WebAssembly/Shared/dynamiccomponent/RocketLab.razor)] `Shared/SpaceX.razor`: -```razor -

SpaceX®

- -

- SpaceX is a registered trademark of - Space Exploration Technologies Corp. -

-``` +[!code-razor[](~/blazor/samples/6.0/BlazorSample_WebAssembly/Shared/dynamiccomponent/SpaceX.razor)] `Shared/UnitedLaunchAlliance.razor`: -```razor -

United Launch Alliance®

- -

- United Launch Alliance and ULA are registered trademarks of - United Launch Alliance, LLC. -

-``` +[!code-razor[](~/blazor/samples/6.0/BlazorSample_WebAssembly/Shared/dynamiccomponent/UnitedLaunchAlliance.razor)] `Shared/VirginGalactic.razor`: -```razor -

Virgin Galactic®

- -

- Virgin Galactic is a registered trademark of - Galactic Enterprises, LLC. -

-``` +[!code-razor[](~/blazor/samples/6.0/BlazorSample_WebAssembly/Shared/dynamiccomponent/VirginGalactic.razor)] `Pages/DynamicComponentExample1.razor`: -```razor -@page "/dynamiccomponent-example-1" - -

DynamicComponent Component Example 1

- -

- -

- -@if (selectedType is not null) -{ -
- -
-} - -@code { - private Type? selectedType; - - private void OnDropdownChange(ChangeEventArgs e) - { - selectedType = e.Value?.ToString()?.Length > 0 ? - Type.GetType($"{APP NAMESPACE}.Shared.{e.Value}") : null; - } -} -``` +[!code-razor[](~/blazor/samples/6.0/BlazorSample_WebAssembly/Pages/dynamiccomponent/DynamicComponentExample1.razor)] In the preceding example: @@ -170,121 +108,27 @@ public class ComponentMetadata } ``` -The following `RocketLab` component (`Shared/RocketLab.razor`) has been updated from the preceding example to include a component parameter named `WindowSeat` to specify if the passenger prefers a window seat on their flight: - -`Shared/RocketLab.razor`: - -```razor -

Rocket Lab®

+The following `RocketLabWithWindowSeat` component (`Shared/RocketLabWithWindowSeat.razor`) has been updated from the preceding example to include a component parameter named `WindowSeat` to specify if the passenger prefers a window seat on their flight: -

- User selected a window seat: @WindowSeat -

+`Shared/RocketLabWithWindowSeat.razor`: -

- Rocket Lab is a trademark of - Rocket Lab USA Inc. -

+[!code-razor[](~/blazor/samples/6.0/BlazorSample_WebAssembly/Shared/dynamiccomponent/RocketLabWithWindowSeat.razor?highlight=13-14)] -@code { - [Parameter] - public bool WindowSeat { get; set; } -} -``` - -In the following example, only the `RocketLab` component's parameter for a window seat (`WindowSeat`) receives the value of the **`Window Seat`** checkbox. - -`Pages/DynamicComponentExample2.razor`: - -```razor -@page "/dynamiccomponent-example-2" - -

DynamicComponent Component Example 2

- -

- -

- -

- -

- -@if (selectedType is not null) -{ -
- -
-} - -@code { - private Dictionary components = - new() - { - { - "RocketLab", - new ComponentMetadata - { - Name = "Rocket Lab", - Parameters = new() { { "WindowSeat", false } } - } - }, - { - "VirginGalactic", - new ComponentMetadata { Name = "Virgin Galactic" } - }, - { - "UnitedLaunchAlliance", - new ComponentMetadata { Name = "ULA" } - }, - { - "SpaceX", - new ComponentMetadata { Name = "SpaceX" } - } - }; - private Type? selectedType; - private bool windowSeat; - - private bool WindowSeat - { - get { return windowSeat; } - set - { - windowSeat = value; - components[nameof(RocketLab)].Parameters["WindowSeat"] = windowSeat; - } - } - - private void OnDropdownChange(ChangeEventArgs e) - { - selectedType = e.Value?.ToString()?.Length > 0 ? - Type.GetType($"{APP NAMESPACE}.Shared.{e.Value}") : null; - } -} -``` - -In the preceding example: +In the following example: +* Only the `RocketLabWithWindowSeat` component's parameter for a window seat (`WindowSeat`) receives the value of the **`Window Seat`** checkbox. * The `{APP NAMESPACE}` placeholder is the namespace of the app (for example, `BlazorSample`). * The dynamically-rendered components are shared components in the app's `Shared` folder: - * Shown in this article section: `RocketLab` (`Shared/RocketLab.razor`) + * Shown in this article section: `RocketLabWithWindowSeat` (`Shared/RocketLabWithWindowSeat.razor`) * Components shown in the [Example](#example) section earlier in this article: * `SpaceX` (`Shared/SpaceX.razor`) * `UnitedLaunchAlliance` (`Shared/UnitedLaunchAlliance.razor`) * `VirginGalactic` (`Shared/VirginGalactic.razor`) +`Pages/DynamicComponentExample2.razor`: + +[!code-razor[](~/blazor/samples/6.0/BlazorSample_WebAssembly/Pages/dynamiccomponent/DynamicComponentExample2.razor)] + ## Avoid catch-all parameters Avoid the use of [catch-all parameters](xref:blazor/fundamentals/routing#catch-all-route-parameters). If catch-all parameters are used, every explicit parameter on effectively is a reserved word that you can't pass to a dynamic child. Any new parameters passed to are a breaking change, as they start shadowing child component parameters that happen to have the same name. It's unlikely that the caller always knows a fixed set of parameter names to pass to all possible dynamic children. diff --git a/aspnetcore/blazor/components/event-handling.md b/aspnetcore/blazor/components/event-handling.md index 212009605188..dc0c90900140 100644 --- a/aspnetcore/blazor/components/event-handling.md +++ b/aspnetcore/blazor/components/event-handling.md @@ -114,8 +114,8 @@ Custom events with custom event arguments are generally enabled with the followi ```csharp public class CustomEventArgs : EventArgs { - public string CustomProperty1 {get; set;} - public string CustomProperty2 {get; set;} + public string? CustomProperty1 {get; set;} + public string? CustomProperty2 {get; set;} } ``` @@ -165,7 +165,7 @@ public static class EventHandlers public class CustomPasteEventArgs : EventArgs { public DateTime EventTimestamp { get; set; } - public string PastedData { get; set; } + public string? PastedData { get; set; } } ``` @@ -178,10 +178,10 @@ Add JavaScript code to supply data for the subclass. In Blazor.registerCustomEventType('custompaste', { browserEventName: 'paste', createEventArgs: event => { - return { - eventTimestamp: new Date(), - pastedData: event.clipboardData.getData('text') - }; + return { + eventTimestamp: new Date(), + pastedData: event.clipboardData.getData('text') + }; } }); @@ -216,7 +216,7 @@ In a Razor component, attach the custom handler to an element.

@code { - private string message; + private string? message; private void HandleCustomPaste(CustomPasteEventArgs eventArgs) { diff --git a/aspnetcore/blazor/components/index.md b/aspnetcore/blazor/components/index.md index 033c02a68df5..4a1e98d78ed4 100644 --- a/aspnetcore/blazor/components/index.md +++ b/aspnetcore/blazor/components/index.md @@ -158,32 +158,11 @@ The following `Counter` component splits HTML and Razor markup from C# code usi `Pages/CounterPartialClass.razor`: -```razor -@page "/counter-partial-class" - -

Counter

- -

Current count: @currentCount

- - -``` +[!code-razor[](~/blazor/samples/6.0/BlazorSample_WebAssembly/Pages/index/CounterPartialClass.razor)] `Pages/CounterPartialClass.razor.cs`: -```csharp -namespace BlazorSample.Pages -{ - public partial class CounterPartialClass - { - private int currentCount = 0; - - void IncrementCount() - { - currentCount++; - } - } -} -``` +[!code-csharp[](~/blazor/samples/6.0/BlazorSample_WebAssembly/Pages/index/CounterPartialClass.razor.cs)] [`@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. @@ -306,7 +285,7 @@ To obtain a value for the `Title` parameter in the preceding example asynchronou @code { - private string title; + private string? title; protected override async Task OnInitializedAsync() { @@ -368,14 +347,14 @@ Apply the [`[EditorRequired]` attribute](xref:Microsoft.AspNetCore.Components.Ed ```csharp [Parameter] [EditorRequired] -public string Title { get; set; } +public string? Title { get; set; } ``` Single-line attribute lists are also supported: ```csharp [Parameter, EditorRequired] -public string Title { get; set; } +public string? Title { get; set; } ``` ## Route parameters @@ -534,7 +513,7 @@ To accept arbitrary attributes, define a [component parameter](#component-parame ```razor @code { [Parameter(CaptureUnmatchedValues = true)] - public Dictionary InputAttributes { get; set; } + public Dictionary? InputAttributes { get; set; } } ``` @@ -971,7 +950,7 @@ The following example demonstrates: 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; + private string? value; } ``` diff --git a/aspnetcore/blazor/components/lifecycle.md b/aspnetcore/blazor/components/lifecycle.md index bf06b230aa9e..85690bedacc3 100644 --- a/aspnetcore/blazor/components/lifecycle.md +++ b/aspnetcore/blazor/components/lifecycle.md @@ -322,8 +322,9 @@ Unsubscribe event handlers from .NET events. The following [Blazor form](xref:bl @code { - // ... - private EventHandler fieldChanged; + ... + + private EventHandler? fieldChanged; protected override void OnInitialized() { @@ -331,7 +332,7 @@ Unsubscribe event handlers from .NET events. The following [Blazor form](xref:bl fieldChanged = (_, __) => { - // ... + ... }; editContext.OnFieldChanged += fieldChanged; @@ -355,7 +356,7 @@ Unsubscribe event handlers from .NET events. The following [Blazor form](xref:bl @code { - // ... + ... protected override void OnInitialized() { @@ -365,7 +366,7 @@ Unsubscribe event handlers from .NET events. The following [Blazor form](xref:bl private void HandleFieldChanged(object sender, FieldChangedEventArgs e) { - // ... + ... } public void Dispose() @@ -398,10 +399,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() { diff --git a/aspnetcore/blazor/components/prerendering-and-integration.md b/aspnetcore/blazor/components/prerendering-and-integration.md index c93678e1319d..f7076a7e8090 100644 --- a/aspnetcore/blazor/components/prerendering-and-integration.md +++ b/aspnetcore/blazor/components/prerendering-and-integration.md @@ -576,6 +576,8 @@ To support routable Razor components in Razor Pages apps: ```razor @page "/routable-counter" + Routable Counter +

Routable Counter

Current count: @currentCount

@@ -672,6 +674,8 @@ To support routable Razor components in MVC apps: ```razor @page "/routable-counter" + Routable Counter +

Routable Counter

Current count: @currentCount

@@ -711,7 +715,7 @@ When the page or view renders: The following Razor page renders a `Counter` component: ```cshtml -

My Razor Page

+

Razor Page

@@ -732,7 +736,7 @@ For more information, see My Razor Page +

Razor Page

@@ -788,17 +792,19 @@ To solve these problems, Blazor supports persisting state in a prerendered page Decide what state to persist using the service. [`PersistentComponentState.RegisterOnPersisting`](xref:Microsoft.AspNetCore.Components.PersistentComponentState.RegisterOnPersisting%2A) registers a callback to persist the component state before the app is paused. The state is retrieved when the application resumes. -The following example shows how the weather forecast in the `FetchData` component from a hosted Blazor WebAssembly app based on the Blazor project template is persisted during prerendering and then retrieved to initialize the component. The Persist Component State Tag Helper persists the component state after all component invocations. +The following example is an updated version of the `FetchData` component in a hosted Blazor WebAssembly app based on the Blazor project template. The `WeatherForecastPreserveState` component persists weather forecast state during prerendering and then retrieves the state to initialize the component. The [Persist Component State Tag Helper](xref:mvc/views/tag-helpers/builtin-th/persist-component-state-tag-helper) persists the component state after all component invocations. -`Pages/FetchData.razor`: +`Pages/WeatherForecastPreserveState.razor`: ```razor -@page "/fetchdata" +@page "/weather-forecast-preserve-state" @implements IDisposable @using BlazorSample.Shared @inject IWeatherForecastService WeatherForecastService @inject PersistentComponentState ApplicationState +Weather Forecast +

Weather forecast

This component demonstrates fetching data from the server.

diff --git a/aspnetcore/blazor/components/templated-components.md b/aspnetcore/blazor/components/templated-components.md index 572f0e9927f1..0a54364ec893 100644 --- a/aspnetcore/blazor/components/templated-components.md +++ b/aspnetcore/blazor/components/templated-components.md @@ -68,10 +68,10 @@ For example, the following `Chart` component receives stock price data and casca @code { [Parameter] - public IEnumerable Data { get; set; } + public IEnumerable? Data { get; set; } [Parameter] - public RenderFragment ChildContent { get; set; } + public RenderFragment? ChildContent { get; set; } } ``` @@ -84,7 +84,7 @@ For example, the following `Chart` component receives stock price data and casca @code { [Parameter] - public string Title { get; set; } + public string? Title { get; set; } [Parameter] public decimal Value { get; set; } diff --git a/aspnetcore/blazor/components/virtualization.md b/aspnetcore/blazor/components/virtualization.md index a64a58fa0453..45b25b38a752 100644 --- a/aspnetcore/blazor/components/virtualization.md +++ b/aspnetcore/blazor/components/virtualization.md @@ -111,7 +111,7 @@ private async ValueTask> LoadEmployees( updates a `Virtualize` component's data without causing a rerender. If is invoked from a Blazor event handler or component lifecycle method, triggering a render isn't required because a render is automatically triggered at the end of the event handler or lifecycle method. If is triggered separately from a background task or event, such as in the following `ForcecastUpdated` delegate, call to update the UI at the end of the background task or event: ```csharp -private Virtualize virtualizeComponent; +private Virtualize? virtualizeComponent; protected override void OnInitialized() { @@ -119,7 +119,7 @@ protected override void OnInitialized() { await InvokeAsync(() => { - await virtualizeComponent.RefreshDataAsync(); + await virtualizeComponent?.RefreshDataAsync(); StateHasChanged(); }); }); @@ -230,14 +230,14 @@ The placeholder elements internally use an [Intersection Observer](https://devel * **Both the placeholders and the content rows are rendered in a single vertical stack with every item filling the whole horizontal width.** This is generally the default. In typical cases with `div` elements, `Virtualize` works by default. If you're using CSS to create a more advanced layout, bear in mind the following requirements: - * Scroll container styling requires a `display` with any of the following values: - * `block` (the default for a `div`). - * `table-row-group` (the default for a `tbody`). - * `flex` with `flex-direction` set to `column`. - * Content row styling requires a `display` with either of the following values: - * `block` (the default for a `div`). - * `table-row` (the default for a `tr`). - * Don't use CSS to interfere with the layout for the placeholder elements. By default, the placeholder elements have a `display` value of `block`, except if the parent is a table row group, in which case they default to `table-row`. Don't try to influence placeholder element width or height, including by causing them to have a border or `content` pseudo-elements. + * Scroll container styling requires a `display` with any of the following values: + * `block` (the default for a `div`). + * `table-row-group` (the default for a `tbody`). + * `flex` with `flex-direction` set to `column`. + * Content row styling requires a `display` with either of the following values: + * `block` (the default for a `div`). + * `table-row` (the default for a `tr`). + * Don't use CSS to interfere with the layout for the placeholder elements. By default, the placeholder elements have a `display` value of `block`, except if the parent is a table row group, in which case they default to `table-row`. Don't try to influence placeholder element width or height, including by causing them to have a border or `content` pseudo-elements. Any approach that stops the placeholders and content elements from rendering as a single vertical stack, or causes the content items to vary in height, prevents correct functioning of the `Virtualize` component. diff --git a/aspnetcore/blazor/samples/6.0/BlazorSample_Server/Pages/dynamiccomponent/DynamicComponentExample1.razor b/aspnetcore/blazor/samples/6.0/BlazorSample_Server/Pages/dynamiccomponent/DynamicComponentExample1.razor new file mode 100644 index 000000000000..b1b42f2663a5 --- /dev/null +++ b/aspnetcore/blazor/samples/6.0/BlazorSample_Server/Pages/dynamiccomponent/DynamicComponentExample1.razor @@ -0,0 +1,33 @@ +@page "/dynamiccomponent-example-1" + +

DynamicComponent Component Example 1

+ +

+ +

+ +@if (selectedType is not null) +{ +
+ +
+} + +@code { + private Type? selectedType; + + private void OnDropdownChange(ChangeEventArgs e) + { + selectedType = e.Value?.ToString()?.Length > 0 ? + Type.GetType($"{APP NAMESPACE}.Shared.{e.Value}") : null; + } +} diff --git a/aspnetcore/blazor/samples/6.0/BlazorSample_Server/Pages/dynamiccomponent/DynamicComponentExample2.razor b/aspnetcore/blazor/samples/6.0/BlazorSample_Server/Pages/dynamiccomponent/DynamicComponentExample2.razor new file mode 100644 index 000000000000..b30eec0fc622 --- /dev/null +++ b/aspnetcore/blazor/samples/6.0/BlazorSample_Server/Pages/dynamiccomponent/DynamicComponentExample2.razor @@ -0,0 +1,77 @@ +@page "/dynamiccomponent-example-2" + +

DynamicComponent Component Example 2

+ +

+ +

+ +

+ +

+ +@if (selectedType is not null) +{ +
+ +
+} + +@code { + private Dictionary components = + new() + { + { + "RocketLabWithWindowSeat", + new ComponentMetadata + { + Name = "Rocket Lab with Window Seat", + Parameters = new() { { "WindowSeat", false } } + } + }, + { + "VirginGalactic", + new ComponentMetadata { Name = "Virgin Galactic" } + }, + { + "UnitedLaunchAlliance", + new ComponentMetadata { Name = "ULA" } + }, + { + "SpaceX", + new ComponentMetadata { Name = "SpaceX" } + } + }; + private Type? selectedType; + private bool windowSeat; + + private bool WindowSeat + { + get { return windowSeat; } + set + { + windowSeat = value; + components[nameof(RocketLabWithWindowSeat)].Parameters["WindowSeat"] = + windowSeat; + } + } + + private void OnDropdownChange(ChangeEventArgs e) + { + selectedType = e.Value?.ToString()?.Length > 0 ? + Type.GetType($"{APP NAMESPACE}.Shared.{e.Value}") : null; + } +} diff --git a/aspnetcore/blazor/samples/6.0/BlazorSample_Server/Pages/index/CounterPartialClass.razor b/aspnetcore/blazor/samples/6.0/BlazorSample_Server/Pages/index/CounterPartialClass.razor new file mode 100644 index 000000000000..6c25d9dcdfe3 --- /dev/null +++ b/aspnetcore/blazor/samples/6.0/BlazorSample_Server/Pages/index/CounterPartialClass.razor @@ -0,0 +1,9 @@ +@page "/counter-partial-class" + +Counter + +

Counter

+ +

Current count: @currentCount

+ + diff --git a/aspnetcore/blazor/samples/6.0/BlazorSample_Server/Pages/index/CounterPartialClass.razor.cs b/aspnetcore/blazor/samples/6.0/BlazorSample_Server/Pages/index/CounterPartialClass.razor.cs new file mode 100644 index 000000000000..bb612130ec61 --- /dev/null +++ b/aspnetcore/blazor/samples/6.0/BlazorSample_Server/Pages/index/CounterPartialClass.razor.cs @@ -0,0 +1,12 @@ +namespace BlazorSample.Pages +{ + public partial class CounterPartialClass + { + private int currentCount = 0; + + private void IncrementCount() + { + currentCount++; + } + } +} diff --git a/aspnetcore/blazor/samples/6.0/BlazorSample_Server/Shared/dynamiccomponent/RocketLab.razor b/aspnetcore/blazor/samples/6.0/BlazorSample_Server/Shared/dynamiccomponent/RocketLab.razor new file mode 100644 index 000000000000..5b65edc04664 --- /dev/null +++ b/aspnetcore/blazor/samples/6.0/BlazorSample_Server/Shared/dynamiccomponent/RocketLab.razor @@ -0,0 +1,6 @@ +

Rocket Lab®

+ +

+ Rocket Lab is a registered trademark of + Rocket Lab USA Inc. +

diff --git a/aspnetcore/blazor/samples/6.0/BlazorSample_Server/Shared/dynamiccomponent/RocketLabWithWindowSeat.razor b/aspnetcore/blazor/samples/6.0/BlazorSample_Server/Shared/dynamiccomponent/RocketLabWithWindowSeat.razor new file mode 100644 index 000000000000..9ebbd3b2ee5c --- /dev/null +++ b/aspnetcore/blazor/samples/6.0/BlazorSample_Server/Shared/dynamiccomponent/RocketLabWithWindowSeat.razor @@ -0,0 +1,15 @@ +

Rocket Lab®

+ +

+ User selected a window seat: @WindowSeat +

+ +

+ Rocket Lab is a trademark of + Rocket Lab USA Inc. +

+ +@code { + [Parameter] + public bool WindowSeat { get; set; } +} diff --git a/aspnetcore/blazor/samples/6.0/BlazorSample_Server/Shared/dynamiccomponent/SpaceX.razor b/aspnetcore/blazor/samples/6.0/BlazorSample_Server/Shared/dynamiccomponent/SpaceX.razor new file mode 100644 index 000000000000..f9fe1fa08756 --- /dev/null +++ b/aspnetcore/blazor/samples/6.0/BlazorSample_Server/Shared/dynamiccomponent/SpaceX.razor @@ -0,0 +1,6 @@ +

SpaceX®

+ +

+ SpaceX is a registered trademark of + Space Exploration Technologies Corp. +

diff --git a/aspnetcore/blazor/samples/6.0/BlazorSample_Server/Shared/dynamiccomponent/UnitedLaunchAlliance.razor b/aspnetcore/blazor/samples/6.0/BlazorSample_Server/Shared/dynamiccomponent/UnitedLaunchAlliance.razor new file mode 100644 index 000000000000..217fbc111d64 --- /dev/null +++ b/aspnetcore/blazor/samples/6.0/BlazorSample_Server/Shared/dynamiccomponent/UnitedLaunchAlliance.razor @@ -0,0 +1,6 @@ +

United Launch Alliance®

+ +

+ United Launch Alliance and ULA are registered trademarks of + United Launch Alliance, LLC. +

diff --git a/aspnetcore/blazor/samples/6.0/BlazorSample_Server/Shared/dynamiccomponent/VirginGalactic.razor b/aspnetcore/blazor/samples/6.0/BlazorSample_Server/Shared/dynamiccomponent/VirginGalactic.razor new file mode 100644 index 000000000000..c12d5b34ba85 --- /dev/null +++ b/aspnetcore/blazor/samples/6.0/BlazorSample_Server/Shared/dynamiccomponent/VirginGalactic.razor @@ -0,0 +1,6 @@ +

Virgin Galactic®

+ +

+ Virgin Galactic is a registered trademark of + Galactic Enterprises, LLC. +

diff --git a/aspnetcore/blazor/samples/6.0/BlazorSample_WebAssembly/Pages/dynamiccomponent/DynamicComponentExample1.razor b/aspnetcore/blazor/samples/6.0/BlazorSample_WebAssembly/Pages/dynamiccomponent/DynamicComponentExample1.razor new file mode 100644 index 000000000000..b1b42f2663a5 --- /dev/null +++ b/aspnetcore/blazor/samples/6.0/BlazorSample_WebAssembly/Pages/dynamiccomponent/DynamicComponentExample1.razor @@ -0,0 +1,33 @@ +@page "/dynamiccomponent-example-1" + +

DynamicComponent Component Example 1

+ +

+ +

+ +@if (selectedType is not null) +{ +
+ +
+} + +@code { + private Type? selectedType; + + private void OnDropdownChange(ChangeEventArgs e) + { + selectedType = e.Value?.ToString()?.Length > 0 ? + Type.GetType($"{APP NAMESPACE}.Shared.{e.Value}") : null; + } +} diff --git a/aspnetcore/blazor/samples/6.0/BlazorSample_WebAssembly/Pages/dynamiccomponent/DynamicComponentExample2.razor b/aspnetcore/blazor/samples/6.0/BlazorSample_WebAssembly/Pages/dynamiccomponent/DynamicComponentExample2.razor new file mode 100644 index 000000000000..b30eec0fc622 --- /dev/null +++ b/aspnetcore/blazor/samples/6.0/BlazorSample_WebAssembly/Pages/dynamiccomponent/DynamicComponentExample2.razor @@ -0,0 +1,77 @@ +@page "/dynamiccomponent-example-2" + +

DynamicComponent Component Example 2

+ +

+ +

+ +

+ +

+ +@if (selectedType is not null) +{ +
+ +
+} + +@code { + private Dictionary components = + new() + { + { + "RocketLabWithWindowSeat", + new ComponentMetadata + { + Name = "Rocket Lab with Window Seat", + Parameters = new() { { "WindowSeat", false } } + } + }, + { + "VirginGalactic", + new ComponentMetadata { Name = "Virgin Galactic" } + }, + { + "UnitedLaunchAlliance", + new ComponentMetadata { Name = "ULA" } + }, + { + "SpaceX", + new ComponentMetadata { Name = "SpaceX" } + } + }; + private Type? selectedType; + private bool windowSeat; + + private bool WindowSeat + { + get { return windowSeat; } + set + { + windowSeat = value; + components[nameof(RocketLabWithWindowSeat)].Parameters["WindowSeat"] = + windowSeat; + } + } + + private void OnDropdownChange(ChangeEventArgs e) + { + selectedType = e.Value?.ToString()?.Length > 0 ? + Type.GetType($"{APP NAMESPACE}.Shared.{e.Value}") : null; + } +} diff --git a/aspnetcore/blazor/samples/6.0/BlazorSample_WebAssembly/Pages/index/CounterPartialClass.razor b/aspnetcore/blazor/samples/6.0/BlazorSample_WebAssembly/Pages/index/CounterPartialClass.razor new file mode 100644 index 000000000000..6c25d9dcdfe3 --- /dev/null +++ b/aspnetcore/blazor/samples/6.0/BlazorSample_WebAssembly/Pages/index/CounterPartialClass.razor @@ -0,0 +1,9 @@ +@page "/counter-partial-class" + +Counter + +

Counter

+ +

Current count: @currentCount

+ + diff --git a/aspnetcore/blazor/samples/6.0/BlazorSample_WebAssembly/Pages/index/CounterPartialClass.razor.cs b/aspnetcore/blazor/samples/6.0/BlazorSample_WebAssembly/Pages/index/CounterPartialClass.razor.cs new file mode 100644 index 000000000000..bb612130ec61 --- /dev/null +++ b/aspnetcore/blazor/samples/6.0/BlazorSample_WebAssembly/Pages/index/CounterPartialClass.razor.cs @@ -0,0 +1,12 @@ +namespace BlazorSample.Pages +{ + public partial class CounterPartialClass + { + private int currentCount = 0; + + private void IncrementCount() + { + currentCount++; + } + } +} diff --git a/aspnetcore/blazor/samples/6.0/BlazorSample_WebAssembly/Shared/dynamiccomponent/RocketLab.razor b/aspnetcore/blazor/samples/6.0/BlazorSample_WebAssembly/Shared/dynamiccomponent/RocketLab.razor new file mode 100644 index 000000000000..5b65edc04664 --- /dev/null +++ b/aspnetcore/blazor/samples/6.0/BlazorSample_WebAssembly/Shared/dynamiccomponent/RocketLab.razor @@ -0,0 +1,6 @@ +

Rocket Lab®

+ +

+ Rocket Lab is a registered trademark of + Rocket Lab USA Inc. +

diff --git a/aspnetcore/blazor/samples/6.0/BlazorSample_WebAssembly/Shared/dynamiccomponent/RocketLabWithWindowSeat.razor b/aspnetcore/blazor/samples/6.0/BlazorSample_WebAssembly/Shared/dynamiccomponent/RocketLabWithWindowSeat.razor new file mode 100644 index 000000000000..9ebbd3b2ee5c --- /dev/null +++ b/aspnetcore/blazor/samples/6.0/BlazorSample_WebAssembly/Shared/dynamiccomponent/RocketLabWithWindowSeat.razor @@ -0,0 +1,15 @@ +

Rocket Lab®

+ +

+ User selected a window seat: @WindowSeat +

+ +

+ Rocket Lab is a trademark of + Rocket Lab USA Inc. +

+ +@code { + [Parameter] + public bool WindowSeat { get; set; } +} diff --git a/aspnetcore/blazor/samples/6.0/BlazorSample_WebAssembly/Shared/dynamiccomponent/SpaceX.razor b/aspnetcore/blazor/samples/6.0/BlazorSample_WebAssembly/Shared/dynamiccomponent/SpaceX.razor new file mode 100644 index 000000000000..f9fe1fa08756 --- /dev/null +++ b/aspnetcore/blazor/samples/6.0/BlazorSample_WebAssembly/Shared/dynamiccomponent/SpaceX.razor @@ -0,0 +1,6 @@ +

SpaceX®

+ +

+ SpaceX is a registered trademark of + Space Exploration Technologies Corp. +

diff --git a/aspnetcore/blazor/samples/6.0/BlazorSample_WebAssembly/Shared/dynamiccomponent/UnitedLaunchAlliance.razor b/aspnetcore/blazor/samples/6.0/BlazorSample_WebAssembly/Shared/dynamiccomponent/UnitedLaunchAlliance.razor new file mode 100644 index 000000000000..217fbc111d64 --- /dev/null +++ b/aspnetcore/blazor/samples/6.0/BlazorSample_WebAssembly/Shared/dynamiccomponent/UnitedLaunchAlliance.razor @@ -0,0 +1,6 @@ +

United Launch Alliance®

+ +

+ United Launch Alliance and ULA are registered trademarks of + United Launch Alliance, LLC. +

diff --git a/aspnetcore/blazor/samples/6.0/BlazorSample_WebAssembly/Shared/dynamiccomponent/VirginGalactic.razor b/aspnetcore/blazor/samples/6.0/BlazorSample_WebAssembly/Shared/dynamiccomponent/VirginGalactic.razor new file mode 100644 index 000000000000..c12d5b34ba85 --- /dev/null +++ b/aspnetcore/blazor/samples/6.0/BlazorSample_WebAssembly/Shared/dynamiccomponent/VirginGalactic.razor @@ -0,0 +1,6 @@ +

Virgin Galactic®

+ +

+ Virgin Galactic is a registered trademark of + Galactic Enterprises, LLC. +

From 6c3f9acdaaabc2af04eea51b61544f5e1943bbfd Mon Sep 17 00:00:00 2001 From: guardrex <1622880+guardrex@users.noreply.github.com> Date: Mon, 13 Dec 2021 09:13:56 -0600 Subject: [PATCH 4/5] Updates --- aspnetcore/blazor/call-web-api.md | 18 +++---- aspnetcore/blazor/file-uploads.md | 8 +-- aspnetcore/blazor/forms-validation.md | 22 ++++---- .../fundamentals/dependency-injection.md | 4 +- .../blazor/fundamentals/handle-errors.md | 53 ++++++++++++------- aspnetcore/blazor/fundamentals/routing.md | 23 ++++---- aspnetcore/blazor/fundamentals/signalr.md | 2 +- .../blazor/globalization-localization.md | 2 +- .../host-and-deploy/configure-trimmer.md | 2 +- aspnetcore/blazor/host-and-deploy/server.md | 31 ++--------- .../webassembly-deployment-layout.md | 6 +-- aspnetcore/blazor/includes/state-container.md | 8 ++- aspnetcore/blazor/index.md | 4 +- .../call-javascript-from-dotnet.md | 14 ++--- aspnetcore/blazor/performance.md | 34 ++++++------ .../host-and-deploy/MeasureLatency.razor | 28 ++++++++++ .../6.0/BlazorSample_Server/_Imports.razor | 1 + .../host-and-deploy/MeasureLatency.razor | 28 ++++++++++ .../BlazorSample_WebAssembly/_Imports.razor | 3 +- aspnetcore/blazor/state-management.md | 13 +++-- .../webassembly-lazy-load-assemblies.md | 2 +- 21 files changed, 180 insertions(+), 126 deletions(-) create mode 100644 aspnetcore/blazor/samples/6.0/BlazorSample_Server/Shared/host-and-deploy/MeasureLatency.razor create mode 100644 aspnetcore/blazor/samples/6.0/BlazorSample_WebAssembly/Shared/host-and-deploy/MeasureLatency.razor diff --git a/aspnetcore/blazor/call-web-api.md b/aspnetcore/blazor/call-web-api.md index 94ab38689412..f010ba690209 100644 --- a/aspnetcore/blazor/call-web-api.md +++ b/aspnetcore/blazor/call-web-api.md @@ -40,7 +40,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; } } ``` @@ -109,7 +109,7 @@ else } @code { - private TodoItem[] todoItems; + private TodoItem[]? todoItems; protected override async Task OnInitializedAsync() => todoItems = await Http.GetFromJsonAsync("api/TodoItems"); @@ -132,7 +132,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() { @@ -152,7 +152,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) @@ -258,7 +258,7 @@ else } @code { - private WeatherForecast[] forecasts; + private WeatherForecast[]? forecasts; protected override async Task OnInitializedAsync() { @@ -351,7 +351,7 @@ else } @code { - private WeatherForecast[] forecasts; + private WeatherForecast[]? forecasts; protected override async Task OnInitializedAsync() { @@ -456,8 +456,8 @@ else

@code { - private WeatherForecast[] forecasts; - private string exceptionMessage; + private WeatherForecast[]? forecasts; + private string? exceptionMessage; protected override async Task OnInitializedAsync() { diff --git a/aspnetcore/blazor/file-uploads.md b/aspnetcore/blazor/file-uploads.md index 596db7b7ddc1..783aa3b0c532 100644 --- a/aspnetcore/blazor/file-uploads.md +++ b/aspnetcore/blazor/file-uploads.md @@ -173,8 +173,8 @@ namespace BlazorSample.Shared public class UploadResult { public bool Uploaded { get; set; } - public string FileName { get; set; } - public string StoredFileName { get; set; } + public string? FileName { get; set; } + public string? StoredFileName { get; set; } public int ErrorCode { get; set; } } } @@ -192,8 +192,8 @@ The following `UploadResult` class is placed in the client project and in the we public class UploadResult { public bool Uploaded { get; set; } - public string FileName { get; set; } - public string StoredFileName { get; set; } + public string? FileName { get; set; } + public string? StoredFileName { get; set; } public int ErrorCode { get; set; } } ``` diff --git a/aspnetcore/blazor/forms-validation.md b/aspnetcore/blazor/forms-validation.md index 35821ab4fdab..1e55ab68b452 100644 --- a/aspnetcore/blazor/forms-validation.md +++ b/aspnetcore/blazor/forms-validation.md @@ -61,7 +61,7 @@ Assignment to @code { - private EditContext editContext; + private EditContext? editContext; private Starship starship = new(); protected override void OnInitialized() @@ -620,14 +620,14 @@ In the following `FormExample6` component, update the namespace of the **`Shared @code { private bool disabled; - private string message; - private string messageStyles = "visibility:hidden"; - private CustomValidation customValidation; + private string? message; + private string? messageStyles = "visibility:hidden"; + private CustomValidation? customValidation; private Starship starship = new() { ProductionDate = DateTime.UtcNow }; private async Task HandleValidSubmit(EditContext editContext) { - customValidation.ClearErrors(); + customValidation?.ClearErrors(); try { @@ -640,7 +640,7 @@ In the following `FormExample6` component, update the namespace of the **`Shared if (response.StatusCode == HttpStatusCode.BadRequest && errors.Any()) { - customValidation.DisplayErrors(errors); + customValidation?.DisplayErrors(errors); } else if (!response.IsSuccessStatusCode) { @@ -888,7 +888,7 @@ Add an additional property to `ExampleModel`, for example: ```csharp [StringLength(10, ErrorMessage = "Description is too long.")] -public string Description { get; set; } +public string? Description { get; set; } ``` Add the `Description` to the `ExampleForm7` component's form: @@ -900,7 +900,7 @@ Add the `Description` to the `ExampleForm7` component's form: Update the `EditContext` instance in the component's `OnInitialized` method to use the new Field CSS Class Provider: ```csharp -editContext.SetFieldCssClassProvider(new CustomFieldClassProvider2()); +editContext?.SetFieldCssClassProvider(new CustomFieldClassProvider2()); ``` Because a CSS validation class isn't applied to the `Description` field (`id="description"`), it isn't styled. However, field validation runs normally. If more than 10 characters are provided, the validation summary indicates the error: @@ -986,11 +986,11 @@ public class ShipDescription { [Required] [StringLength(40, ErrorMessage = "Description too long (40 char).")] - public string ShortDescription { get; set; } + public string? ShortDescription { get; set; } [Required] [StringLength(240, ErrorMessage = "Description too long (240 char).")] - public string LongDescription { get; set; } + public string? LongDescription { get; set; } } ``` diff --git a/aspnetcore/blazor/fundamentals/dependency-injection.md b/aspnetcore/blazor/fundamentals/dependency-injection.md index 1f819545e814..963aedefa8cf 100644 --- a/aspnetcore/blazor/fundamentals/dependency-injection.md +++ b/aspnetcore/blazor/fundamentals/dependency-injection.md @@ -294,12 +294,12 @@ public interface ITransitiveTransientDisposableDependency public class TransientDependency { private readonly ITransitiveTransientDisposableDependency - _transitiveTransientDisposableDependency; + transitiveTransientDisposableDependency; public TransientDependency(ITransitiveTransientDisposableDependency transitiveTransientDisposableDependency) { - _transitiveTransientDisposableDependency = + this.transitiveTransientDisposableDependency = transitiveTransientDisposableDependency; } } diff --git a/aspnetcore/blazor/fundamentals/handle-errors.md b/aspnetcore/blazor/fundamentals/handle-errors.md index c65d5524aff7..469aabcc3dd7 100644 --- a/aspnetcore/blazor/fundamentals/handle-errors.md +++ b/aspnetcore/blazor/fundamentals/handle-errors.md @@ -183,7 +183,7 @@ Because the error boundary is defined in the layout in the preceding examples, t ... @code { - private ErrorBoundary errorBoundary; + private ErrorBoundary? errorBoundary; protected override void OnParametersSet() { @@ -210,7 +210,7 @@ The following `Error` component example merely logs errors, but methods of the c @code { [Parameter] - public RenderFragment ChildContent { get; set; } + public RenderFragment? ChildContent { get; set; } public void ProcessError(Exception ex) { @@ -234,34 +234,51 @@ In the `App` component, wrap the `Router` component with the `Error` component. To process errors in a component: -* Designate the `Error` component as a [`CascadingParameter`](xref:blazor/components/cascading-values-and-parameters#cascadingparameter-attribute) in the [`@code`](xref:mvc/views/razor#code) block: +* Designate the `Error` component as a [`CascadingParameter`](xref:blazor/components/cascading-values-and-parameters#cascadingparameter-attribute) in the [`@code`](xref:mvc/views/razor#code) block. In an example `Counter` component in an app based on a Blazor project template, add the following `Error` property: - ```razor + ```csharp [CascadingParameter] - public Error Error { get; set; } + public Error? Error { get; set; } ``` -* Call an error processing method in any `catch` block with an appropriate exception type. The example `Error` component only offers a single `ProcessError` method, but the error processing component can provide any number of error processing methods to address alternative error processing requirements throughout the app. +* Call an error processing method in any `catch` block with an appropriate exception type. The example `Error` component only offers a single `ProcessError` method, but the error processing component can provide any number of error processing methods to address alternative error processing requirements throughout the app. In the following `Counter` component example, an exception is thrown and trapped when the count is greater than five: - ```csharp - try - { - ... - } - catch (Exception ex) - { - Error.ProcessError(ex); + ```razor + @code { + private int currentCount = 0; + + [CascadingParameter] + public Error? Error { get; set; } + + private void IncrementCount() + { + try + { + currentCount++; + + if (currentCount > 5) + { + throw new InvalidOperationException("Current count is over five!"); + } + } + catch (Exception ex) + { + Error?.ProcessError(ex); + } + } } ``` -Using the preceding example `Error` component and `ProcessError` method, the browser's developer tools console indicates the trapped, logged error: +Using the preceding `Error` component with the preceding changes made to a `Counter` component, the browser's developer tools console indicates the trapped, logged error: -> fail: BlazorSample.Shared.Error[0] -> Error:ProcessError - Type: System.NullReferenceException Message: Object reference not set to an instance of an object. +```console +fail: BlazorSample.Shared.Error[0] +Error:ProcessError - Type: System.InvalidOperationException Message: Current count is over five! +``` If the `ProcessError` method directly participates in rendering, such as showing a custom error message bar or changing the CSS styles of the rendered elements, call [`StateHasChanged`](xref:blazor/components/lifecycle#state-changes-statehaschanged) at the end of the `ProcessErrors` method to rerender the UI. -Because the approaches in this section handle errors with a [`try-catch`](/dotnet/csharp/language-reference/keywords/try-catch) statement, a Blazor Server app's SignalR connection between the client and server isn't broken when an error occurs and the circuit remains alive. Any unhandled exception is fatal to a circuit. For more information, see the preceding section on [how a Blazor Server app reacts to unhandled exceptions](#how-a-blazor-server-app-reacts-to-unhandled-exceptions). +Because the approaches in this section handle errors with a [`try-catch`](/dotnet/csharp/language-reference/keywords/try-catch) statement, a Blazor Server app's SignalR connection between the client and server isn't broken when an error occurs and the circuit remains alive. Other unhandled exceptions remain fatal to a circuit. For more information, see the preceding section on [how a Blazor Server app reacts to unhandled exceptions](#how-a-blazor-server-app-reacts-to-unhandled-exceptions). ## Log errors with a persistent provider diff --git a/aspnetcore/blazor/fundamentals/routing.md b/aspnetcore/blazor/fundamentals/routing.md index b3558123c2e5..3fdd88ba6e78 100644 --- a/aspnetcore/blazor/fundamentals/routing.md +++ b/aspnetcore/blazor/fundamentals/routing.md @@ -265,7 +265,7 @@ Specify the `[SupplyParameterFromQuery]` attribute's Page: @Page

-

Assignees:

+@if (Stars is not null) +{ +

Assignees:

-
    - @foreach (var name in Stars) - { -
  • @name
  • - } -
+
    + @foreach (var name in Stars) + { +
  • @name
  • + } +
+} @code { [Parameter] [SupplyParameterFromQuery] - public string Filter { get; set; } + public string? Filter { get; set; } [Parameter] [SupplyParameterFromQuery] @@ -305,7 +308,7 @@ In the following example with a URL of `/search?filter=scifi%20stars&page=3&star [Parameter] [SupplyParameterFromQuery(Name = "star")] - public string[] Stars { get; set; } + public string[]? Stars { get; set; } } ``` diff --git a/aspnetcore/blazor/fundamentals/signalr.md b/aspnetcore/blazor/fundamentals/signalr.md index 795cdd2b6b20..ad0aecba521c 100644 --- a/aspnetcore/blazor/fundamentals/signalr.md +++ b/aspnetcore/blazor/fundamentals/signalr.md @@ -45,7 +45,7 @@ To configure SignalR's underlying client to send credentials, such as cookies or * Where a hub connection is built, assign the to the option: ```csharp - HubConnectionBuilder hubConnecton; + private HubConnectionBuilder? hubConnecton; ... diff --git a/aspnetcore/blazor/globalization-localization.md b/aspnetcore/blazor/globalization-localization.md index 1a87e352baaf..44959e00e9d1 100644 --- a/aspnetcore/blazor/globalization-localization.md +++ b/aspnetcore/blazor/globalization-localization.md @@ -856,7 +856,7 @@ Add the namespace for @code { - private string greeting; + private string? greeting; protected override void OnInitialized() { diff --git a/aspnetcore/blazor/host-and-deploy/configure-trimmer.md b/aspnetcore/blazor/host-and-deploy/configure-trimmer.md index feb1246c5098..2ce0067974b5 100644 --- a/aspnetcore/blazor/host-and-deploy/configure-trimmer.md +++ b/aspnetcore/blazor/host-and-deploy/configure-trimmer.md @@ -24,7 +24,7 @@ To configure the Trimmer, see the [Trimming options](/dotnet/core/deploying/trim * Stop the Trimmer from trimming specific assemblies. * "Root" assemblies for trimming. * Surface warnings for reflected types by setting the `` property to `false` in the project file. -* Control symbol trimming and degugger support. +* Control symbol trimming and debugger support. * Set Trimmer features for trimming framework library features. ## Additional resources diff --git a/aspnetcore/blazor/host-and-deploy/server.md b/aspnetcore/blazor/host-and-deploy/server.md index fe7533109272..bc86c5ee6e67 100644 --- a/aspnetcore/blazor/host-and-deploy/server.md +++ b/aspnetcore/blazor/host-and-deploy/server.md @@ -201,36 +201,11 @@ For more information, see the [Apache documentation](https://httpd.apache.org/do ## Measure network latency -[JS interop](xref:blazor/js-interop/call-javascript-from-dotnet) can be used to measure network latency, as the following example demonstrates: - -```razor -@inject IJSRuntime JS - -@if (latency is null) -{ - Calculating... -} -else -{ - @(latency.Value.TotalMilliseconds)ms -} +[JS interop](xref:blazor/js-interop/call-javascript-from-dotnet) can be used to measure network latency, as the following example demonstrates. -@code { - private DateTime startTime; - private TimeSpan? latency; +`Shared/MeasureLatency.razor`: - protected override async Task OnAfterRenderAsync(bool firstRender) - { - if (firstRender) - { - startTime = DateTime.UtcNow; - var _ = await JS.InvokeAsync("toString"); - latency = DateTime.UtcNow - startTime; - StateHasChanged(); - } - } -} -``` +[!code-razor[](~/blazor/samples/6.0/BlazorSample_WebAssembly/Shared/host-and-deploy/MeasureLatency.razor)] For a reasonable UI experience, we recommend a sustained UI latency of 250 ms or less. diff --git a/aspnetcore/blazor/host-and-deploy/webassembly-deployment-layout.md b/aspnetcore/blazor/host-and-deploy/webassembly-deployment-layout.md index 3c562fae1b26..764a56732bf4 100644 --- a/aspnetcore/blazor/host-and-deploy/webassembly-deployment-layout.md +++ b/aspnetcore/blazor/host-and-deploy/webassembly-deployment-layout.md @@ -138,13 +138,13 @@ namespace Microsoft.AspNetCore.Components.WebAssembly.MultipartBundle.Tasks public class BundleBlazorAssets : Task { [Required] - public ITaskItem[] PublishBlazorBootStaticWebAsset { get; set; } + public ITaskItem[]? PublishBlazorBootStaticWebAsset { get; set; } [Required] - public string BundlePath { get; set; } + public string? BundlePath { get; set; } [Output] - public ITaskItem[] Extension { get; set; } + public ITaskItem[]? Extension { get; set; } public override bool Execute() { diff --git a/aspnetcore/blazor/includes/state-container.md b/aspnetcore/blazor/includes/state-container.md index 433ef90a3ae1..4c575c134c68 100644 --- a/aspnetcore/blazor/includes/state-container.md +++ b/aspnetcore/blazor/includes/state-container.md @@ -9,15 +9,13 @@ Nested components typically bind data using *chained bind* as described in savedString; + get => savedString ?? string.Empty; set { savedString = value; @@ -25,7 +23,7 @@ public class StateContainer } } - public event Action OnChange; + public event Action? OnChange; private void NotifyStateChanged() => OnChange?.Invoke(); } diff --git a/aspnetcore/blazor/index.md b/aspnetcore/blazor/index.md index 697f91c5b7b1..d3bdfd22d72b 100644 --- a/aspnetcore/blazor/index.md +++ b/aspnetcore/blazor/index.md @@ -55,10 +55,10 @@ Blazor uses natural HTML tags for UI composition. The following Razor markup dem @code { [Parameter] - public RenderFragment ChildContent { get; set; } + public RenderFragment? ChildContent { get; set; } [Parameter] - public string Title { get; set; } + public string? Title { get; set; } private void OnYes() { diff --git a/aspnetcore/blazor/javascript-interoperability/call-javascript-from-dotnet.md b/aspnetcore/blazor/javascript-interoperability/call-javascript-from-dotnet.md index 1f9aa9b5d36a..0fb39a69cc87 100644 --- a/aspnetcore/blazor/javascript-interoperability/call-javascript-from-dotnet.md +++ b/aspnetcore/blazor/javascript-interoperability/call-javascript-from-dotnet.md @@ -250,7 +250,7 @@ The following example shows capturing a reference to the `username` `` el > > For more information, see . -An is passed through to JS code via JS interop. The JS code receives an `HTMLElement` instance, which it can use with normal DOM APIs. For example, the following code defines a .NET extension method (`TriggerClickEvent`) that enables sending a mouse click to an element. +An is passed through to JS code via JS interop. The JS code receives an [`HTMLElement`](https://developer.mozilla.org/docs/Web/API/HTMLElement) instance, which it can use with normal DOM APIs. For example, the following code defines a .NET extension method (`TriggerClickEvent`) that enables sending a mouse click to an element. The JS function `clickElement` creates a [`click`](https://developer.mozilla.org/docs/Web/API/Element/click_event) event on the passed HTML element (`element`): @@ -347,7 +347,7 @@ The `{JAVASCRIPT FUNCTION}` placeholder is the JS function identifier. @code { private ElementReference username; - private string returnValue; + private string? returnValue; private async Task OnClickMethod() { @@ -458,7 +458,7 @@ The following example demonstrates the concept. Within the `if` statement when `
@code { - private HtmlElement unmanagedElement; + private ElementReference unmanagedElement; protected override async Task OnAfterRenderAsync(bool firstRender) { @@ -563,7 +563,7 @@ Inside the closing `` tag of `wwwroot/index.html` (Blazor WebAssembly) or

@code { - private string result; + private string? result; private async Task SendByteArray() { @@ -956,7 +956,7 @@ The following example shows capturing a reference to the `username` `` el > > For more information, see . -An is passed through to JS code via JS interop. The JS code receives an `HTMLElement` instance, which it can use with normal DOM APIs. For example, the following code defines a .NET extension method (`TriggerClickEvent`) that enables sending a mouse click to an element. +An is passed through to JS code via JS interop. The JS code receives an [`HTMLElement`](https://developer.mozilla.org/docs/Web/API/HTMLElement) instance, which it can use with normal DOM APIs. For example, the following code defines a .NET extension method (`TriggerClickEvent`) that enables sending a mouse click to an element. The JS function `clickElement` creates a [`click`](https://developer.mozilla.org/docs/Web/API/Element/click_event) event on the passed HTML element (`element`): @@ -1164,7 +1164,7 @@ The following example demonstrates the concept. Within the `if` statement when `
@code { - private HtmlElement unmanagedElement; + private ElementReference unmanagedElement; protected override async Task OnAfterRenderAsync(bool firstRender) { @@ -1510,7 +1510,7 @@ The following example shows capturing a reference to the `username` `` el > > For more information, see . -An is passed through to JS code via JS interop. The JS code receives an `HTMLElement` instance, which it can use with normal DOM APIs. For example, the following code defines a .NET extension method (`TriggerClickEvent`) that enables sending a mouse click to an element. +An is passed through to JS code via JS interop. The JS code receives an [`HTMLElement`](https://developer.mozilla.org/docs/Web/API/HTMLElement) instance, which it can use with normal DOM APIs. For example, the following code defines a .NET extension method (`TriggerClickEvent`) that enables sending a mouse click to an element. The JS function `clickElement` creates a [`click`](https://developer.mozilla.org/docs/Web/API/Element/click_event) event on the passed HTML element (`element`): diff --git a/aspnetcore/blazor/performance.md b/aspnetcore/blazor/performance.md index 2918a3dbcd56..a398aa998642 100644 --- a/aspnetcore/blazor/performance.md +++ b/aspnetcore/blazor/performance.md @@ -42,23 +42,23 @@ The following airline flight search tool example uses private fields to track th ```razor @code { - private int prevInboundFlightId; - private int prevOutboundFlightId; + private int prevInboundFlightId = 0; + private int prevOutboundFlightId = 0; private bool shouldRender; [Parameter] - public FlightInfo InboundFlight { get; set; } + public FlightInfo? InboundFlight { get; set; } [Parameter] - public FlightInfo OutboundFlight { get; set; } + public FlightInfo? OutboundFlight { get; set; } protected override void OnParametersSet() { - shouldRender = InboundFlight.FlightId != prevInboundFlightId - || OutboundFlight.FlightId != prevOutboundFlightId; + shouldRender = InboundFlight?.FlightId != prevInboundFlightId + || OutboundFlight?.FlightId != prevOutboundFlightId; - prevInboundFlightId = InboundFlight.FlightId; - prevOutboundFlightId = OutboundFlight.FlightId; + prevInboundFlightId = InboundFlight?.FlightId ?? 0; + prevOutboundFlightId = OutboundFlight?.FlightId ?? 0; } protected override bool ShouldRender() => shouldRender; @@ -126,7 +126,7 @@ Consider the following portion of a parent component that renders child componen @code { [Parameter] - public ChatMessage Message { get; set; } + public ChatMessage? Message { get; set; } } ``` @@ -227,10 +227,10 @@ To reduce parameter load, bundle multiple parameters in a custom class. For exam @code { [Parameter] - public TItem Data { get; set; } + public TItem? Data { get; set; } [Parameter] - public GridOptions Options { get; set; } + public GridOptions? Options { get; set; } } ``` @@ -273,7 +273,7 @@ Components can elect to receive "unmatched" parameter values using the OtherAttributes { get; set; } + public IDictionary? OtherAttributes { get; set; } } ``` @@ -304,7 +304,7 @@ In extreme cases, you can override the component's virtual TextChanged { get; set; } @@ -362,7 +362,7 @@ Rather than use native events that rapidly fire, consider the use of JS interop @code { private ElementReference mouseMoveElement; - private DotNetObjectReference selfReference; + private DotNetObjectReference? selfReference; private string message = "Move the mouse in the box"; [JSInvokable] @@ -436,7 +436,7 @@ In the following example, no event handler added to the component triggers a rer } Task IHandleEvent.HandleEventAsync( - EventCallbackWorkItem callback, object arg) => callback.InvokeAsync(arg); + EventCallbackWorkItem callback, object? arg) => callback.InvokeAsync(arg); } ``` @@ -598,8 +598,8 @@ If a large number of buttons are rendered using the preceding approach, renderin private class Button { - public string Id { get; set; } - public Action Action { get; set; } + public string? Id { get; set; } + public Action Action { get; set; } = e => { }; } } ``` diff --git a/aspnetcore/blazor/samples/6.0/BlazorSample_Server/Shared/host-and-deploy/MeasureLatency.razor b/aspnetcore/blazor/samples/6.0/BlazorSample_Server/Shared/host-and-deploy/MeasureLatency.razor new file mode 100644 index 000000000000..d21c5538ac78 --- /dev/null +++ b/aspnetcore/blazor/samples/6.0/BlazorSample_Server/Shared/host-and-deploy/MeasureLatency.razor @@ -0,0 +1,28 @@ +@inject IJSRuntime JS + +

Measure Latency

+ +@if (latency is null) +{ + Calculating... +} +else +{ + @(latency.Value.TotalMilliseconds)ms +} + +@code { + private DateTime startTime; + private TimeSpan? latency; + + protected override async Task OnAfterRenderAsync(bool firstRender) + { + if (firstRender) + { + startTime = DateTime.UtcNow; + var _ = await JS.InvokeAsync("toString"); + latency = DateTime.UtcNow - startTime; + StateHasChanged(); + } + } +} diff --git a/aspnetcore/blazor/samples/6.0/BlazorSample_Server/_Imports.razor b/aspnetcore/blazor/samples/6.0/BlazorSample_Server/_Imports.razor index 777197785cd1..f11f67a7d7ba 100644 --- a/aspnetcore/blazor/samples/6.0/BlazorSample_Server/_Imports.razor +++ b/aspnetcore/blazor/samples/6.0/BlazorSample_Server/_Imports.razor @@ -15,3 +15,4 @@ @using BlazorSample.Shared.index @using BlazorSample.Shared.forms_and_validation @using BlazorSample.Shared.call_dotnet_from_js +@using BlazorSample.Shared.host_and_deploy diff --git a/aspnetcore/blazor/samples/6.0/BlazorSample_WebAssembly/Shared/host-and-deploy/MeasureLatency.razor b/aspnetcore/blazor/samples/6.0/BlazorSample_WebAssembly/Shared/host-and-deploy/MeasureLatency.razor new file mode 100644 index 000000000000..d21c5538ac78 --- /dev/null +++ b/aspnetcore/blazor/samples/6.0/BlazorSample_WebAssembly/Shared/host-and-deploy/MeasureLatency.razor @@ -0,0 +1,28 @@ +@inject IJSRuntime JS + +

Measure Latency

+ +@if (latency is null) +{ + Calculating... +} +else +{ + @(latency.Value.TotalMilliseconds)ms +} + +@code { + private DateTime startTime; + private TimeSpan? latency; + + protected override async Task OnAfterRenderAsync(bool firstRender) + { + if (firstRender) + { + startTime = DateTime.UtcNow; + var _ = await JS.InvokeAsync("toString"); + latency = DateTime.UtcNow - startTime; + StateHasChanged(); + } + } +} diff --git a/aspnetcore/blazor/samples/6.0/BlazorSample_WebAssembly/_Imports.razor b/aspnetcore/blazor/samples/6.0/BlazorSample_WebAssembly/_Imports.razor index 9284aa38907b..9f57129f2958 100644 --- a/aspnetcore/blazor/samples/6.0/BlazorSample_WebAssembly/_Imports.razor +++ b/aspnetcore/blazor/samples/6.0/BlazorSample_WebAssembly/_Imports.razor @@ -14,4 +14,5 @@ @using BlazorSample.Shared.advanced_scenarios @using BlazorSample.Shared.index @using BlazorSample.Shared.forms_and_validation -@using BlazorSample.Shared.call_dotnet_from_js \ No newline at end of file +@using BlazorSample.Shared.call_dotnet_from_js +@using BlazorSample.Shared.host_and_deploy diff --git a/aspnetcore/blazor/state-management.md b/aspnetcore/blazor/state-management.md index b484cdeea806..5c03c8206b4b 100644 --- a/aspnetcore/blazor/state-management.md +++ b/aspnetcore/blazor/state-management.md @@ -384,7 +384,7 @@ else private bool isLoaded; [Parameter] - public RenderFragment ChildContent { get; set; } + public RenderFragment? ChildContent { get; set; } public int CurrentCount { get; set; } @@ -419,17 +419,20 @@ Wrapped components receive and can modify the persisted counter state. The follo ```razor @page "/counter" -

Current count: @CounterStateProvider.CurrentCount

+

Current count: @CounterStateProvider?.CurrentCount

@code { [CascadingParameter] - private CounterStateProvider CounterStateProvider { get; set; } + private CounterStateProvider? CounterStateProvider { get; set; } private async Task IncrementCount() { - CounterStateProvider.CurrentCount++; - await CounterStateProvider.SaveChangesAsync(); + if (CounterStateProvider is not null) + { + CounterStateProvider.CurrentCount++; + await CounterStateProvider.SaveChangesAsync(); + } } } ``` diff --git a/aspnetcore/blazor/webassembly-lazy-load-assemblies.md b/aspnetcore/blazor/webassembly-lazy-load-assemblies.md index b712ddfccf85..e83a28cb0b55 100644 --- a/aspnetcore/blazor/webassembly-lazy-load-assemblies.md +++ b/aspnetcore/blazor/webassembly-lazy-load-assemblies.md @@ -255,7 +255,7 @@ The demonstration in this section: @code { private RobotModel robotModel = new() { AxisSelection = Axis.Left }; - private string message; + private string? message; private void HandleValidSubmit() { From 9448d74acfc65e51575261177fdf326ba113fb27 Mon Sep 17 00:00:00 2001 From: guardrex <1622880+guardrex@users.noreply.github.com> Date: Tue, 14 Dec 2021 07:52:13 -0600 Subject: [PATCH 5/5] Updates --- .../ContactsDb.db-shm | Bin 32768 -> 32768 bytes .../ContactsDb.db-wal | Bin 152472 -> 168952 bytes .../Pages/AddContact.razor | 2 +- .../Pages/EditContact.razor | 4 ++-- .../Pages/Index.razor | 12 ++++++------ .../Pages/ViewContact.razor | 6 +++--- .../Shared/ContactForm.razor | 2 +- .../Shared/ContactRow.razor | 4 ++-- 8 files changed, 15 insertions(+), 15 deletions(-) diff --git a/aspnetcore/blazor/samples/6.0/BlazorServerEFCoreSample/BlazorServerDbContextExample/ContactsDb.db-shm b/aspnetcore/blazor/samples/6.0/BlazorServerEFCoreSample/BlazorServerDbContextExample/ContactsDb.db-shm index 52068b1f1daf8e96904d232ce06ab1cbea37943a..3670481acb49877e4860ce89fd44ca43ad4f2a10 100644 GIT binary patch delta 195 zcmZo@U}|V!s+V}A%K!o_K+MR%AfO4P`GENF^P KZT=L(rwsr_fi}hf diff --git a/aspnetcore/blazor/samples/6.0/BlazorServerEFCoreSample/BlazorServerDbContextExample/ContactsDb.db-wal b/aspnetcore/blazor/samples/6.0/BlazorServerEFCoreSample/BlazorServerDbContextExample/ContactsDb.db-wal index aa0235a3be29f2ce523e25efe85137c51f60e5e9..8e1052b0eb3dfccb84b4a0b7de79bdd711083b0f 100644 GIT binary patch delta 223 zcmbQSob$(Wu7(!IEle|lWf>S4*npVt?dHna0%h9EV))d2y{=A_;$?ZpU@$rMpu_b0 z&P;0CnO&HwS(w;rw>Nk&O=V;O>hPX^QIbh2HWre`QK ztzxpYoUBu%wcSpINta)g71@exmG&^BV96&Sbt_pcfMBw2ktRf?42nwbu*-*^tdAGq l1!^~BxHdgsnJJ!)QIX*q!?lTxVvOO_Un?`Ip_rh>4*=C_LeT&K delta 13 UcmeydoNLB%&W0AoEle|l0W0(cm;e9( diff --git a/aspnetcore/blazor/samples/6.0/BlazorServerEFCoreSample/BlazorServerDbContextExample/Pages/AddContact.razor b/aspnetcore/blazor/samples/6.0/BlazorServerEFCoreSample/BlazorServerDbContextExample/Pages/AddContact.razor index 1f3804ec6baf..6fa794ecf94b 100644 --- a/aspnetcore/blazor/samples/6.0/BlazorServerEFCoreSample/BlazorServerDbContextExample/Pages/AddContact.razor +++ b/aspnetcore/blazor/samples/6.0/BlazorServerEFCoreSample/BlazorServerDbContextExample/Pages/AddContact.razor @@ -4,7 +4,7 @@ @inject NavigationManager Nav @inject IPageHelper PageHelper -@if (Contact != null) +@if (Contact is not null) { Could not find contact with id @ContactId.

} @@ -154,7 +154,7 @@ else // get values from database var dbValues = dbex.Entries[0].GetDatabaseValues(); - if (dbValues == null) + if (dbValues is null) { // deleted - show contact not found Nav.NavigateTo($"/view/{Contact.Id}"); diff --git a/aspnetcore/blazor/samples/6.0/BlazorServerEFCoreSample/BlazorServerDbContextExample/Pages/Index.razor b/aspnetcore/blazor/samples/6.0/BlazorServerEFCoreSample/BlazorServerDbContextExample/Pages/Index.razor index e84758d8bd85..758bb44ae551 100644 --- a/aspnetcore/blazor/samples/6.0/BlazorServerEFCoreSample/BlazorServerDbContextExample/Pages/Index.razor +++ b/aspnetcore/blazor/samples/6.0/BlazorServerEFCoreSample/BlazorServerDbContextExample/Pages/Index.razor @@ -63,7 +63,7 @@  ZipCode - @if (Filters.Loading || Contacts == null) + @if (Filters.Loading || Contacts is null) {
@@ -71,7 +71,7 @@
} - @if (Contacts != null && Contacts.Count == 0) + @if (Contacts is not null && Contacts.Count == 0) {
@@ -79,7 +79,7 @@
} - @if (Contacts != null) + @if (Contacts is not null) { @foreach (var contact in Contacts) { @@ -202,12 +202,12 @@ using var context = DbFactory.CreateDbContext(); Filters.Loading = true; - if (Wrapper is not null) + if (Wrapper is not null && context.Contacts is not null) { - var contact = await context.Contacts? + var contact = await context.Contacts .FirstAsync(c => c.Id == Wrapper.DeleteRequestId); - if (contact != null) + if (contact is not null) { context.Contacts?.Remove(contact); await context.SaveChangesAsync(); diff --git a/aspnetcore/blazor/samples/6.0/BlazorServerEFCoreSample/BlazorServerDbContextExample/Pages/ViewContact.razor b/aspnetcore/blazor/samples/6.0/BlazorServerEFCoreSample/BlazorServerDbContextExample/Pages/ViewContact.razor index 776e4e948963..7cdd3474dff0 100644 --- a/aspnetcore/blazor/samples/6.0/BlazorServerEFCoreSample/BlazorServerDbContextExample/Pages/ViewContact.razor +++ b/aspnetcore/blazor/samples/6.0/BlazorServerEFCoreSample/BlazorServerDbContextExample/Pages/ViewContact.razor @@ -12,7 +12,7 @@ } else { - @if (Contact == null || Loading) + @if (Contact is null || Loading) {
Loading...
} @@ -160,7 +160,7 @@ else .SingleOrDefaultAsync( c => c.Id == ContactId); - if (Contact == null) + if (Contact is null) { NotFound = true; } @@ -188,7 +188,7 @@ else var contact = await context.Contacts .SingleOrDefaultAsync(c => c.Id == ContactId); - if (contact != null) + if (contact is not null) { context.Contacts?.Remove(contact); await context.SaveChangesAsync(); diff --git a/aspnetcore/blazor/samples/6.0/BlazorServerEFCoreSample/BlazorServerDbContextExample/Shared/ContactForm.razor b/aspnetcore/blazor/samples/6.0/BlazorServerEFCoreSample/BlazorServerDbContextExample/Shared/ContactForm.razor index e9bec004579a..2161a742d88a 100644 --- a/aspnetcore/blazor/samples/6.0/BlazorServerEFCoreSample/BlazorServerDbContextExample/Shared/ContactForm.razor +++ b/aspnetcore/blazor/samples/6.0/BlazorServerEFCoreSample/BlazorServerDbContextExample/Shared/ContactForm.razor @@ -2,7 +2,7 @@

@Mode Contact


-@if (Contact != null) +@if (Contact is not null) {
@@ -52,7 +52,7 @@ get => _currentContact; set { - if (value != null && !value.Equals(_currentContact)) + if (value is not null && !value.Equals(_currentContact)) { _currentContact = value; DeleteConfirmation = false;