-
Notifications
You must be signed in to change notification settings - Fork 10k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[release/8.0-preview6] [Blazor] Form mapping error handling and valid…
…ation integration for SSR Blazor (#49031) Backport of #48990 to release/8.0-preview6 /cc @javiercn # [Blazor] Form mapping error handling and validation integration for SSR Blazor Adds support for error handling and validation to Server Side Rendered Blazor. ## Description Integrates the form data binding experience with Blazor and provides support for error handling. Fixes #46983 (in this specific format) ## Customer Impact This is one of the remaining major functionalities for Blazor Server Side rendering, so we want to make sure we can get as much feedback as possible on the experience. Specially since we are planning an app building exercise. ## Regression? - [ ] Yes - [X] No [If yes, specify the version the behavior has regressed from] ## Risk - [ ] High - [ ] Medium - [X] Low The changes should be additive, and we've included a significant amount of test coverage for the new functionality. ## Verification - [X] Manual (required) - [X] Automated ## Packaging changes reviewed? - [ ] Yes - [ ] No - [X] N/A ---- ## When servicing release/2.1 - [ ] Make necessary changes in eng/PatchConfig.props
- Loading branch information
1 parent
7d75f00
commit 417ce3b
Showing
59 changed files
with
2,684 additions
and
463 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
// Licensed to the .NET Foundation under one or more agreements. | ||
// The .NET Foundation licenses this file to you under the MIT license. | ||
|
||
namespace Microsoft.AspNetCore.Components; | ||
|
||
/// <summary> | ||
/// An error that occurred during the form mapping process. | ||
/// </summary> | ||
public class BindingError | ||
{ | ||
private static readonly char[] Separators = new char[] { '.', '[' }; | ||
private readonly List<FormattableString> _errorMessages; | ||
|
||
/// <summary> | ||
/// Initializes a new instance of <see cref="BindingError"/>. | ||
/// </summary> | ||
/// <param name="path">The path from the root of the binding operation to the property or element that failed to bind.</param> | ||
/// <param name="errorMessages">The error messages associated with the binding error.</param> | ||
/// <param name="attemptedValue">The attempted value that failed to bind.</param> | ||
internal BindingError(string path, List<FormattableString> errorMessages, string? attemptedValue) | ||
{ | ||
_errorMessages = errorMessages; | ||
AttemptedValue = attemptedValue; | ||
Path = path; | ||
Name = GetName(Path); | ||
} | ||
|
||
/// <summary> | ||
/// Gets or sets the instance that contains the property or element that failed to bind. | ||
/// </summary> | ||
/// <remarks> | ||
/// For object models, this is the instance of the object that contains the property that failed to bind. | ||
/// For collection models, this is the collection instance that contains the element that failed to bind. | ||
/// For dictionaries, this is the dictionary instance that contains the element that failed to bind. | ||
/// </remarks> | ||
public object Container { get; internal set; } = null!; | ||
|
||
/// <summary> | ||
/// Gets or sets the name of the property or element that failed to bind. | ||
/// </summary> | ||
public string Name { get; } | ||
|
||
/// <summary> | ||
/// Gets or sets the full path from the model root to the property or element that failed to bind. | ||
/// </summary> | ||
public string Path { get; } | ||
|
||
/// <summary> | ||
/// Gets the list of error messages associated with the binding errors for this field. | ||
/// </summary> | ||
public IReadOnlyList<FormattableString> ErrorMessages => _errorMessages; | ||
|
||
/// <summary> | ||
/// Gets the attempted value that failed to bind (if any). | ||
/// </summary> | ||
public string? AttemptedValue { get; } | ||
|
||
private static string GetName(string path) | ||
{ | ||
var errorKey = path; | ||
var lastSeparatorIndex = path.LastIndexOfAny(Separators); | ||
if (lastSeparatorIndex >= 0) | ||
{ | ||
if (path[lastSeparatorIndex] == '[') | ||
{ | ||
var closingBracket = path.IndexOf(']', lastSeparatorIndex); | ||
// content within brackets | ||
errorKey = path[(lastSeparatorIndex + 1)..closingBracket]; | ||
} | ||
else | ||
{ | ||
errorKey = path[(lastSeparatorIndex + 1)..]; | ||
} | ||
} | ||
|
||
return errorKey; | ||
} | ||
|
||
internal void AddError(FormattableString error) | ||
{ | ||
_errorMessages.Add(error); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
80 changes: 80 additions & 0 deletions
80
src/Components/Components/src/Binding/FormValueSupplierContext.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
// Licensed to the .NET Foundation under one or more agreements. | ||
// The .NET Foundation licenses this file to you under the MIT license. | ||
|
||
namespace Microsoft.AspNetCore.Components.Binding; | ||
|
||
/// <summary> | ||
/// Context for binding a form value. | ||
/// </summary> | ||
public class FormValueSupplierContext | ||
{ | ||
private bool _resultSet; | ||
|
||
/// <summary> | ||
/// Initializes a new instance of <see cref="FormValueSupplierContext"/>. | ||
/// </summary> | ||
/// <param name="formName">The name of the form to bind data from.</param> | ||
/// <param name="valueType">The <see cref="Type"/> of the value to bind.</param> | ||
/// <param name="parameterName">The name of the parameter to bind data to.</param> | ||
public FormValueSupplierContext( | ||
string formName, | ||
Type valueType, | ||
string parameterName) | ||
{ | ||
ArgumentNullException.ThrowIfNull(formName, nameof(formName)); | ||
ArgumentNullException.ThrowIfNull(valueType, nameof(valueType)); | ||
ArgumentNullException.ThrowIfNull(parameterName, nameof(parameterName)); | ||
FormName = formName; | ||
ParameterName = parameterName; | ||
ValueType = valueType; | ||
} | ||
|
||
/// <summary> | ||
/// Gets the name of the form to bind data from. | ||
/// </summary> | ||
public string FormName { get; } | ||
|
||
/// <summary> | ||
/// Gets the name of the parameter to bind data to. | ||
/// </summary> | ||
public string ParameterName { get; } | ||
|
||
/// <summary> | ||
/// Gets the <see cref="Type"/> of the value to bind. | ||
/// </summary> | ||
public Type ValueType { get; } | ||
|
||
/// <summary> | ||
/// Gets the callback to invoke when an error occurs. | ||
/// </summary> | ||
public Action<string, FormattableString, string?>? OnError { get; set; } | ||
|
||
/// <summary> | ||
/// Maps a set of errors to a concrete containing instance. | ||
/// </summary> | ||
/// <remarks> | ||
/// For example, maps errors for a given property in a class to the class instance. | ||
/// This is required so that validation can work without the need of the full identifier. | ||
/// </remarks> | ||
public Action<string, object>? MapErrorToContainer { get; set; } | ||
|
||
/// <summary> | ||
/// Gets the result of the binding operation. | ||
/// </summary> | ||
public object? Result { get; private set; } | ||
|
||
/// <summary> | ||
/// Sets the result of the binding operation. | ||
/// </summary> | ||
/// <param name="result">The result of the binding operation.</param> | ||
/// <exception cref="InvalidOperationException">Thrown if the result has already been set.</exception> | ||
public void SetResult(object? result) | ||
{ | ||
if (_resultSet) | ||
{ | ||
throw new InvalidOperationException($"The result has already been set to '{Result}'."); | ||
} | ||
_resultSet = true; | ||
Result = result; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.