Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions aspnetcore/blazor/call-web-api.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,12 @@ Example:

In the app's `Program` file, call:

<!-- UPDATE 10.0 - Missing API doc for 'Microsoft.Identity.Web.DownstreamApiExtensions.AddDownstreamApi' -->
<!-- UPDATE 11.0 - Awaiting API per https://github.com/dotnet/AspNetCore.Docs/issues/36373.
Marking this for 11.0 because it's the last 10.0 item and inline tracking
now adopts 11.0 or later work items. This ensures that this inline item
isn't missed.

Missing API doc for 'Microsoft.Identity.Web.DownstreamApiExtensions.AddDownstreamApi' -->

* <xref:Microsoft.Identity.Web.MicrosoftIdentityWebApiAuthenticationBuilder.EnableTokenAcquisitionToCallDownstreamApi%2A>: Enables token acquisition to call web APIs.
* `AddDownstreamApi`: Microsoft Identity Web packages provide API to create a named downstream web service for making web API calls. <xref:Microsoft.Identity.Abstractions.IDownstreamApi> is injected into a server-side class, which is used to call <xref:Microsoft.Identity.Abstractions.IDownstreamApi.CallApiForUserAsync%2A> to obtain weather data from an external web API (`MinimalApiJwt` project).
Expand Down Expand Up @@ -194,7 +199,7 @@ builder.Services.AddDataProtection()
`{KEY IDENTIFIER}`: Azure Key Vault key identifier used for key encryption. An access policy allows the application to access the key vault with `Get`, `Unwrap Key`, and `Wrap Key` permissions. The key identifier is obtained from the key in the Entra or Azure portal after it's created. If you enable autorotation of the key vault key, make sure that you use a versionless key identifier in the app's key vault configuration, where no key GUID is placed at the end of the identifier (example: `https://contoso.vault.azure.net/keys/data-protection`).

> [!NOTE]
> In non-Production environments, the preceding example uses <xref:Azure.Identity.DefaultAzureCredential> to simplify authentication while developing apps that deploy to Azure by combining credentials used in Azure hosting environments with credentials used in local development. When moving to production, an alternative is a better choice, such as the <xref:Azure.Identity.ManagedIdentityCredential> shown in the preceding example. For more information, see [Authenticate Azure-hosted .NET apps to Azure resources using a system-assigned managed identity](/dotnet/azure/sdk/authentication/system-assigned-managed-identity).
> In non-`Production` environments, the preceding example uses <xref:Azure.Identity.DefaultAzureCredential> to simplify authentication while developing apps that deploy to Azure by combining credentials used in Azure hosting environments with credentials used in local development. When moving to production, an alternative is a better choice, such as the <xref:Azure.Identity.ManagedIdentityCredential> shown in the preceding example. For more information, see [Authenticate Azure-hosted .NET apps to Azure resources using a system-assigned managed identity](/dotnet/azure/sdk/authentication/system-assigned-managed-identity).

Inject <xref:Microsoft.Identity.Abstractions.IDownstreamApi> and call <xref:Microsoft.Identity.Abstractions.IDownstreamApi.CallApiForUserAsync%2A> when calling on behalf of a user:

Expand Down
40 changes: 31 additions & 9 deletions aspnetcore/blazor/components/data-binding.md
Original file line number Diff line number Diff line change
Expand Up @@ -221,16 +221,38 @@ Additional examples

For more information on the `InputText` component, see <xref:blazor/forms/input-components>.

Components support two-way data binding by defining a pair of `@bind` attributes with either a `:get` or `:set` modifier . The `{PARAMETER}` placeholder in the following examples is used to bind a component parameter:
Components support two-way data binding by defining a pair of `@bind` attributes with either a `:get` or `:set` modifier. The `{PARAMETER}` placeholder in the following examples is used to bind a component parameter:

* `@bind:get`/`@bind-{PARAMETER}:get`: Specifies the value to bind.
* `@bind:set`/`@bind-{PARAMETER}:set`: Specifies a callback for when the value changes.

The `:get` and `:set` modifiers are always used together.
The `:get`/`:set` modifiers are always used together.

With `:get`/`:set` binding, you can react to a value change before it's applied to the DOM, and you can change the applied value, if necessary. Whereas with `@bind:event="{EVENT}"` attribute binding, where the `{EVENT}` placeholder is a DOM event, you receive the notification after the DOM is updated, and there's no capacity to modify the applied value while binding.
For binding to HTML elements, use `:get`/`:set` modifiers to ensure an `<input>` element stays synchronized with a bound value, even when the value is modified in the handler:

The following `BindGetSet` component demonstrates `@bind:get`/`@bind:set` syntax for `<input>` elements and the [`InputText` component](xref:blazor/forms/input-components) used by [Blazor forms](xref:blazor/forms/index) in synchronous (`Set`) and asynchronous (`SetAsync`) scenarios.
```razor
<input @bind:get="inputValue" @bind:set="HandleValueChange" />
```

> [!NOTE]
> The legacy approach prior to the release of .NET 7 used the `value` attribute with `@onchange`:
>
> ```razor
> <input value="@inputValue" @onchange="HandleValueChange" />
> ```
>
> The preceding approach can cause synchronization errors, where the `<input>` element displays a different value than the value held in the bound variable. Migrate to `:get`/`:set` modifiers to avoid this issue.

For component parameters, the following syntaxes for a hypothetical `Child` component are functionally equivalent:

```razor
<Child Value="@inputValue" ValueChanged="@(v => inputValue = v)" />
<Child @bind-Value:get="inputValue" @bind-Value:set="@(v => inputValue = v)" />
```

With `:get`/`:set` modifier binding, you can react to a value change before it's applied to the DOM, and you can change the applied value, if necessary. Whereas with `@bind:event="{EVENT}"` attribute binding, where the `{EVENT}` placeholder is a DOM event, you receive the notification after the DOM is updated, and there's no capacity to modify the applied value while binding.

The following `BindGetSet` component demonstrates `:get`/`:set` syntax for `<input>` elements and the [`InputText` component](xref:blazor/forms/input-components) used by [Blazor forms](xref:blazor/forms/index) in synchronous (`Set`) and asynchronous (`SetAsync`) scenarios.

`BindGetSet.razor`:

Expand Down Expand Up @@ -270,7 +292,7 @@ The following `BindGetSet` component demonstrates `@bind:get`/`@bind:set` syntax

For more information on the `InputText` component, see <xref:blazor/forms/input-components>.

For another example use of `@bind:get` and `@bind:set`, see the [Bind across more than two components](#bind-across-more-than-two-components) section later in this article.
For another example use of `:get` and `:set` modifiers, see the [Bind across more than two components](#bind-across-more-than-two-components) section later in this article.

Razor attribute binding is case-sensitive:

Expand All @@ -279,7 +301,7 @@ Razor attribute binding is case-sensitive:

## Use `@bind:get`/`@bind:set` modifiers and avoid event handlers for two-way data binding

Two-way data binding isn't possible to implement with an event handler. Use `@bind:get`/`@bind:set` modifiers for two-way data binding.
Two-way data binding isn't possible to implement with an event handler. Use `:get`/`:set` modifiers for two-way data binding.

<span aria-hidden="true">❌</span> Consider the following ***dysfunctional approach*** for two-way data binding using an event handler:

Expand All @@ -306,9 +328,9 @@ Two-way data binding isn't possible to implement with an event handler. Use `@bi

The `OnInput` event handler updates the value of `inputValue` to `Long!` after a fourth character is provided. However, the user can continue adding characters to the element value in the UI. The value of `inputValue` isn't bound back to the element's value with each keystroke. The preceding example is only capable of one-way data binding.

The reason for this behavior is that Blazor isn't aware that your code intends to modify the value of `inputValue` in the event handler. Blazor doesn't try to force DOM element values and .NET variable values to match unless they're bound with `@bind` syntax. In earlier versions of Blazor, two-way data binding is implemented by [binding the element to a property and controlling the property's value with its setter](#binding-to-a-property-with-c-get-and-set-accessors). In ASP.NET Core in .NET 7 or later, `@bind:get`/`@bind:set` modifier syntax is used to implement two-way data binding, as the next example demonstrates.
The reason for this behavior is that Blazor isn't aware that your code intends to modify the value of `inputValue` in the event handler. Blazor doesn't try to force DOM element values and .NET variable values to match unless they're bound with `@bind` syntax. In earlier versions of Blazor, two-way data binding is implemented by [binding the element to a property and controlling the property's value with its setter](#binding-to-a-property-with-c-get-and-set-accessors). In ASP.NET Core in .NET 7 or later, `:get`/`:set` modifier syntax is used to implement two-way data binding, as the next example demonstrates.

<span aria-hidden="true">✔️</span> Consider the following ***correct approach*** using `@bind:get`/`@bind:set` for two-way data binding:
<span aria-hidden="true">✔️</span> Consider the following ***correct approach*** using `:get`/`:set` for two-way data binding:

```razor
<p>
Expand All @@ -331,7 +353,7 @@ The reason for this behavior is that Blazor isn't aware that your code intends t
}
```

Using `@bind:get`/`@bind:set` modifiers both controls the underlying value of `inputValue` via `@bind:set` and binds the value of `inputValue` to the element's value via `@bind:get`. The preceding example demonstrates the correct approach for implementing two-way data binding.
Using `:get`/`:set` modifiers both controls the underlying value of `inputValue` via `:set` and binds the value of `inputValue` to the element's value via `:get`. The preceding example demonstrates the correct approach for implementing two-way data binding.

:::moniker-end

Expand Down
2 changes: 1 addition & 1 deletion aspnetcore/blazor/components/quickgrid.md
Original file line number Diff line number Diff line change
Expand Up @@ -477,7 +477,7 @@ With the **Add New Scaffold Item** dialog open to **Installed** > **Common** > *

Complete the **Add Razor Components using Entity Framework (CRUD)** dialog:

<!-- UPDATE 10.0 Keep an eye on https://github.com/dotnet/Scaffolding/issues/3131
<!-- UPDATE 11.0 Keep an eye on https://github.com/dotnet/Scaffolding/issues/3131
for a scaffolding update that makes the scaffolder a little
smarter about suggesting an existing dB context that has a
factory provider. If the scaffolder can do that, this instruction
Expand Down
47 changes: 39 additions & 8 deletions aspnetcore/blazor/forms/validation.md
Original file line number Diff line number Diff line change
Expand Up @@ -1576,27 +1576,23 @@ The <xref:System.ComponentModel.DataAnnotations.CompareAttribute> doesn't work w

Blazor form validation includes support for validating properties of nested objects and collection items with the built-in <xref:Microsoft.AspNetCore.Components.Forms.DataAnnotationsValidator>.

To create a validated form, use a <xref:Microsoft.AspNetCore.Components.Forms.DataAnnotationsValidator> component inside an <xref:Microsoft.AspNetCore.Components.Forms.EditForm> component, just as before.
To create a validated form, use a <xref:Microsoft.AspNetCore.Components.Forms.DataAnnotationsValidator> component inside an <xref:Microsoft.AspNetCore.Components.Forms.EditForm> component.

To opt into the nested objects and collection types validation feature:

1. Call the <xref:Microsoft.Extensions.DependencyInjection.ValidationServiceCollectionExtensions.AddValidation%2A> extension method in the `Program` file where services are registered.
2. Declare the form model types in a C# class file, not in a Razor component (`.razor`).
3. Annotate the root form model type with the [`[ValidatableType]` attribute](xref:Microsoft.Extensions.Validation.ValidatableTypeAttribute).

Without following the preceding steps, form validation behavior doesn't include nested model and collection type validation.

<!-- UPDATE 10.0 - Replace with a fully working, cut-'n-paste example -->

The following example demonstrates customer orders with the improved form validation (details omitted for brevity):
The following example demonstrates customer orders with nested collection form validation.

In `Program.cs`, call <xref:Microsoft.Extensions.DependencyInjection.ValidationServiceCollectionExtensions.AddValidation%2A> on the service collection:

```csharp
builder.Services.AddValidation();
```

In the following `Order` class, the `[ValidatableType]` attribute is required on the top-level model type. The other types are discovered automatically. `OrderItem` and `ShippingAddress` aren't shown for brevity, but nested and collection validation works the same way in those types if they were shown.
In the following `Order` class, the `[ValidatableType]` attribute is required on the top-level model type. The other types are discovered automatically.

`Order.cs`:

Expand All @@ -1622,6 +1618,41 @@ public class Customer
}
```

`OrderItem.cs`:

```csharp
public class OrderItem
{
[Required(ErrorMessage = "Id is required.")]
public int Id { get; set; }

[Required(ErrorMessage = "Description is required.")]
public string? Description { get; set; }

[Required(ErrorMessage = "Price is required.")]
public decimal Price { get; set; }
}
```

`ShippingAddress.cs`:

```csharp
public class ShippingAddress
{
[Required(ErrorMessage = "Street is required.")]
public string? Street { get; set; }

[Required(ErrorMessage = "City is required.")]
public string? City { get; set; }

[Required(ErrorMessage = "State/Province is required.")]
public string? StateProvince { get; set; }

[Required(ErrorMessage = "PostalCode is required.")]
public string? PostalCode { get; set; }
}
```

In the following `OrderPage` component, the <xref:Microsoft.AspNetCore.Components.Forms.DataAnnotationsValidator> component is present in the <xref:Microsoft.AspNetCore.Components.Forms.EditForm> component.

`OrderPage.razor`:
Expand Down Expand Up @@ -1649,7 +1680,7 @@ In the following `OrderPage` component, the <xref:Microsoft.AspNetCore.Component
}
```

The requirement to declare the model types outside of Razor components (`.razor` files) is due to the fact that both the new validation feature and the Razor compiler itself are using a source generator. Currently, output of one source generator can't be used as an input for another source generator.
The requirement to declare the model types outside of Razor components (`.razor` files) is due to the fact that both the nested collection validation feature and the Razor compiler itself are using a source generator. Currently, output of one source generator can't be used as an input for another source generator.

For guidance on using validation models from a different assembly, see the [Use validation models from a different assembly](#use-validation-models-from-a-different-assembly) section.

Expand Down
Loading
Loading