From 523f9778210f230c0524e101de21c5ad5e3c192c Mon Sep 17 00:00:00 2001 From: Luke Latham <1622880+guardrex@users.noreply.github.com> Date: Fri, 26 Jul 2019 06:21:00 -0500 Subject: [PATCH 1/7] Stricter @key (#13504) --- aspnetcore/blazor/components.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aspnetcore/blazor/components.md b/aspnetcore/blazor/components.md index 7958ad03c4..190fdb4d80 100644 --- a/aspnetcore/blazor/components.md +++ b/aspnetcore/blazor/components.md @@ -566,7 +566,7 @@ Generally, it makes sense to supply one of the following kinds of value for `@ke * Model object instances (for example, a `Person` instance as in the earlier example). This ensures preservation based on object reference equality. * Unique identifiers (for example, primary key values of type `int`, `string`, or `Guid`). -Avoid supplying a value that can clash unexpectedly. If `@key="@someObject.GetHashCode()"` is supplied, unexpected clashes may occur because the hash codes of unrelated objects can be the same. If clashing `@key` values are requested within the same parent, the `@key` values won't be honored. +Ensure that values used for `@key` 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. ## Lifecycle methods From db1d0bdd5cac97eeed6c329f8249f70cbab61afc Mon Sep 17 00:00:00 2001 From: "N. Taylor Mullen" Date: Fri, 26 Jul 2019 10:41:34 -0700 Subject: [PATCH 2/7] Document public [Parameter] expectation in 3.0.0-preview8. - A lot of the parameter documentation was already present (such as not setting parameters outside of their declaring class). - Updated all `[Parameter]` locations to utilize `public` properties with `public` setters. - Updated all the locations I could find where we were suggesting users to utilize non-public parameters. #13453 --- .../Components/ChildComponent.razor | 6 ++-- .../Components/ListViewTemplate.razor | 4 +-- .../3.x/BlazorSample/Components/Tab.razor | 6 ++-- .../3.x/BlazorSample/Components/TabSet.razor | 2 +- .../Components/TableTemplate.razor | 8 ++--- .../CascadingValuesParametersTabSet.razor | 6 ++-- .../BlazorSample/Pages/ParentComponent.razor | 6 ++-- .../BlazorSample/Pages/RouteParameter.razor | 2 +- aspnetcore/blazor/components.md | 32 +++++++++---------- aspnetcore/blazor/get-started.md | 4 +-- .../samples_snapshot/3.x/Counter2.razor | 2 +- aspnetcore/blazor/index.md | 6 ++-- .../samples_snapshot/3.x/Constraint.razor | 2 +- .../tutorials/build-your-first-blazor-app.md | 4 +-- .../samples_snapshot/3.x/Counter.razor | 2 +- 15 files changed, 46 insertions(+), 46 deletions(-) diff --git a/aspnetcore/blazor/common/samples/3.x/BlazorSample/Components/ChildComponent.razor b/aspnetcore/blazor/common/samples/3.x/BlazorSample/Components/ChildComponent.razor index 2b3e0f1c01..9682f61b65 100644 --- a/aspnetcore/blazor/common/samples/3.x/BlazorSample/Components/ChildComponent.razor +++ b/aspnetcore/blazor/common/samples/3.x/BlazorSample/Components/ChildComponent.razor @@ -9,12 +9,12 @@ @code { [Parameter] - private string Title { get; set; } + public string Title { get; set; } [Parameter] - private RenderFragment ChildContent { get; set; } + public RenderFragment ChildContent { get; set; } [Parameter] - private EventCallback OnClick { get; set; } + public EventCallback OnClick { get; set; } } diff --git a/aspnetcore/blazor/common/samples/3.x/BlazorSample/Components/ListViewTemplate.razor b/aspnetcore/blazor/common/samples/3.x/BlazorSample/Components/ListViewTemplate.razor index 3cd9b6c786..b66e228b88 100644 --- a/aspnetcore/blazor/common/samples/3.x/BlazorSample/Components/ListViewTemplate.razor +++ b/aspnetcore/blazor/common/samples/3.x/BlazorSample/Components/ListViewTemplate.razor @@ -9,8 +9,8 @@ @code { [Parameter] - private RenderFragment ItemTemplate { get; set; } + public RenderFragment ItemTemplate { get; set; } [Parameter] - private IReadOnlyList Items { get; set; } + public IReadOnlyList Items { get; set; } } diff --git a/aspnetcore/blazor/common/samples/3.x/BlazorSample/Components/Tab.razor b/aspnetcore/blazor/common/samples/3.x/BlazorSample/Components/Tab.razor index 0c011f3509..36ad3fa660 100644 --- a/aspnetcore/blazor/common/samples/3.x/BlazorSample/Components/Tab.razor +++ b/aspnetcore/blazor/common/samples/3.x/BlazorSample/Components/Tab.razor @@ -10,13 +10,13 @@ @code { [CascadingParameter] - private TabSet ContainerTabSet { get; set; } + public TabSet ContainerTabSet { get; set; } [Parameter] - private string Title { get; set; } + public string Title { get; set; } [Parameter] - public RenderFragment ChildContent { get; private set; } + public RenderFragment ChildContent { get; set; } private string TitleCssClass => ContainerTabSet.ActiveTab == this ? "active" : null; diff --git a/aspnetcore/blazor/common/samples/3.x/BlazorSample/Components/TabSet.razor b/aspnetcore/blazor/common/samples/3.x/BlazorSample/Components/TabSet.razor index 762374957f..8ae1332d09 100644 --- a/aspnetcore/blazor/common/samples/3.x/BlazorSample/Components/TabSet.razor +++ b/aspnetcore/blazor/common/samples/3.x/BlazorSample/Components/TabSet.razor @@ -14,7 +14,7 @@ @code { [Parameter] - private RenderFragment ChildContent { get; set; } + public RenderFragment ChildContent { get; set; } public ITab ActiveTab { get; private set; } diff --git a/aspnetcore/blazor/common/samples/3.x/BlazorSample/Components/TableTemplate.razor b/aspnetcore/blazor/common/samples/3.x/BlazorSample/Components/TableTemplate.razor index eef2775ea2..822a3b5161 100644 --- a/aspnetcore/blazor/common/samples/3.x/BlazorSample/Components/TableTemplate.razor +++ b/aspnetcore/blazor/common/samples/3.x/BlazorSample/Components/TableTemplate.razor @@ -17,14 +17,14 @@ @code { [Parameter] - private RenderFragment TableHeader { get; set; } + public RenderFragment TableHeader { get; set; } [Parameter] - private RenderFragment RowTemplate { get; set; } + public RenderFragment RowTemplate { get; set; } [Parameter] - private RenderFragment TableFooter { get; set; } + public RenderFragment TableFooter { get; set; } [Parameter] - private IReadOnlyList Items { get; set; } + public IReadOnlyList Items { get; set; } } diff --git a/aspnetcore/blazor/common/samples/3.x/BlazorSample/Pages/CascadingValuesParametersTabSet.razor b/aspnetcore/blazor/common/samples/3.x/BlazorSample/Pages/CascadingValuesParametersTabSet.razor index 887197ac24..78b189eea5 100644 --- a/aspnetcore/blazor/common/samples/3.x/BlazorSample/Pages/CascadingValuesParametersTabSet.razor +++ b/aspnetcore/blazor/common/samples/3.x/BlazorSample/Pages/CascadingValuesParametersTabSet.razor @@ -91,7 +91,7 @@ namespace BlazorSample.UIInterfaces @@code { [Parameter] - private RenderFragment ChildContent { get; set; } + public RenderFragment ChildContent { get; set; } public ITab ActiveTab { get; private set; } @@ -140,10 +140,10 @@ namespace BlazorSample.UIInterfaces private TabSet ContainerTabSet { get; set; } [Parameter] - private string Title { get; set; } + public string Title { get; set; } [Parameter] - public RenderFragment ChildContent { get; private set; } + public RenderFragment ChildContent { get; set; } private string TitleCssClass => ContainerTabSet.ActiveTab == this ? "active" : null; diff --git a/aspnetcore/blazor/common/samples/3.x/BlazorSample/Pages/ParentComponent.razor b/aspnetcore/blazor/common/samples/3.x/BlazorSample/Pages/ParentComponent.razor index 7cd5eca96d..5977d52ecf 100644 --- a/aspnetcore/blazor/common/samples/3.x/BlazorSample/Pages/ParentComponent.razor +++ b/aspnetcore/blazor/common/samples/3.x/BlazorSample/Pages/ParentComponent.razor @@ -61,11 +61,11 @@ @@code { [Parameter] - private string Title { get; set; } + public string Title { get; set; } [Parameter] - private RenderFragment ChildContent { get; set; } + public RenderFragment ChildContent { get; set; } [Parameter] - private EventCallback<UIMouseEventArgs> OnClick { get; set; } + public EventCallback<UIMouseEventArgs> OnClick { get; set; } } diff --git a/aspnetcore/blazor/common/samples/3.x/BlazorSample/Pages/RouteParameter.razor b/aspnetcore/blazor/common/samples/3.x/BlazorSample/Pages/RouteParameter.razor index b3b5d23f9c..4097d61d79 100644 --- a/aspnetcore/blazor/common/samples/3.x/BlazorSample/Pages/RouteParameter.razor +++ b/aspnetcore/blazor/common/samples/3.x/BlazorSample/Pages/RouteParameter.razor @@ -26,7 +26,7 @@ @@code { [Parameter] - private string Text { get; set; } = "fantastic"; + public string Text { get; set; } = "fantastic"; }

Example: diff --git a/aspnetcore/blazor/components.md b/aspnetcore/blazor/components.md index 190fdb4d80..d8a21aca0e 100644 --- a/aspnetcore/blazor/components.md +++ b/aspnetcore/blazor/components.md @@ -85,7 +85,7 @@ The following markup in *Index.razor* renders a `HeadingComponent` instance: ## Component parameters -Components can have *component parameters*, which are defined using properties (usually *non-public*) on the component class with the `[Parameter]` attribute. Use attributes to specify arguments for a component in markup. +Components can have *component parameters*, which are defined using public properties on the component class with the `[Parameter]` attribute. Use attributes to specify arguments for a component in markup. *Components/ChildComponent.razor*: @@ -134,19 +134,19 @@ In the following example, the first `` element (`id="useIndividualParams" @code { [Parameter] - private string Maxlength { get; set; } = "10"; + public string Maxlength { get; set; } = "10"; [Parameter] - private string Placeholder { get; set; } = "Input placeholder text"; + public string Placeholder { get; set; } = "Input placeholder text"; [Parameter] - private string Required { get; set; } = "required"; + public string Required { get; set; } = "required"; [Parameter] - private string Size { get; set; } = "50"; + public string Size { get; set; } = "50"; [Parameter] - private Dictionary InputAttributes { get; set; } = + public Dictionary InputAttributes { get; set; } = new Dictionary() { { "maxlength", "10" }, @@ -179,8 +179,8 @@ To accept arbitrary attributes, define a component parameter using the `[Paramet ```cshtml @code { - [Parameter(CaptureUnmatchedValues = true)] - private Dictionary InputAttributes { get; set; } + [Parameter(CaptureUnmatchedAttributes = true)] + public Dictionary InputAttributes { get; set; } } ``` @@ -225,7 +225,7 @@ Data binding works with format strings using [@bind:forma @code { [Parameter] - private DateTime StartDate { get; set; } = new DateTime(2020, 1, 1); + public DateTime StartDate { get; set; } = new DateTime(2020, 1, 1); } ``` @@ -244,10 +244,10 @@ The following child component (`ChildComponent`) has a `Year` component paramete @code { [Parameter] - private int Year { get; set; } + public int Year { get; set; } [Parameter] - private EventCallback YearChanged { get; set; } + public EventCallback YearChanged { get; set; } } ``` @@ -270,7 +270,7 @@ The following parent component uses `ChildComponent` and binds the `ParentYear` @code { [Parameter] - private int ParentYear { get; set; } = 1978; + public int ParentYear { get; set; } = 1978; private void ChangeTheYear() { @@ -508,7 +508,7 @@ Consider the following example: @code { [Parameter] - private IEnumerable People { get; set; } + public IEnumerable People { get; set; } } ``` @@ -524,7 +524,7 @@ The mapping process can be controlled with the `@key` directive attribute. `@key @code { [Parameter] - private IEnumerable People { get; set; } + public IEnumerable People { get; set; } } ``` @@ -757,7 +757,7 @@ In the following example, `IsCompleted` determines if `checked` is rendered in t @code { [Parameter] - private bool IsCompleted { get; set; } + public bool IsCompleted { get; set; } } ``` @@ -1055,7 +1055,7 @@ Consider the following `PetDetails` component, which can be manually built into @code { [Parameter] - private string PetDetailsQuote { get; set; } + public string PetDetailsQuote { get; set; } } ``` diff --git a/aspnetcore/blazor/get-started.md b/aspnetcore/blazor/get-started.md index b8ea1e4fc3..9bb1a8bfe1 100644 --- a/aspnetcore/blazor/get-started.md +++ b/aspnetcore/blazor/get-started.md @@ -5,7 +5,7 @@ description: Get started with Blazor by building a Blazor app with the tooling o monikerRange: '>= aspnetcore-3.0' ms.author: riande ms.custom: mvc -ms.date: 07/23/2019 +ms.date: 07/26/2019 uid: blazor/get-started --- # Get started with ASP.NET Core Blazor @@ -146,7 +146,7 @@ Run the app. The homepage has its own counter provided by the `Counter` componen Component parameters are specified using attributes or [child content](xref:blazor/components#child-content), which allow you to set properties on the child component. To add a parameter to the `Counter` component, update the component's `@code` block: -* Add a property for `IncrementAmount` with a `[Parameter]` attribute. +* Add a public property for `IncrementAmount` with a `[Parameter]` attribute. * Change the `IncrementCount` method to use the `IncrementAmount` when increasing the value of `currentCount`. *Pages/Counter.razor*: diff --git a/aspnetcore/blazor/get-started/samples_snapshot/3.x/Counter2.razor b/aspnetcore/blazor/get-started/samples_snapshot/3.x/Counter2.razor index 49602a87a9..2f3099ca98 100644 --- a/aspnetcore/blazor/get-started/samples_snapshot/3.x/Counter2.razor +++ b/aspnetcore/blazor/get-started/samples_snapshot/3.x/Counter2.razor @@ -10,7 +10,7 @@ private int currentCount = 0; [Parameter] - private int IncrementAmount { get; set; } = 1; + public int IncrementAmount { get; set; } = 1; private void IncrementCount() { diff --git a/aspnetcore/blazor/index.md b/aspnetcore/blazor/index.md index c37f43bacb..1c324763ce 100644 --- a/aspnetcore/blazor/index.md +++ b/aspnetcore/blazor/index.md @@ -5,7 +5,7 @@ description: Explore ASP.NET Core Blazor, a way to build interactive client-side monikerRange: '>= aspnetcore-3.0' ms.author: riande ms.custom: "mvc, seoapril2019" -ms.date: 07/01/2019 +ms.date: 07/26/2019 uid: blazor/index --- # Introduction to Blazor @@ -55,10 +55,10 @@ The following Razor markup demonstrates a component (*Dialog.razor*), which can @code { [Parameter] - private string Title { get; set; } + public string Title { get; set; } [Parameter] - private RenderFragment ChildContent { get; set; } + public RenderFragment ChildContent { get; set; } private void OnYes() { diff --git a/aspnetcore/blazor/routing/samples_snapshot/3.x/Constraint.razor b/aspnetcore/blazor/routing/samples_snapshot/3.x/Constraint.razor index befd95f350..0d69d2d406 100644 --- a/aspnetcore/blazor/routing/samples_snapshot/3.x/Constraint.razor +++ b/aspnetcore/blazor/routing/samples_snapshot/3.x/Constraint.razor @@ -4,5 +4,5 @@ @code { [Parameter] - private int Id { get; set; } + public int Id { get; set; } } diff --git a/aspnetcore/tutorials/build-your-first-blazor-app.md b/aspnetcore/tutorials/build-your-first-blazor-app.md index 8d37231b12..0e3d73740c 100644 --- a/aspnetcore/tutorials/build-your-first-blazor-app.md +++ b/aspnetcore/tutorials/build-your-first-blazor-app.md @@ -5,7 +5,7 @@ description: Build a Blazor app step-by-step. monikerRange: '>= aspnetcore-3.0' ms.author: riande ms.custom: mvc -ms.date: 07/01/2019 +ms.date: 07/26/2019 uid: tutorials/first-blazor-app --- # Build your first Blazor app @@ -61,7 +61,7 @@ Include a component in another component using an HTML syntax. ## Component parameters -Components can also have parameters. Component parameters are defined using non-public properties on the component class decorated with `[Parameter]`. Use attributes to specify arguments for a component in markup. +Components can also have parameters. Component parameters are defined using public properties on the component class decorated with `[Parameter]`. Use attributes to specify arguments for a component in markup. 1. Update the component's `@code` C# code: diff --git a/aspnetcore/tutorials/build-your-first-blazor-app/samples_snapshot/3.x/Counter.razor b/aspnetcore/tutorials/build-your-first-blazor-app/samples_snapshot/3.x/Counter.razor index 49602a87a9..2f3099ca98 100644 --- a/aspnetcore/tutorials/build-your-first-blazor-app/samples_snapshot/3.x/Counter.razor +++ b/aspnetcore/tutorials/build-your-first-blazor-app/samples_snapshot/3.x/Counter.razor @@ -10,7 +10,7 @@ private int currentCount = 0; [Parameter] - private int IncrementAmount { get; set; } = 1; + public int IncrementAmount { get; set; } = 1; private void IncrementCount() { From 23dd8f7b35da392f807594205a0691498652146d Mon Sep 17 00:00:00 2001 From: Isaac Levin <8878502+isaac2004@users.noreply.github.com> Date: Fri, 2 Aug 2019 10:58:11 -0400 Subject: [PATCH 3/7] Update Blazor Templates #13200 (#13536) --- aspnetcore/blazor/get-started.md | 12 ++++++------ aspnetcore/blazor/hosting-models.md | 7 +++---- aspnetcore/host-and-deploy/blazor/client-side.md | 4 ++-- aspnetcore/security/blazor/index.md | 4 ++-- 4 files changed, 13 insertions(+), 14 deletions(-) diff --git a/aspnetcore/blazor/get-started.md b/aspnetcore/blazor/get-started.md index 9bb1a8bfe1..c1b1144d3d 100644 --- a/aspnetcore/blazor/get-started.md +++ b/aspnetcore/blazor/get-started.md @@ -34,7 +34,7 @@ Get started with Blazor: 4\. Provide a project name in the **Project name** field or accept the default project name. Confirm the **Location** entry is correct or provide a location for the project. Select **Create**. - 5\. For a Blazor client-side experience, choose the **Blazor (client-side)** template. For a Blazor server-side experience, choose the **Blazor Server App** template. Select **Create**. For information on the two Blazor hosting models, server-side and client-side, see . + 5\. For a Blazor client-side experience, choose the **Blazor WebAssembly App** template. For a Blazor server-side experience, choose the **Blazor Server App** template. Select **Create**. For information on the two Blazor hosting models, server-side and client-side, see . 6\. Press **F5** to run the app. @@ -50,13 +50,13 @@ Get started with Blazor: 3\. For a Blazor client-side experience, execute the following command in a command shell: ```console - dotnet new blazor -o WebApplication1 + dotnet new blazorwasm -o WebApplication1 ``` For a Blazor server-side experience, execute the following command in a command shell: ```console - dotnet new blazorserverside -o WebApplication1 + dotnet new blazorserver -o WebApplication1 ``` For information on the two Blazor hosting models, server-side and client-side, see . @@ -79,7 +79,7 @@ Get started with Blazor: 3\. In the sidebar, select **.NET Core** > **App**. - 4\. For a Blazor server-side experience, select the **ASP.NET Core Blazor Server App** template. For a Blazor client-side experience, select the **ASP.NET Core Blazor WebAssembly App** template. Select **Next**. For information on the two Blazor hosting models, server-side and client-side, see . + 4\. For a Blazor server-side experience, select the **Blazor Server App** template. For a Blazor client-side experience, select the **Blazor WebAssembly App** template. Select **Next**. For information on the two Blazor hosting models, server-side and client-side, see . 5\. The **Target Framework** defaults to **.NET Core 3.0**. Select **Next**. @@ -94,7 +94,7 @@ Get started with Blazor: For a Blazor client-side experience, execute the following commands in a command shell: ```console - dotnet new blazor -o WebApplication1 + dotnet new blazorwasm -o WebApplication1 cd WebApplication1 dotnet run ``` @@ -102,7 +102,7 @@ Get started with Blazor: For a Blazor server-side experience, execute the following commands in a command shell: ```console - dotnet new blazorserverside -o WebApplication1 + dotnet new blazorserver -o WebApplication1 cd WebApplication1 dotnet run ``` diff --git a/aspnetcore/blazor/hosting-models.md b/aspnetcore/blazor/hosting-models.md index 32b377511e..c4cfd181ed 100644 --- a/aspnetcore/blazor/hosting-models.md +++ b/aspnetcore/blazor/hosting-models.md @@ -22,10 +22,9 @@ The principal hosting model for Blazor is running client-side in the browser on ![Blazor client-side: The Blazor app runs on a UI thread inside the browser.](hosting-models/_static/client-side.png) -To create a Blazor app using the client-side hosting model, use either of the following templates: +To create a Blazor app using the client-side hosting model, use the **Blazor WebAssembly App** template ([dotnet new blazorwasm](/dotnet/core/tools/dotnet-new)). -* **Blazor (client-side)** ([dotnet new blazor](/dotnet/core/tools/dotnet-new)) – Deployed as a set of static files. -* **Blazor (ASP.NET Core Hosted)** ([dotnet new blazorhosted](/dotnet/core/tools/dotnet-new)) – Hosted by an ASP.NET Core server. The ASP.NET Core app serves the Blazor app to clients. The Blazor client-side app can interact with the server over the network using web API calls or [SignalR](xref:signalr/introduction). +After selecting the **Blazor WebAssembly App** template, you have the option of configuring the app to use an ASP.NET Core backend by selecting the **ASP.NET Core hosted** check box ([dotnet new blazorwasm --hosted](/dotnet/core/tools/dotnet-new)). The ASP.NET Core app serves the Blazor app to clients. The Blazor client-side app can interact with the server over the network using web API calls or [SignalR](xref:signalr/introduction). The templates include the *blazor.webassembly.js* script that handles: @@ -52,7 +51,7 @@ With the server-side hosting model, the app is executed on the server from withi ![The browser interacts with the app (hosted inside of an ASP.NET Core app) on the server over a SignalR connection.](hosting-models/_static/server-side.png) -To create a Blazor app using the server-side hosting model, use the ASP.NET Core **Blazor Server App** template ([dotnet new blazorserverside](/dotnet/core/tools/dotnet-new)). The ASP.NET Core app hosts the server-side app and creates the SignalR endpoint where clients connect. +To create a Blazor app using the server-side hosting model, use the ASP.NET Core **Blazor Server App** template ([dotnet new blazorserver](/dotnet/core/tools/dotnet-new)). The ASP.NET Core app hosts the server-side app and creates the SignalR endpoint where clients connect. The ASP.NET Core app references the app's `Startup` class to add: diff --git a/aspnetcore/host-and-deploy/blazor/client-side.md b/aspnetcore/host-and-deploy/blazor/client-side.md index 013c41d005..e8275e81c9 100644 --- a/aspnetcore/host-and-deploy/blazor/client-side.md +++ b/aspnetcore/host-and-deploy/blazor/client-side.md @@ -142,7 +142,7 @@ The app responds locally at `http://localhost:port/CoolApp`. For more information, see the section on the [path base host configuration value](#path-base). -If an app uses the [client-side hosting model](xref:blazor/hosting-models#client-side) (based on the **Blazor (client-side)** project template, the `blazor` template when using the [dotnet new](/dotnet/core/tools/dotnet-new) command) and is hosted as an IIS sub-app in an ASP.NET Core app, it's important to disable the inherited ASP.NET Core Module handler or make sure the root (parent) app's `` section in the *web.config* file isn't inherited by the sub-app. +If an app uses the [client-side hosting model](xref:blazor/hosting-models#client-side) (based on the **Blazor WebAssembly App** project template, the `blazorwasm` template when using the [dotnet new](/dotnet/core/tools/dotnet-new) command) and is hosted as an IIS sub-app in an ASP.NET Core app, it's important to disable the inherited ASP.NET Core Module handler or make sure the root (parent) app's `` section in the *web.config* file isn't inherited by the sub-app. Remove the handler in the app's published *web.config* file by adding a `` section to the file: @@ -174,7 +174,7 @@ Removing the handler or disabling inheritance is performed in addition to config A *hosted deployment* serves the Blazor client-side app to browsers from an [ASP.NET Core app](xref:index) that runs on a web server. -The Blazor app is included with the ASP.NET Core app in the published output so that the two apps are deployed together. A web server that is capable of hosting an ASP.NET Core app is required. For a hosted deployment, Visual Studio includes the **Blazor (ASP.NET Core hosted)** project template (`blazorhosted` template when using the [dotnet new](/dotnet/core/tools/dotnet-new) command). +The Blazor app is included with the ASP.NET Core app in the published output so that the two apps are deployed together. A web server that is capable of hosting an ASP.NET Core app is required. For a hosted deployment, Visual Studio includes the **Blazor WebAssembly App** project template (`blazorwasm` template when using the [dotnet new](/dotnet/core/tools/dotnet-new) command) with the **Hosted** option selected. For more information on ASP.NET Core app hosting and deployment, see . diff --git a/aspnetcore/security/blazor/index.md b/aspnetcore/security/blazor/index.md index 2542fb96bd..475c947c04 100644 --- a/aspnetcore/security/blazor/index.md +++ b/aspnetcore/security/blazor/index.md @@ -51,7 +51,7 @@ A dialog opens to offer the same set of authentication mechanisms available for Follow the Visual Studio Code guidance in the article to create a new Blazor server-side project with an authentication mechanism: ```console -dotnet new blazorserverside -o {APP NAME} -au {AUTHENTICATION} +dotnet new blazorserver -o {APP NAME} -au {AUTHENTICATION} ``` Permissible authentication values (`{AUTHENTICATION}`) are shown in the following table. @@ -85,7 +85,7 @@ The command creates a folder named with the value provided for the `{APP NAME}` Follow the .NET Core CLI guidance in the article to create a new Blazor server-side project with an authentication mechanism: ```console -dotnet new blazorserverside -o {APP NAME} -au {AUTHENTICATION} +dotnet new blazorserver -o {APP NAME} -au {AUTHENTICATION} ``` Permissible authentication values (`{AUTHENTICATION}`) are shown in the following table. From 1427a656746added6dcc25360be2b47d642aad76 Mon Sep 17 00:00:00 2001 From: Luke Latham <1622880+guardrex@users.noreply.github.com> Date: Fri, 2 Aug 2019 10:32:52 -0500 Subject: [PATCH 4/7] Component binding is now case sensitive (#13558) --- aspnetcore/blazor/components.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/aspnetcore/blazor/components.md b/aspnetcore/blazor/components.md index d8a21aca0e..6f524bb13d 100644 --- a/aspnetcore/blazor/components.md +++ b/aspnetcore/blazor/components.md @@ -20,6 +20,8 @@ Blazor apps are built using *components*. A component is a self-contained chunk Components are implemented in [Razor](xref:mvc/views/razor) component files (*.razor*) using a combination of C# and HTML markup. A component in Blazor is formally referred to as a *Razor component*. +A component's name must start with an uppercase character. For example, *MyCoolComponent.razor* is valid, and *myCoolComponent.razor* is invalid. + Components can be authored using the *.cshtml* file extension as long as the files are identified as Razor component files using the `_RazorComponentInclude` MSBuild property. For example, an app that specifies that all *.cshtml* files under the *Pages* folder should be treated as Razor components files: ```xml @@ -71,10 +73,12 @@ While pages and views can use components, the converse isn't true. Components ca For more information on how components are rendered and component state is managed in Blazor server-side apps, see the article. -## Using components +## Use components Components can include other components by declaring them using HTML element syntax. The markup for using a component looks like an HTML tag where the name of the tag is the component type. +Attribute binding is case sensitive. For example, `@bind` is valid, and `@Bind` is invalid. + The following markup in *Index.razor* renders a `HeadingComponent` instance: [!code-cshtml[](common/samples/3.x/BlazorSample/Pages/Index.razor?name=snippet_HeadingComponent)] @@ -83,6 +87,8 @@ The following markup in *Index.razor* renders a `HeadingComponent` instance: [!code-cshtml[](common/samples/3.x/BlazorSample/Components/HeadingComponent.razor)] +If a component contains an HTML element with an uppercase first letter that doesn't match a component name, a warning is emitted indicating that the element has an unexpected name. Adding an `@using` statement for the component's namespace makes the component available, which removes the warning. + ## Component parameters Components can have *component parameters*, which are defined using public properties on the component class with the `[Parameter]` attribute. Use attributes to specify arguments for a component in markup. From d59654a336960927936ddd8c1c1b6aae5c2ef9e4 Mon Sep 17 00:00:00 2001 From: Luke Latham <1622880+guardrex@users.noreply.github.com> Date: Tue, 6 Aug 2019 17:04:29 -0500 Subject: [PATCH 5/7] Blazor globalization (#13696) --- aspnetcore/blazor/components.md | 36 ++++++++++++++++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/aspnetcore/blazor/components.md b/aspnetcore/blazor/components.md index 6f524bb13d..8fb5d5a616 100644 --- a/aspnetcore/blazor/components.md +++ b/aspnetcore/blazor/components.md @@ -5,7 +5,7 @@ description: Learn how to create and use Razor components, including how to bind monikerRange: '>= aspnetcore-3.0' ms.author: riande ms.custom: mvc -ms.date: 08/02/2019 +ms.date: 08/07/2019 uid: blazor/components --- # Create and use ASP.NET Core Razor components @@ -222,6 +222,31 @@ In addition to handling `onchange` events with `@bind` syntax, a property or fie Unlike `onchange`, which fires when the element loses focus, `oninput` fires when the value of the text box changes. +**Globalization** + +`@bind` values are formatted for display and parsed using the current culture's rules. + +The current culture can be accessed from the property. + +[CultureInfo.InvariantCulture](xref:System.Globalization.CultureInfo.InvariantCulture) is used for the following field types (``): + +* `date` +* `number` + +The preceding field types: + +* Are displayed using their appropriate browser-based formatting rules. +* Can't contain free-form text. +* Provide user interaction characteristics based on the browser's implementation. + +The following field types have specific formatting requirements and aren't currently supported by Blazor because they aren't supported by all major browsers: + +* `datetime-local` +* `month` +* `week` + +`@bind` supports the `@bind:culture` parameter to provide a for parsing and formatting a value. Specifying a culture isn't recommended when using the `date` and `number` field types. `date` and `number` have built-in Blazor support that provides the required culture. + **Format strings** Data binding works with format strings using [@bind:format](xref:mvc/views/razor#bind). Other format expressions, such as currency or number formats, aren't available at this time. @@ -235,8 +260,17 @@ Data binding works with format strings using [@bind:forma } ``` +In the preceding code, the `` element's field type (`type`) defaults to `text`. `@bind:format` is supported for binding the following .NET types: + +* +* ? +* +* ? + The `@bind:format` attribute specifies the date format to apply to the `value` of the `` element. The format is also used to parse the value when an `onchange` event occurs. +Specifying a format for the `date` field type isn't recommended because Blazor has built-in support to format dates. + **Component parameters** Binding recognizes component parameters, where `@bind-{property}` can bind a property value across components. From da1879459a7f2216bb7848a7f897d3aaad8dd2ce Mon Sep 17 00:00:00 2001 From: Luke Latham <1622880+guardrex@users.noreply.github.com> Date: Thu, 8 Aug 2019 12:20:05 -0500 Subject: [PATCH 6/7] Blazor localization (#13724) --- aspnetcore/blazor/components.md | 122 ++++++++++++++++++++++++++++++++ 1 file changed, 122 insertions(+) diff --git a/aspnetcore/blazor/components.md b/aspnetcore/blazor/components.md index 8fb5d5a616..161a2b3244 100644 --- a/aspnetcore/blazor/components.md +++ b/aspnetcore/blazor/components.md @@ -247,6 +247,8 @@ The following field types have specific formatting requirements and aren't curre `@bind` supports the `@bind:culture` parameter to provide a for parsing and formatting a value. Specifying a culture isn't recommended when using the `date` and `number` field types. `date` and `number` have built-in Blazor support that provides the required culture. +For information on how to set the user's culture, see the [Localization](#localization) section. + **Format strings** Data binding works with format strings using [@bind:format](xref:mvc/views/razor#bind). Other format expressions, such as currency or number formats, aren't available at this time. @@ -1223,3 +1225,123 @@ This is a trivial example. In more realistic cases with complex and deeply neste * Don't write long blocks of manually-implemented `RenderTreeBuilder` logic. Prefer `.razor` files and allow the compiler to deal with the sequence numbers. * If sequence numbers are hardcoded, the diff algorithm only requires that sequence numbers increase in value. The initial value and gaps are irrelevant. One legitimate option is to use the code line number as the sequence number, or start from zero and increase by ones or hundreds (or any preferred interval). * Blazor uses sequence numbers, while other tree-diffing UI frameworks don't use them. Diffing is far faster when sequence numbers are used, and Blazor has the advantage of a compile step that deals with sequence numbers automatically for developers authoring `.razor` files. + +## Localization + +Blazor server-side apps are localized using [Localization Middleware](xref:fundamentals/localization#localization-middleware). The middleware selects the appropriate culture for users requesting resources from the app. + +The culture can be set using one of the following approaches: + +* [Cookies](#cookies) +* [Provide UI to choose the culture](#provide-ui-to-choose-the-culture) + +For more information and examples, see . + +### Cookies + +A localization culture cookie can persist the user's culture. The cookie is created by the `OnGet` method of the app's host page (*Pages/Host.cshtml.cs*). The Localization Middleware reads the cookie on subsequent requests to set the user's culture. + +Use of a cookie ensures that the WebSocket connection can correctly propagate the culture. If localization schemes are based on the URL path or query string, the scheme might not be able to work with WebSockets, thus fail to persist the culture. Therefore, use of a localization culture cookie is the recommended approach. + +Any technique can be used to assign a culture if the culture is persisted in a localization cookie. If the app already has an established localization scheme for server-side ASP.NET Core, continue to use the app's existing localization infrastructure and set the localization culture cookie within the app's scheme. + +The following example shows how to set the current culture in a cookie that can be read by the Localization Middleware. Create a *Pages/Host.cshtml.cs* file with the following contents in the Blazor server-side app: + +```csharp +public class HostModel : PageModel +{ + public void OnGet() + { + HttpContext.Response.Cookies.Append( + CookieRequestCultureProvider.DefaultCookieName, + CookieRequestCultureProvider.MakeCookieValue( + new RequestCulture( + CultureInfo.CurrentCulture, + CultureInfo.CurrentUICulture))); + } +} +``` + +Localization is handled in the app: + +1. The browser sends an initial HTTP request to the app. +1. The culture is assigned by the Localization Middleware. +1. The `OnGet` method in *_Host.cshtml.cs* persists the culture in a cookie as part of the response. +1. The browser opens a WebSocket connection to create an interactive Blazor server-side session. +1. The Localization Middleware reads the cookie and assigns the culture. +1. The Blazor server-side session begins with the correct culture. + +## Provide UI to choose the culture + +To provide UI to allow a user to select a culture, a *redirect-based approach* is recommended. The process is similar to what happens in a web app when a user attempts to access a secure resource—the user is redirected to a sign-in page and then redirected back to the original resource. + +The app persists the user's selected culture via a redirect to a controller. The controller sets the user's selected culture into a cookie and redirects the user back to the original URI. + +Establish an HTTP endpoint on the server to set the user's selected culture in a cookie and perform the redirect back to the original URI: + +```csharp +[Route("[controller]/[action]")] +public class CultureController : Controller +{ + public IActionResult SetCulture(string culture, string redirectUri) + { + if (culture != null) + { + HttpContext.Response.Cookies.Append( + CookieRequestCultureProvider.DefaultCookieName, + CookieRequestCultureProvider.MakeCookieValue( + new RequestCulture(culture))); + } + + return LocalRedirect(redirectUri); + } +} +``` + +> [!WARNING] +> Use the `LocalRedirect` action result to prevent open redirect attacks. For more information, see . + +The following component shows an example of how to perform the initial redirection when the user selects a culture: + +```cshtml +@inject IUriHelper UriHelper + +

Select your language

+ + + +@code { + private double textNumber; + + private void OnSelected(UIChangeEventArgs e) + { + var culture = (string)e.Value; + var uri = new Uri(UriHelper.GetAbsoluteUri()) + .GetComponents(UriComponents.PathAndQuery, UriFormat.Unescaped); + var query = $"?culture={Uri.EscapeDataString(culture)}&" + + $"redirectUri={Uri.EscapeDataString(uri)}"; + + UriHelper.NavigateTo("/Culture/SetCulture" + query, forceLoad: true); + } +} +``` + +### Use .NET localization scenarios in Blazor apps + +Inside Blazor apps, the following .NET localization and globalization scenarios are available: + +* .NET's resources system +* Culture-specific number and date formatting + +Blazor's `@bind` functionality performs globalization based on the user's current culture. For more information, see the [Data binding](#data-binding) section. + +A limited set of ASP.NET Core's localization scenarios are currently supported: + +* `IStringLocalizer<>` *is supported* in Blazor apps. +* `IHtmlLocalizer<>`, `IViewLocalizer<>`, and Data Annotations localization are ASP.NET Core MVC scenarios and **not supported** in Blazor apps. + +For more information, see . From 2c09ff6998bedbe2b0f58e021c3f742962b7f66b Mon Sep 17 00:00:00 2001 From: Luke Latham <1622880+guardrex@users.noreply.github.com> Date: Tue, 13 Aug 2019 11:24:03 -0500 Subject: [PATCH 7/7] Pre8 preliminary updates (#13799) --- aspnetcore/blazor/components.md | 2 +- aspnetcore/blazor/get-started.md | 6 +++--- aspnetcore/blazor/index.md | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/aspnetcore/blazor/components.md b/aspnetcore/blazor/components.md index 161a2b3244..7b819922d9 100644 --- a/aspnetcore/blazor/components.md +++ b/aspnetcore/blazor/components.md @@ -5,7 +5,7 @@ description: Learn how to create and use Razor components, including how to bind monikerRange: '>= aspnetcore-3.0' ms.author: riande ms.custom: mvc -ms.date: 08/07/2019 +ms.date: 08/02/2019 uid: blazor/components --- # Create and use ASP.NET Core Razor components diff --git a/aspnetcore/blazor/get-started.md b/aspnetcore/blazor/get-started.md index c1b1144d3d..d42138e6d0 100644 --- a/aspnetcore/blazor/get-started.md +++ b/aspnetcore/blazor/get-started.md @@ -5,7 +5,7 @@ description: Get started with Blazor by building a Blazor app with the tooling o monikerRange: '>= aspnetcore-3.0' ms.author: riande ms.custom: mvc -ms.date: 07/26/2019 +ms.date: 07/23/2019 uid: blazor/get-started --- # Get started with ASP.NET Core Blazor @@ -19,7 +19,7 @@ Get started with Blazor: 1. Install the Blazor templates by running the following command in a command shell: ```console - dotnet new -i Microsoft.AspNetCore.Blazor.Templates::3.0.0-preview7.19365.7 + dotnet new -i Microsoft.AspNetCore.Blazor.Templates::3.0.0-preview8.19405.7 ``` 1. Follow the guidance for your choice of tooling: @@ -39,7 +39,7 @@ Get started with Blazor: 6\. Press **F5** to run the app. > [!NOTE] - > If you installed the Blazor Visual Studio extension for a prior preview release of ASP.NET Core Blazor (Preview 6 or earlier), you can uninstall the extension at Preview 7. Installing the Blazor templates in a command shell is now sufficient to surface the templates in Visual Studio. + > If you installed the Blazor Visual Studio extension for a prior preview release of ASP.NET Core Blazor (Preview 6 or earlier), you can uninstall the extension. Installing the Blazor templates in a command shell is now sufficient to surface the templates in Visual Studio. # [Visual Studio Code](#tab/visual-studio-code) diff --git a/aspnetcore/blazor/index.md b/aspnetcore/blazor/index.md index 1c324763ce..22d6e81d36 100644 --- a/aspnetcore/blazor/index.md +++ b/aspnetcore/blazor/index.md @@ -5,7 +5,7 @@ description: Explore ASP.NET Core Blazor, a way to build interactive client-side monikerRange: '>= aspnetcore-3.0' ms.author: riande ms.custom: "mvc, seoapril2019" -ms.date: 07/26/2019 +ms.date: 07/01/2019 uid: blazor/index --- # Introduction to Blazor