Forms/input components article NRT updates (#29404)

pull/29397/head
Luke Latham 2023-06-01 14:03:43 -04:00 committed by GitHub
parent 8c42f2b884
commit f86d0c71e2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 270 additions and 7 deletions

View File

@ -150,12 +150,27 @@ In the following component code, `newItemName` is provided by a bound element of
}
```
<xref:System.Net.Http.Json.HttpClientJsonExtensions.PostAsJsonAsync%2A> returns an <xref:System.Net.Http.HttpResponseMessage>. To deserialize the JSON content from the response message, use the <xref:System.Net.Http.Json.HttpContentJsonExtensions.ReadFromJsonAsync%2A> extension method. The following example reads JSON weather data:
<xref:System.Net.Http.Json.HttpClientJsonExtensions.PostAsJsonAsync%2A> returns an <xref:System.Net.Http.HttpResponseMessage>. To deserialize the JSON content from the response message, use the <xref:System.Net.Http.Json.HttpContentJsonExtensions.ReadFromJsonAsync%2A> extension method. The following example reads JSON weather data as an array:
:::moniker range=">= aspnetcore-6.0"
```csharp
var content = await response.Content.ReadFromJsonAsync<WeatherForecast>();
var content = await response.Content.ReadFromJsonAsync<WeatherForecast[]>() ??
Array.Empty<WeatherForecast>();
```
In the preceding example, an empty array is created if no weather data is returned by the method, so `content` isn't null after the statement executes.
:::moniker-end
:::moniker range="< aspnetcore-6.0"
```csharp
var content = await response.Content.ReadFromJsonAsync<WeatherForecast[]>();
```
:::moniker-end
### PUT as JSON (`PutAsJsonAsync`)
<xref:System.Net.Http.Json.HttpClientJsonExtensions.PutAsJsonAsync%2A> sends an HTTP PUT request with JSON-encoded content.
@ -186,12 +201,27 @@ In the following component code, `editItem` values for `Name` and `IsCompleted`
}
```
<xref:System.Net.Http.Json.HttpClientJsonExtensions.PutAsJsonAsync%2A> returns an <xref:System.Net.Http.HttpResponseMessage>. To deserialize the JSON content from the response message, use the <xref:System.Net.Http.Json.HttpContentJsonExtensions.ReadFromJsonAsync%2A> extension method. The following example reads JSON weather data:
<xref:System.Net.Http.Json.HttpClientJsonExtensions.PutAsJsonAsync%2A> returns an <xref:System.Net.Http.HttpResponseMessage>. To deserialize the JSON content from the response message, use the <xref:System.Net.Http.Json.HttpContentJsonExtensions.ReadFromJsonAsync%2A> extension method. The following example reads JSON weather data as an array:
:::moniker range=">= aspnetcore-6.0"
```csharp
var content = await response.Content.ReadFromJsonAsync<WeatherForecast>();
var content = await response.Content.ReadFromJsonAsync<WeatherForecast[]>() ??
Array.Empty<WeatherForecast>();
```
In the preceding example, an empty array is created if no weather data is returned by the method, so `content` isn't null after the statement executes.
:::moniker-end
:::moniker range="< aspnetcore-6.0"
```csharp
var content = await response.Content.ReadFromJsonAsync<WeatherForecast[]>();
```
:::moniker-end
:::moniker range=">= aspnetcore-7.0"
### PATCH as JSON (`PatchAsJsonAsync`)
@ -234,12 +264,15 @@ The following example doesn't show loading `incompleteTodoItems` for brevity. Se
}
```
<xref:System.Net.Http.Json.HttpClientJsonExtensions.PatchAsJsonAsync%2A> returns an <xref:System.Net.Http.HttpResponseMessage>. To deserialize the JSON content from the response message, use the <xref:System.Net.Http.Json.HttpContentJsonExtensions.ReadFromJsonAsync%2A> extension method. The following example reads JSON weather data:
<xref:System.Net.Http.Json.HttpClientJsonExtensions.PatchAsJsonAsync%2A> returns an <xref:System.Net.Http.HttpResponseMessage>. To deserialize the JSON content from the response message, use the <xref:System.Net.Http.Json.HttpContentJsonExtensions.ReadFromJsonAsync%2A> extension method. The following example reads JSON weather data as an array:
```csharp
var content = await response.Content.ReadFromJsonAsync<WeatherForecast>();
var content = await response.Content.ReadFromJsonAsync<WeatherForecast[]>() ??
Array.Empty<WeatherForecast>();
```
In the preceding example, an empty array is created if no weather data is returned by the method, so `content` isn't null after the statement executes.
:::moniker-end
### Additional extension methods

View File

@ -973,6 +973,8 @@ In the following `FormExample6` component, update the namespace of the **`Shared
`Pages/FormExample6.razor`:
:::moniker range=">= aspnetcore-6.0"
```razor
@page "/form-example-6"
@using System.Net
@ -1069,7 +1071,8 @@ In the following `FormExample6` component, update the namespace of the **`Shared
"StarshipValidation", (Starship)editContext.Model);
var errors = await response.Content
.ReadFromJsonAsync<Dictionary<string, List<string>>>();
.ReadFromJsonAsync<Dictionary<string, List<string>>>() ??
new Dictionary<string, List<string>>();
if (response.StatusCode == HttpStatusCode.BadRequest &&
errors.Any())
@ -1103,6 +1106,142 @@ In the following `FormExample6` component, update the namespace of the **`Shared
}
```
:::moniker-end
:::moniker range="< aspnetcore-6.0"
```razor
@page "/form-example-6"
@using System.Net
@using System.Net.Http.Json
@using Microsoft.AspNetCore.Authorization
@using Microsoft.AspNetCore.Components.WebAssembly.Authentication
@using Microsoft.Extensions.Logging
@using BlazorSample.Shared
@attribute [Authorize]
@inject HttpClient Http
@inject ILogger<FormExample6> Logger
<h1>Starfleet Starship Database</h1>
<h2>New Ship Entry Form</h2>
<EditForm Model="@starship" OnValidSubmit="@HandleValidSubmit">
<DataAnnotationsValidator />
<CustomValidation @ref="customValidation" />
<ValidationSummary />
<p>
<label>
Identifier:
<InputText @bind-Value="starship.Identifier" disabled="@disabled" />
</label>
</p>
<p>
<label>
Description (optional):
<InputTextArea @bind-Value="starship.Description"
disabled="@disabled" />
</label>
</p>
<p>
<label>
Primary Classification:
<InputSelect @bind-Value="starship.Classification" disabled="@disabled">
<option value="">Select classification ...</option>
<option value="Exploration">Exploration</option>
<option value="Diplomacy">Diplomacy</option>
<option value="Defense">Defense</option>
</InputSelect>
</label>
</p>
<p>
<label>
Maximum Accommodation:
<InputNumber @bind-Value="starship.MaximumAccommodation"
disabled="@disabled" />
</label>
</p>
<p>
<label>
Engineering Approval:
<InputCheckbox @bind-Value="starship.IsValidatedDesign"
disabled="@disabled" />
</label>
</p>
<p>
<label>
Production Date:
<InputDate @bind-Value="starship.ProductionDate" disabled="@disabled" />
</label>
</p>
<button type="submit" disabled="@disabled">Submit</button>
<p style="@messageStyles">
@message
</p>
<p>
<a href="http://www.startrek.com/">Star Trek</a>,
&copy;1966-2019 CBS Studios, Inc. and
<a href="https://www.paramount.com">Paramount Pictures</a>
</p>
</EditForm>
@code {
private bool disabled;
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();
try
{
var response = await Http.PostAsJsonAsync<Starship>(
"StarshipValidation", (Starship)editContext.Model);
var errors = await response.Content
.ReadFromJsonAsync<Dictionary<string, List<string>>>();
if (response.StatusCode == HttpStatusCode.BadRequest &&
errors.Any())
{
customValidation.DisplayErrors(errors);
}
else if (!response.IsSuccessStatusCode)
{
throw new HttpRequestException(
$"Validation failed. Status Code: {response.StatusCode}");
}
else
{
disabled = true;
messageStyles = "color:green";
message = "The form has been processed.";
}
}
catch (AccessTokenNotAvailableException ex)
{
ex.Redirect();
}
catch (Exception ex)
{
Logger.LogError("Form processing error: {Message}", ex.Message);
disabled = true;
messageStyles = "color:red";
message = "There was an error processing the form.";
}
}
}
```
:::moniker-end
> [!NOTE]
> As an alternative to the use of a [validation component](#validator-components), data annotation validation attributes can be used. Custom attributes applied to the form's model activate with the use of the <xref:Microsoft.AspNetCore.Components.Forms.DataAnnotationsValidator> component. When used with server-side validation, the attributes must be executable on the server. For more information, see <xref:mvc/models/validation#alternatives-to-built-in-attributes>.
@ -1225,6 +1364,10 @@ Make the `enums` accessible to the:
Use <xref:Microsoft.AspNetCore.Components.Forms.InputRadio%601> components with the <xref:Microsoft.AspNetCore.Components.Forms.InputRadioGroup%601> component to create a radio button group. In the following example, properties are added to the `Starship` model described in the [Example form](#example-form) section:
:::moniker-end
:::moniker range=">= aspnetcore-6.0"
```csharp
[Required]
[Range(typeof(Manufacturer), nameof(Manufacturer.SpaceX),
@ -1238,6 +1381,27 @@ public Color? Color { get; set; } = null;
public Engine? Engine { get; set; } = null;
```
:::moniker-end
:::moniker range=">= aspnetcore-5.0 < aspnetcore-6.0"
```csharp
[Required]
[Range(typeof(Manufacturer), nameof(Manufacturer.SpaceX),
nameof(Manufacturer.VirginGalactic), ErrorMessage = "Pick a manufacturer.")]
public Manufacturer Manufacturer { get; set; } = Manufacturer.Unknown;
[Required, EnumDataType(typeof(Color))]
public Color Color { get; set; } = null;
[Required, EnumDataType(typeof(Engine))]
public Engine Engine { get; set; } = null;
```
:::moniker-end
:::moniker range=">= aspnetcore-5.0"
Update the `Starfleet Starship Database` form (`FormExample2` component) from the [Example form](#example-form) section. Add the components to produce:
* A radio button group for the ship manufacturer.
@ -1473,6 +1637,8 @@ The `IsValid` method of the following `SaladChefValidatorAttribute` class obtain
`SaladChefValidatorAttribute.cs`:
:::moniker range=">= aspnetcore-6.0"
```csharp
using System.ComponentModel.DataAnnotations;
@ -1493,10 +1659,38 @@ public class SaladChefValidatorAttribute : ValidationAttribute
}
```
:::moniker-end
:::moniker range="< aspnetcore-6.0"
```csharp
using System.ComponentModel.DataAnnotations;
public class SaladChefValidatorAttribute : ValidationAttribute
{
protected override ValidationResult IsValid(object value,
ValidationContext validationContext)
{
var saladChef = validationContext.GetRequiredService<SaladChef>();
if (saladChef.ThingsYouCanPutInASalad.Contains(value?.ToString()))
{
return ValidationResult.Success;
}
return new ValidationResult("You should not put that in a salad!");
}
}
```
:::moniker-end
The following `ValidationWithDI` component validates user input by applying the `SaladChefValidatorAttribute` (`[SaladChefValidator]`) to the salad ingredient string (`SaladIngredient`).
`Pages/ValidationWithDI.razor`:
:::moniker range=">= aspnetcore-6.0"
```razor
@page "/validation-with-di"
@using System.ComponentModel.DataAnnotations
@ -1527,6 +1721,42 @@ The following `ValidationWithDI` component validates user input by applying the
}
```
:::moniker-end
:::moniker range="< aspnetcore-6.0"
```razor
@page "/validation-with-di"
@using System.ComponentModel.DataAnnotations
@using Microsoft.AspNetCore.Components.Forms
<EditForm Model="@this" autocomplete="off">
<DataAnnotationsValidator />
<p>
Name something you can put in a salad:
<input @bind="SaladIngredient" />
</p>
<button type="submit">Submit</button>
<ul>
@foreach (var message in context.GetValidationMessages())
{
<li class="validation-message">@message</li>
}
</ul>
</EditForm>
@code {
[SaladChefValidator]
public string SaladIngredient { get; set; }
}
```
:::moniker-end
## Custom validation CSS class attributes
:::moniker range=">= aspnetcore-7.0"