From fde6911b51c536fed99557140fc9a9819a08cda0 Mon Sep 17 00:00:00 2001 From: guardrex <1622880+guardrex@users.noreply.github.com> Date: Tue, 18 Aug 2020 12:26:34 -0500 Subject: [PATCH 1/4] Blazor component params and binding improvements --- aspnetcore/blazor/components/data-binding.md | 106 ++++++++----------- aspnetcore/blazor/components/index.md | 56 ++++------ 2 files changed, 64 insertions(+), 98 deletions(-) diff --git a/aspnetcore/blazor/components/data-binding.md b/aspnetcore/blazor/components/data-binding.md index 0e28225a7ab5..75ab80cd175f 100644 --- a/aspnetcore/blazor/components/data-binding.md +++ b/aspnetcore/blazor/components/data-binding.md @@ -5,7 +5,7 @@ description: Learn about data binding features for components and DOM elements i monikerRange: '>= aspnetcore-3.1' ms.author: riande ms.custom: mvc -ms.date: 03/26/2020 +ms.date: 08/18/2020 no-loc: [cookie, Cookie, Blazor, "Blazor Server", "Blazor WebAssembly", "Identity", "Let's Encrypt", Razor, SignalR] uid: blazor/components/data-binding --- @@ -87,13 +87,13 @@ Consider the following scenario: * An `` element is bound to an `int` type with an initial value of `123`: ```razor - + @code { - [Parameter] - public int MyProperty { get; set; } = 123; + private int inputValue = 123; } ``` + * The user updates the value of the element to `123.45` in the page and changes the element focus. In the preceding scenario, the element's value is reverted to `123`. When the value `123.45` is rejected in favor of the original value of `123`, the user understands that their value wasn't accepted. @@ -111,11 +111,10 @@ By default, binding applies to the element's `onchange` event (`@bind="{PROPERTY Data binding works with format strings using `@bind:format`. Other format expressions, such as currency or number formats, aren't available at this time. ```razor - + @code { - [Parameter] - public DateTime StartDate { get; set; } = new DateTime(2020, 1, 1); + private DateTime startDate = new DateTime(2020, 1, 1); } ``` @@ -131,103 +130,86 @@ The `@bind:format` attribute specifies the date format to apply to the `value` o Specifying a format for the `date` field type isn't recommended because Blazor has built-in support to format dates. In spite of the recommendation, only use the `yyyy-MM-dd` date format for binding to work correctly if a format is supplied with the `date` field type: ```razor - + ``` ## Parent-to-child binding with component parameters -Binding recognizes component parameters, where `@bind-{PROPERTY}` can bind a property value from a parent component down to a child component. Binding from a child to a parent is covered in the [Child-to-parent binding with chained bind](#child-to-parent-binding-with-chained-bind) section. +Component parameters permit binding properties and fields of a parent component with `@bind-{PROPERTY OR FIELD}` syntax. -The following child component (`ChildComponent`) has a `Year` component parameter and `YearChanged` callback: +The following `Child` component (`Child.razor`) has a `Year` component parameter and `YearChanged` callback: ```razor -

Child Component

- -

Year: @Year

+
+
+

Child

+

Child Year: @Year

+

+ +

+
+
@code { + private Random r = new Random(); + [Parameter] public int Year { get; set; } [Parameter] public EventCallback YearChanged { get; set; } + + private Task UpdateYear() + { + Year = r.Next(10050, 12021); + + return YearChanged.InvokeAsync(Year); + } } ``` The must be named as the component parameter name followed by the `Changed` suffix (`{PARAMETER NAME}Changed`), `YearChanged` in the preceding example. For more information on , see . -The following parent component uses: - -* `ChildComponent` and binds the `ParentYear` parameter from the parent to the `Year` parameter on the child component. -* The `onclick` event is used to trigger the `ChangeTheYear` method. For more information, see . +In the following `Parent` component (`Parent.razor`), the `year` field is bound to the `Year` parameter of the child component: ```razor -@page "/ParentComponent" +@page "/Parent" -

Parent Component

+

Parent

-

ParentYear: @ParentYear

+

Parent year: @year

- + - + @code { - [Parameter] - public int ParentYear { get; set; } = 1978; + private Random r = new Random(); + private int year = 1978; - private void ChangeTheYear() + private void UpdateYear() { - ParentYear = 1986; + year = r.Next(1950, 2021); } } ``` -Loading the `ParentComponent` produces the following markup: - -```html -

Parent Component

- -

ParentYear: 1978

- -

Child Component

- -

Year: 1978

-``` - -If the value of the `ParentYear` property is changed by selecting the button in the `ParentComponent`, the `Year` property of the `ChildComponent` is updated. The new value of `Year` is rendered in the UI when the `ParentComponent` is rerendered: - -```html -

Parent Component

- -

ParentYear: 1986

- -

Child Component

- -

Year: 1986

-``` - The `Year` parameter is bindable because it has a companion `YearChanged` event that matches the type of the `Year` parameter. -By convention, `` is essentially equivalent to writing: - -```razor - -``` - -In general, a property can be bound to a corresponding event handler by including an `@bind-{PROPRETY}:event` attribute. For example, the property `MyProp` can be bound to `MyEventHandler` using the following two attributes: +By convention, a property can be bound to a corresponding event handler by including an `@bind-{PROPERTY}:event` attribute assigned to the handler. `` is equivalent to writing: ```razor - + ``` ## Child-to-parent binding with chained bind A common scenario is chaining a data-bound parameter to a page element in the component's output. This scenario is called a *chained bind* because multiple levels of binding occur simultaneously. -A chained bind can't be implemented with [`@bind`](xref:mvc/views/razor#bind) syntax in the page's element. The event handler and value must be specified separately. A parent component, however, can use [`@bind`](xref:mvc/views/razor#bind) syntax with the component's parameter. +A chained bind can't be implemented with [`@bind`](xref:mvc/views/razor#bind) syntax in the child component. The event handler and value must be specified separately. A parent component, however, can use [`@bind`](xref:mvc/views/razor#bind) syntax with the child component's parameter. The following `PasswordField` component (`PasswordField.razor`): @@ -238,7 +220,7 @@ The following `PasswordField` component (`PasswordField.razor`): ```razor

Child Component

-Password: +Password: = aspnetcore-3.1' ms.author: riande ms.custom: mvc -ms.date: 07/14/2020 +ms.date: 08/18/2020 no-loc: [cookie, Cookie, Blazor, "Blazor Server", "Blazor WebAssembly", "Identity", "Let's Encrypt", Razor, SignalR] uid: blazor/components/index --- @@ -250,7 +250,7 @@ In the following example from the sample app, the `ParentComponent` sets the val [!code-razor[](index/samples_snapshot/ParentComponent.razor?highlight=5-6)] > [!WARNING] -> Don't create components that write to their own *component parameters*, use a private field instead. For more information, see the [Don't create components that write to their own parameter properties](#dont-create-components-that-write-to-their-own-parameter-properties) section. +> Don't create components that write to their own *component parameters* when the component's content is rendered with a , use a private field instead. For more information, see the [Overwritten parameters with `RenderFragment`](#overwritten-parameters-with-renderfragment) section. ## Child content @@ -302,28 +302,20 @@ In the following example, the first `` element (`id="useIndividualParams" ```razor + maxlength="@maxlength" + placeholder="@placeholder" + required="@required" + size="@size" /> @code { - [Parameter] - public string Maxlength { get; set; } = "10"; - - [Parameter] - public string Placeholder { get; set; } = "Input placeholder text"; - - [Parameter] - public string Required { get; set; } = "required"; - - [Parameter] - public string Size { get; set; } = "50"; + public string maxlength = "10"; + public string placeholder = "Input placeholder text"; + public string required = "required"; + public string size = "50"; - [Parameter] public Dictionary InputAttributes { get; set; } = new Dictionary() { @@ -335,7 +327,7 @@ In the following example, the first `` element (`id="useIndividualParams" } ``` -The type of the parameter must implement `IEnumerable>` with string keys. Using `IReadOnlyDictionary` is also an option in this scenario. +The type of the parameter must implement `IEnumerable>` or `IReadOnlyDictionary` with string keys. The rendered `` elements using both approaches is identical: @@ -418,10 +410,10 @@ Component references provide a way to reference a component instance so that you * Define a field with the same type as the child component. ```razor - + @code { - private MyLoginDialog loginDialog; + private CustomLoginDialog loginDialog; private void OnSomething() { @@ -617,7 +609,7 @@ Generally, it makes sense to supply one of the following kinds of value for [`@k Ensure that values used for [`@key`][5] don't clash. If clashing values are detected within the same parent element, Blazor throws an exception because it can't deterministically map old elements or components to new elements or components. Only use distinct values, such as object instances or primary key values. -## Don't create components that write to their own parameter properties +## Overwritten parameters with `RenderFragment` Parameters are overwritten under the following conditions: @@ -632,17 +624,13 @@ Consider the following `Expander` component that: * Toggles showing child content with a component parameter. ```razor -
+
-
-

Toggle (Expanded = @Expanded)

-
+

Toggle (Expanded = @Expanded)

@if (Expanded) { -
- @ChildContent -
+

@ChildContent

}
@@ -688,17 +676,13 @@ The following revised `Expander` component: * Uses the private field to maintain its internal toggle state. ```razor -
+
-
-

Toggle (expanded = @expanded)

-
+

Toggle (expanded = @expanded)

@if (expanded) { -
- @ChildContent -
+

@ChildContent

}
From a0f5cfa0176b240fe5dbd0edb24377c408033362 Mon Sep 17 00:00:00 2001 From: guardrex <1622880+guardrex@users.noreply.github.com> Date: Wed, 19 Aug 2020 11:19:37 -0500 Subject: [PATCH 2/4] Updates --- aspnetcore/blazor/components/data-binding.md | 44 +++++++++++++------- aspnetcore/blazor/components/index.md | 6 +-- 2 files changed, 31 insertions(+), 19 deletions(-) diff --git a/aspnetcore/blazor/components/data-binding.md b/aspnetcore/blazor/components/data-binding.md index 75ab80cd175f..349b368f9c33 100644 --- a/aspnetcore/blazor/components/data-binding.md +++ b/aspnetcore/blazor/components/data-binding.md @@ -15,19 +15,27 @@ By [Luke Latham](https://github.com/guardrex) and [Daniel Roth](https://github.c Razor components provide data binding features via an HTML element attribute named [`@bind`](xref:mvc/views/razor#bind) with a field, property, or Razor expression value. -The following example binds the `CurrentValue` property to the text box's value: +The following example binds an `` element to the `currentValue` field and an `` element to the `CurrentValue` property: ```razor - +

+ Current value: @currentValue +

+ +

+ Current value: @CurrentValue +

@code { + private string currentValue; + private string CurrentValue { get; set; } } ``` -When the text box loses focus, the property's value is updated. +When one of the elements loses focus, it's bound field or property is updated. -The text box is updated in the UI only when the component is rendered, not in response to changing the property's value. Since components render themselves after event handler code executes, property updates are *usually* reflected in the UI immediately after an event handler is triggered. +The text box is updated in the UI only when the component is rendered, not in response to changing the field's or property's value. Since components render themselves after event handler code executes, field and property updates are *usually* reflected in the UI immediately after an event handler is triggered. Using [`@bind`](xref:mvc/views/razor#bind) with the `CurrentValue` property (``) is essentially equivalent to the following: @@ -35,13 +43,13 @@ Using [`@bind`](xref:mvc/views/razor#bind) with the `CurrentValue` property (` - + @code { private string CurrentValue { get; set; } } ``` -When the component is rendered, the `value` of the input element comes from the `CurrentValue` property. When the user types in the text box and changes element focus, the `onchange` event is fired and the `CurrentValue` property is set to the changed value. In reality, the code generation is more complex because [`@bind`](xref:mvc/views/razor#bind) handles cases where type conversions are performed. In principle, [`@bind`](xref:mvc/views/razor#bind) associates the current value of an expression with a `value` attribute and handles changes using the registered handler. +When the component is rendered, the `value` of the input element comes from the `CurrentValue` property. When the user types in the text box and changes element focus, the `onchange` event is fired and the `CurrentValue` property is set to the changed value. In reality, the code generation is more complex than that because [`@bind`](xref:mvc/views/razor#bind) handles cases where type conversions are performed. In principle, [`@bind`](xref:mvc/views/razor#bind) associates the current value of an expression with a `value` attribute and handles changes using the registered handler. Bind a property or field on other events by also including an `@bind:event` attribute with an `event` parameter. The following example binds the `CurrentValue` property on the `oninput` event: @@ -55,11 +63,15 @@ Bind a property or field on other events by also including an `@bind:event` attr Unlike `onchange`, which fires when the element loses focus, `oninput` fires when the value of the text box changes. -Use `@bind-{ATTRIBUTE}` with `@bind-{ATTRIBUTE}:event` syntax to bind element attributes other than `value`. In the following example, the paragraph's style is updated when the `paragraphStyle` value changes: +Use `@bind-{ATTRIBUTE}` with `@bind-{ATTRIBUTE}:event` syntax to bind element attributes other than `value`. In the following example: -```razor -@page "/binding-example" +* The paragraph's style is **red** when the component loads (`style="color:red"`). +* The user changes the value of the text box to reflect a different CSS color style and changes the focus. For example, the user changes the text box value to `color:blue` and presses the Tab key on the keyboard. +* When the focus changes: + * The value of `paragraphStyle` is assigned from the `` element's value. + * The paragraph style is updated to reflect the new style in `paragraphStyle`. The text color changes to **blue**. +```razor

@@ -101,8 +113,8 @@ In the preceding scenario, the element's value is reverted to `123`. When the va By default, binding applies to the element's `onchange` event (`@bind="{PROPERTY OR FIELD}"`). Use `@bind="{PROPERTY OR FIELD}" @bind:event={EVENT}` to trigger binding on a different event. For the `oninput` event (`@bind:event="oninput"`), the reversion occurs after any keystroke that introduces an unparsable value. When targeting the `oninput` event with an `int`-bound type, a user is prevented from typing a `.` character. A `.` character is immediately removed, so the user receives immediate feedback that only whole numbers are permitted. There are scenarios where reverting the value on the `oninput` event isn't ideal, such as when the user should be allowed to clear an unparsable `` value. Alternatives include: * Don't use the `oninput` event. Use the default `onchange` event (only specify `@bind="{PROPERTY OR FIELD}"`), where an invalid value isn't reverted until the element loses focus. -* Bind to a nullable type, such as `int?` or `string`, and provide custom logic to handle invalid entries. -* Use a [form validation component](xref:blazor/forms-validation), such as or . Form validation components have built-in support to manage invalid inputs. Form validation components: +* Bind to a nullable type, such as `int?` or `string` and provide custom logic to handle invalid entries. +* Use a [form validation component](xref:blazor/forms-validation), such as or . Form validation components have built-in support to manage invalid inputs. For more information, see . Form validation components: * Permit the user to provide invalid input and receive validation errors on the associated . * Display validation errors in the UI without interfering with the user entering additional webform data. @@ -127,7 +139,7 @@ In the preceding code, the `` element's field type (`type`) defaults to ` 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. In spite of the recommendation, only use the `yyyy-MM-dd` date format for binding to work correctly if a format is supplied with the `date` field type: +Specifying a format for the `date` field type isn't recommended because Blazor has built-in support to format dates. In spite of the recommendation, only use the `yyyy-MM-dd` date format for binding to function correctly if a format is supplied with the `date` field type: ```razor @@ -142,7 +154,7 @@ The following `Child` component (`Child.razor`) has a `Year` component parameter ```razor
-

Child

+

Child Component

Child Year: @Year