Skip to content

Commit 1fbcb22

Browse files
Eliminate unused binding context ID concept
1 parent 72704a4 commit 1fbcb22

10 files changed

+147
-393
lines changed

src/Components/Web/src/Forms/EditForm.cs

Lines changed: 6 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -78,17 +78,14 @@ public EditContext? EditContext
7878
/// </summary>
7979
[Parameter] public EventCallback<EditContext> OnInvalidSubmit { get; set; }
8080

81-
[CascadingParameter] private FormMappingContext? BindingContext { get; set; }
81+
[CascadingParameter] private FormMappingContext? MappingContext { get; set; }
8282

8383
/// <summary>
8484
/// Gets or sets the form handler name. This is not used by interactive forms.
8585
/// It is only used when posting to a server-side endpoint.
8686
/// </summary>
87-
/// <remarks>
8887
[Parameter] public string? FormHandlerName { get; set; }
8988

90-
[Inject] private NavigationManager? TempNav { get; set; } // TODO: Remove
91-
9289
/// <inheritdoc />
9390
protected override void OnParametersSet()
9491
{
@@ -133,9 +130,8 @@ protected override void BuildRenderTree(RenderTreeBuilder builder)
133130

134131
builder.OpenElement(0, "form");
135132

136-
if (BindingContext != null)
133+
if (MappingContext != null)
137134
{
138-
// TODO: Remove bindingcontext.id concept
139135
builder.AddAttribute(2, "method", "post");
140136
}
141137

@@ -144,11 +140,11 @@ protected override void BuildRenderTree(RenderTreeBuilder builder)
144140

145141
// In SSR cases, we register onsubmit as a named event and emit other child elements
146142
// to include the handler and antiforgery token in the post data
147-
if (BindingContext != null)
143+
if (MappingContext != null)
148144
{
149-
var submitEventName = CombineStrings(BindingContext.Name, FormHandlerName) ?? string.Empty;
150-
builder.AddNamedEvent(5, "onsubmit", submitEventName);
151-
RenderSSRFormHandlingChildren(builder, 6, submitEventName);
145+
var combinedFormName = MappingContext.GetCombinedFormName(FormHandlerName) ?? string.Empty;
146+
builder.AddNamedEvent(5, "onsubmit", combinedFormName);
147+
RenderSSRFormHandlingChildren(builder, 6, combinedFormName);
152148
}
153149

154150
builder.OpenComponent<CascadingValue<EditContext>>(7);
@@ -162,9 +158,6 @@ protected override void BuildRenderTree(RenderTreeBuilder builder)
162158
builder.CloseRegion();
163159
}
164160

165-
private static string? CombineStrings(string? a, string? b)
166-
=> string.IsNullOrEmpty(a) ? b : string.IsNullOrEmpty(b) ? a : $"{a}.{b}";
167-
168161
private void RenderSSRFormHandlingChildren(RenderTreeBuilder builder, int sequence, string submitEventName)
169162
{
170163
builder.OpenRegion(sequence);

src/Components/Web/src/Forms/Mapping/FormMappingContext.cs

Lines changed: 12 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -14,33 +14,17 @@ public sealed class FormMappingContext
1414
private List<KeyValuePair<string, FormMappingError>>? _pendingErrors;
1515
private Dictionary<string, Dictionary<string, FormMappingError>>? _errorsByFormName;
1616

17-
internal FormMappingContext(string name, string mappingContextId)
17+
internal FormMappingContext(string name)
1818
{
1919
ArgumentNullException.ThrowIfNull(name);
20-
ArgumentNullException.ThrowIfNull(mappingContextId);
21-
// We are initializing the root context, that can be a "named" root context, or the default context.
22-
// A named root context only provides a name, and that acts as the MappingId
23-
// A "default" root context does not provide a name, and instead it provides an explicit Mapping ID.
24-
// The explicit mapping ID matches that of the default handler, which is the URL Path.
25-
if (string.IsNullOrEmpty(name) ^ string.IsNullOrEmpty(mappingContextId))
26-
{
27-
throw new InvalidOperationException("A root mapping context needs to provide a name and explicit mapping context id or none.");
28-
}
29-
3020
Name = name;
31-
MappingContextId = mappingContextId ?? name;
3221
}
3322

3423
/// <summary>
3524
/// The context name.
3625
/// </summary>
3726
public string Name { get; }
3827

39-
/// <summary>
40-
/// The computed identifier used to determine what parts of the app can map data.
41-
/// </summary>
42-
public string MappingContextId { get; }
43-
4428
/// <summary>
4529
/// Retrieves the list of errors for a given model key.
4630
/// </summary>
@@ -108,8 +92,17 @@ public IEnumerable<FormMappingError> GetAllErrors(string formName)
10892
_errorsByFormName?.TryGetValue(formName, out var formErrors) == true &&
10993
formErrors.TryGetValue(key, out var mappingError) ? mappingError.AttemptedValue : null;
11094

111-
internal static string Combine(FormMappingContext? parentContext, string name) =>
112-
string.IsNullOrEmpty(parentContext?.Name) ? name : $"{parentContext.Name}.{name}";
95+
internal string GetCombinedFormName(string? formHandlerName)
96+
{
97+
if (string.IsNullOrEmpty(Name))
98+
{
99+
return string.IsNullOrEmpty(formHandlerName) ? string.Empty : formHandlerName;
100+
}
101+
else
102+
{
103+
return string.IsNullOrEmpty(formHandlerName) ? Name : $"{Name}.{formHandlerName}";
104+
}
105+
}
113106

114107
internal void AddError(string key, FormattableString error, string? attemptedValue)
115108
{

src/Components/Web/src/Forms/Mapping/FormMappingScope.cs

Lines changed: 10 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
namespace Microsoft.AspNetCore.Components.Forms;
99

1010
/// <summary>
11-
/// Defines the mapping context for data received from form posts.
11+
/// Defines the mapping scope for data received from form posts.
1212
/// </summary>
1313
public sealed class FormMappingScope : ICascadingValueSupplier, IComponent
1414
{
@@ -17,19 +17,15 @@ public sealed class FormMappingScope : ICascadingValueSupplier, IComponent
1717
private bool _hasPendingQueuedRender;
1818

1919
/// <summary>
20-
/// The mapping context name.
20+
/// The mapping scope name.
2121
/// </summary>
22-
[Parameter] public string Name { get; set; } = "";
22+
[Parameter, EditorRequired] public string Name { get; set; } = default!;
2323

2424
/// <summary>
2525
/// Specifies the content to be rendered inside this <see cref="FormMappingScope"/>.
2626
/// </summary>
2727
[Parameter] public RenderFragment<FormMappingContext> ChildContent { get; set; } = default!;
2828

29-
[CascadingParameter] private FormMappingContext? ParentContext { get; set; }
30-
31-
[Inject] internal NavigationManager Navigation { get; set; } = null!;
32-
3329
[Inject] internal IFormValueMapper? FormValueModelBinder { get; set; } // Nonnull only on platforms that support HTTP form posts
3430

3531
void IComponent.Attach(RenderHandle renderHandle)
@@ -40,16 +36,17 @@ void IComponent.Attach(RenderHandle renderHandle)
4036
Task IComponent.SetParametersAsync(ParameterView parameters)
4137
{
4238
parameters.SetParameterProperties(this);
43-
if (ParentContext != null && string.IsNullOrEmpty(Name))
44-
{
45-
throw new InvalidOperationException($"Nested form mapping contexts must define a Name. (Parent context) = '{ParentContext.Name}'.");
46-
}
4739

4840
if (_cascadingValueSupplier is null)
4941
{
50-
_cascadingValueSupplier = new SupplyParameterFromFormValueProvider(FormValueModelBinder, Navigation, ParentContext, Name);
42+
if (string.IsNullOrEmpty(Name))
43+
{
44+
throw new InvalidOperationException($"The {nameof(FormMappingScope)} component requires a nonempty {nameof(Name)} parameter value.");
45+
}
46+
47+
_cascadingValueSupplier = new SupplyParameterFromFormValueProvider(FormValueModelBinder, Name);
5148
}
52-
else if (!string.Equals(Name, _cascadingValueSupplier.Name))
49+
else if (!string.Equals(Name, _cascadingValueSupplier.MappingScopeName))
5350
{
5451
throw new InvalidOperationException($"{nameof(FormMappingScope)} '{nameof(Name)}' can't change after initialization.");
5552
}

src/Components/Web/src/Forms/Mapping/SupplyParameterFromFormServiceCollectionExtensions.cs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,7 @@ public static IServiceCollection AddSupplyValueFromFormProvider(this IServiceCol
2222
{
2323
return new SupplyParameterFromFormValueProvider(
2424
services.GetRequiredService<IFormValueMapper>(),
25-
services.GetRequiredService<NavigationManager>(),
26-
null, "");
25+
mappingScopeName: "");
2726
}));
2827

2928
return serviceCollection;

src/Components/Web/src/Forms/Mapping/SupplyParameterFromFormValueProvider.cs

Lines changed: 19 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -6,47 +6,26 @@
66

77
namespace Microsoft.AspNetCore.Components.Forms.Mapping;
88

9+
// Provides values for [SupplyParameterFromForm] parameters on components.
10+
// It is used in two ways:
11+
// - By default, an instance is registered in DI, supplying values outside any FormMappingScope
12+
// - If there is a FormMappingScope, internally it creates an instance of this to implement ICascadingValueSupplier for it
913
internal class SupplyParameterFromFormValueProvider : ICascadingValueSupplier
1014
{
11-
private readonly FormMappingContext _mappingContext;
1215
private readonly IFormValueMapper? _formValueMapper;
16+
private readonly FormMappingContext _mappingContext;
1317

1418
public FormMappingContext MappingContext => _mappingContext;
1519

16-
public SupplyParameterFromFormValueProvider(IFormValueMapper? formValueMapper, NavigationManager navigation, FormMappingContext? parentContext, string thisName)
20+
public SupplyParameterFromFormValueProvider(IFormValueMapper? formValueMapper, string mappingScopeName)
1721
{
18-
ArgumentNullException.ThrowIfNull(navigation);
19-
2022
_formValueMapper = formValueMapper;
21-
Name = thisName;
22-
23-
// MappingContextId: action parameter used to define the handler
24-
// Name: form name and context used to map
25-
// Cases:
26-
// 1) No name ("")
27-
// Name = "";
28-
// MappingContextId = "";
29-
// <form name="" action="" />
30-
// 2) Name provided
31-
// Name = "my-handler";
32-
// MappingContextId = <<base-relative-uri>>((<<existing-query>>&)|?)handler=my-handler
33-
// <form name="my-handler" action="relative/path?existing=value&handler=my-handler
34-
// 3) Parent has a name "parent-name"
35-
// Name = "parent-name.my-handler";
36-
// MappingContextId = <<base-relative-uri>>((<<existing-query>>&)|?)handler=my-handler
37-
var name = FormMappingContext.Combine(parentContext, thisName);
38-
var mappingId = string.IsNullOrEmpty(name) ? "" : GenerateMappingContextId(name);
39-
_mappingContext = new FormMappingContext(name, mappingId);
40-
41-
string GenerateMappingContextId(string name)
42-
{
43-
var mappingId = navigation.ToBaseRelativePath(navigation.GetUriWithQueryParameter("handler", name));
44-
var hashIndex = mappingId.IndexOf('#');
45-
return hashIndex == -1 ? mappingId : new string(mappingId.AsSpan(0, hashIndex));
46-
}
23+
_mappingContext = new FormMappingContext(mappingScopeName);
24+
25+
MappingScopeName = mappingScopeName;
4726
}
4827

49-
public string Name { get; }
28+
public string MappingScopeName { get; }
5029

5130
bool ICascadingValueSupplier.IsFixed => true;
5231

@@ -59,10 +38,10 @@ public bool CanSupplyValue(in CascadingParameterInfo parameterInfo)
5938
}
6039

6140
// We also supply values for [SupplyValueFromForm]
62-
if (_formValueMapper is not null && parameterInfo.Attribute is SupplyParameterFromFormAttribute)
41+
if (_formValueMapper is not null && parameterInfo.Attribute is SupplyParameterFromFormAttribute supplyParameterFromFormAttribute)
6342
{
64-
var (formName, valueType) = GetFormNameAndValueType(_mappingContext, parameterInfo);
65-
return _formValueMapper.CanMap(valueType, formName);
43+
var combinedFormName = _mappingContext.GetCombinedFormName(supplyParameterFromFormAttribute.Handler);
44+
return _formValueMapper.CanMap(parameterInfo.PropertyType, combinedFormName);
6645
}
6746

6847
return false;
@@ -77,9 +56,9 @@ public bool CanSupplyValue(in CascadingParameterInfo parameterInfo)
7756
}
7857

7958
// We also supply values for [SupplyValueFromForm]
80-
if (_formValueMapper is { } valueMapper && parameterInfo.Attribute is SupplyParameterFromFormAttribute)
59+
if (_formValueMapper is { } valueMapper && parameterInfo.Attribute is SupplyParameterFromFormAttribute supplyParameterFromFormAttribute)
8160
{
82-
return GetFormPostValue(valueMapper, _mappingContext, parameterInfo);
61+
return GetFormPostValue(valueMapper, _mappingContext, parameterInfo, supplyParameterFromFormAttribute);
8362
}
8463

8564
throw new InvalidOperationException($"Received an unexpected attribute type {parameterInfo.Attribute.GetType()}");
@@ -91,18 +70,18 @@ void ICascadingValueSupplier.Subscribe(ComponentState subscriber, in CascadingPa
9170
void ICascadingValueSupplier.Unsubscribe(ComponentState subscriber, in CascadingParameterInfo parameterInfo)
9271
=> throw new NotSupportedException(); // IsFixed = true, so the framework won't call this
9372

94-
internal static object? GetFormPostValue(IFormValueMapper formValueMapper, FormMappingContext? mappingContext, in CascadingParameterInfo parameterInfo)
73+
internal static object? GetFormPostValue(IFormValueMapper formValueMapper, FormMappingContext? mappingContext, in CascadingParameterInfo parameterInfo, SupplyParameterFromFormAttribute supplyParameterFromFormAttribute)
9574
{
9675
Debug.Assert(mappingContext != null);
97-
var (formName, valueType) = GetFormNameAndValueType(mappingContext, parameterInfo);
76+
var combinedFormName = mappingContext.GetCombinedFormName(supplyParameterFromFormAttribute.Handler);
9877

9978
var parameterName = parameterInfo.Attribute.Name ?? parameterInfo.PropertyName;
10079
var handler = ((SupplyParameterFromFormAttribute)parameterInfo.Attribute).Handler;
10180
Action<string, FormattableString, string?> errorHandler = string.IsNullOrEmpty(handler) ?
10281
mappingContext.AddError :
103-
(name, message, value) => mappingContext.AddError(formName, parameterName, message, value);
82+
(name, message, value) => mappingContext.AddError(combinedFormName, parameterName, message, value);
10483

105-
var context = new FormValueMappingContext(formName!, valueType, parameterName)
84+
var context = new FormValueMappingContext(combinedFormName, parameterInfo.PropertyType, parameterName)
10685
{
10786
OnError = errorHandler,
10887
MapErrorToContainer = mappingContext.AttachParentValue
@@ -112,15 +91,4 @@ void ICascadingValueSupplier.Unsubscribe(ComponentState subscriber, in Cascading
11291

11392
return context.Result;
11493
}
115-
116-
private static (string FormName, Type ValueType) GetFormNameAndValueType(FormMappingContext? mappingContext, in CascadingParameterInfo parameterInfo)
117-
{
118-
var valueType = parameterInfo.PropertyType;
119-
var valueName = ((SupplyParameterFromFormAttribute)parameterInfo.Attribute).Handler;
120-
var formName = string.IsNullOrEmpty(valueName) ?
121-
(mappingContext?.Name) :
122-
FormMappingContext.Combine(mappingContext, valueName);
123-
124-
return (formName!, valueType);
125-
}
12694
}

src/Components/Web/src/PublicAPI.Unshipped.txt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@ Microsoft.AspNetCore.Components.Forms.FormMappingContext.GetAttemptedValue(strin
2525
Microsoft.AspNetCore.Components.Forms.FormMappingContext.GetAttemptedValue(string! key) -> string?
2626
Microsoft.AspNetCore.Components.Forms.FormMappingContext.GetErrors(string! formName, string! key) -> Microsoft.AspNetCore.Components.Forms.Mapping.FormMappingError?
2727
Microsoft.AspNetCore.Components.Forms.FormMappingContext.GetErrors(string! key) -> Microsoft.AspNetCore.Components.Forms.Mapping.FormMappingError?
28-
Microsoft.AspNetCore.Components.Forms.FormMappingContext.MappingContextId.get -> string!
2928
Microsoft.AspNetCore.Components.Forms.FormMappingContext.Name.get -> string!
3029
Microsoft.AspNetCore.Components.Forms.FormMappingScope
3130
Microsoft.AspNetCore.Components.Forms.FormMappingScope.ChildContent.get -> Microsoft.AspNetCore.Components.RenderFragment<Microsoft.AspNetCore.Components.Forms.FormMappingContext!>!

0 commit comments

Comments
 (0)