Skip to content

Commit e48c31d

Browse files
committed
Sample implementation
1 parent 70dedc6 commit e48c31d

File tree

9 files changed

+195
-18
lines changed

9 files changed

+195
-18
lines changed

src/Components/Components/src/ComponentBase.cs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,10 +37,15 @@ public ComponentBase()
3737
{
3838
_hasPendingQueuedRender = false;
3939
_hasNeverRendered = false;
40-
BuildRenderTree(builder);
40+
RenderCore(builder);
4141
};
4242
}
4343

44+
private protected virtual void RenderCore(RenderTreeBuilder builder)
45+
{
46+
BuildRenderTree(builder);
47+
}
48+
4449
/// <summary>
4550
/// Renders the component to the supplied <see cref="RenderTreeBuilder"/>.
4651
/// </summary>
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
namespace BlazorUnitedApp.Data;
5+
6+
public class Address
7+
{
8+
public string Street { get; set; } = string.Empty;
9+
public string City { get; set; } = string.Empty;
10+
public string State { get; set; } = string.Empty;
11+
public string Zip { get; set; } = string.Empty;
12+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
namespace BlazorUnitedApp.Data;
5+
6+
public class Customer
7+
{
8+
public string Name { get; set; } = string.Empty;
9+
public Address BillingAddress { get; set; } = new Address();
10+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
@inherits Editor<Address>
2+
@using BlazorUnitedApp.Data
3+
4+
<div>
5+
<label>
6+
<span>Street</span>
7+
<InputText @bind-Value="Value.Street" />
8+
<ValidationMessage For="() => Value.Street" />
9+
</label>
10+
</div>
11+
<div>
12+
<label>
13+
<span>State</span>
14+
<InputText @bind-Value="Value.State" />
15+
<ValidationMessage For="() => Value.State" />
16+
</label>
17+
</div>
18+
<div>
19+
<label>
20+
<span>Zip</span>
21+
<InputText @bind-Value="Value.Zip" />
22+
<ValidationMessage For="() => Value.Zip" />
23+
</label>
24+
</div>
25+
<div>
26+
<label>
27+
<span>City</span>
28+
<InputText @bind-Value="Value.City" />
29+
<ValidationMessage For="() => Value.City" />
30+
</label>
31+
</div>
Lines changed: 24 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,40 @@
11
@page "/"
2+
@using BlazorUnitedApp.Data;
23
<PageTitle>Index</PageTitle>
34

4-
<h1>@Value?.Parameter</h1>
5-
6-
<EditForm Model="Value?.Parameter">
7-
<InputText @bind-Value="Value!.Parameter" />
5+
<EditForm Model="Value" method="POST" OnSubmit="DisplayCustomer">
6+
<div>
7+
<label>
8+
<span>Name</span>
9+
<InputText @bind-Value="Value!.Name" />
10+
</label>
11+
</div>
12+
<AddressEditor @bind-Value="Value.BillingAddress" />
813
<input type="submit" value="Send" />
914
</EditForm>
1015

1116
@if (_submitted)
1217
{
13-
<p>Submited.</p>
18+
<!-- Display customer data -->
19+
<h3>Customer</h3>
20+
<p>Name: @Value!.Name</p>
21+
<p>Street: @Value.BillingAddress.Street</p>
22+
<p>City: @Value.BillingAddress.City</p>
23+
<p>State: @Value.BillingAddress.State</p>
24+
<p>Zip: @Value.BillingAddress.Zip</p>
1425
}
1526

16-
@code{
17-
[SupplyParameterFromForm] Data? Value { get; set; }
27+
@code {
28+
29+
public void DisplayCustomer()
30+
{
31+
_submitted = true;
32+
}
33+
34+
[SupplyParameterFromForm] Customer? Value { get; set; }
1835

1936
protected override void OnInitialized() => Value ??= new();
2037

2138
bool _submitted = false;
2239
public void Submit() => _submitted = true;
23-
24-
public class Data
25-
{
26-
public string Parameter { get; set; } = "";
27-
}
2840
}

src/Components/Shared/src/ExpressionFormatting/ExpressionFormatter.cs

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,11 @@ public static void ClearCache()
2424
}
2525

2626
public static string FormatLambda(LambdaExpression expression)
27+
{
28+
return FormatLambda(expression, prefix: null);
29+
}
30+
31+
public static string FormatLambda(LambdaExpression expression, string prefix = null)
2732
{
2833
var builder = new ReverseStringBuilder(stackalloc char[StackAllocBufferSize]);
2934
var node = expression.Body;
@@ -45,6 +50,12 @@ public static string FormatLambda(LambdaExpression expression)
4550
throw new InvalidOperationException("Method calls cannot be formatted.");
4651
}
4752

53+
node = methodCallExpression.Object;
54+
if (prefix != null && node is ConstantExpression)
55+
{
56+
break;
57+
}
58+
4859
if (wasLastExpressionMemberAccess)
4960
{
5061
wasLastExpressionMemberAccess = false;
@@ -54,11 +65,16 @@ public static string FormatLambda(LambdaExpression expression)
5465
builder.InsertFront("]");
5566
FormatIndexArgument(methodCallExpression.Arguments[0], ref builder);
5667
builder.InsertFront("[");
57-
node = methodCallExpression.Object;
68+
5869
break;
5970

6071
case ExpressionType.ArrayIndex:
6172
var binaryExpression = (BinaryExpression)node;
73+
node = binaryExpression.Left;
74+
if (prefix != null && node is ConstantExpression)
75+
{
76+
break;
77+
}
6278

6379
if (wasLastExpressionMemberAccess)
6480
{
@@ -69,12 +85,15 @@ public static string FormatLambda(LambdaExpression expression)
6985
builder.InsertFront("]");
7086
FormatIndexArgument(binaryExpression.Right, ref builder);
7187
builder.InsertFront("[");
72-
node = binaryExpression.Left;
7388
break;
7489

7590
case ExpressionType.MemberAccess:
7691
var memberExpression = (MemberExpression)node;
77-
var nextNode = memberExpression.Expression;
92+
node = memberExpression.Expression;
93+
if (prefix != null && node is ConstantExpression)
94+
{
95+
break;
96+
}
7897

7998
if (wasLastExpressionMemberAccess)
8099
{
@@ -85,7 +104,6 @@ public static string FormatLambda(LambdaExpression expression)
85104
var name = memberExpression.Member.Name;
86105
builder.InsertFront(name);
87106

88-
node = nextNode;
89107
break;
90108

91109
default:
@@ -95,6 +113,12 @@ public static string FormatLambda(LambdaExpression expression)
95113
}
96114
}
97115

116+
if (prefix != null)
117+
{
118+
builder.InsertFront(".");
119+
builder.InsertFront(prefix);
120+
}
121+
98122
var result = builder.ToString();
99123

100124
builder.Dispose();
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
using System.Linq.Expressions;
5+
using Microsoft.AspNetCore.Components.Rendering;
6+
7+
namespace Microsoft.AspNetCore.Components.Forms;
8+
public abstract class Editor<T> : ComponentBase
9+
{
10+
private HtmlFieldPrefix _value;
11+
12+
[Parameter] public T Value { get; set; } = default!;
13+
[Parameter] public Expression<Func<T>> ValueExpression { get; set; } = default!;
14+
[Parameter] public EventCallback<T> ValueChanged { get; set; } = default!;
15+
16+
[CascadingParameter] private HtmlFieldPrefix FieldPrefix { get; set; } = default!;
17+
18+
protected override void OnParametersSet()
19+
{
20+
_value = FieldPrefix != null ? FieldPrefix.Combine(ValueExpression) : new HtmlFieldPrefix(ValueExpression);
21+
}
22+
23+
private protected override void RenderCore(RenderTreeBuilder builder)
24+
{
25+
builder.OpenComponent<CascadingValue<HtmlFieldPrefix>>(0);
26+
builder.AddAttribute(1, "Value", _value);
27+
builder.AddAttribute(2, "IsFixed", true);
28+
builder.AddAttribute(3, "ChildContent", (RenderFragment)BuildRenderTree);
29+
builder.CloseComponent();
30+
}
31+
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
using System.Linq.Expressions;
5+
6+
namespace Microsoft.AspNetCore.Components.Forms;
7+
8+
internal class HtmlFieldPrefix(LambdaExpression initial)
9+
{
10+
private readonly LambdaExpression[] _rest;
11+
12+
internal HtmlFieldPrefix(LambdaExpression expression, params LambdaExpression[] rest)
13+
: this(expression)
14+
{
15+
_rest = rest;
16+
}
17+
18+
public string GetPrefix()
19+
{
20+
return "prefix";
21+
}
22+
23+
public HtmlFieldPrefix Combine(LambdaExpression other)
24+
{
25+
var restLength = _rest?.Length ?? 0;
26+
var length = restLength + 1;
27+
var expressions = new LambdaExpression[length];
28+
for (var i = 0; i < restLength - 1; i++)
29+
{
30+
expressions[i] = _rest![i];
31+
}
32+
33+
expressions[length - 1] = other;
34+
35+
return new HtmlFieldPrefix(initial, expressions);
36+
}
37+
38+
public string GetFieldName(LambdaExpression expression)
39+
{
40+
string prefix = ExpressionFormatter.FormatLambda(initial);
41+
var restLength = _rest?.Length ?? 0;
42+
for (int i = 0; i < restLength; i++)
43+
{
44+
prefix = ExpressionFormatter.FormatLambda(_rest![i], prefix);
45+
}
46+
47+
return ExpressionFormatter.FormatLambda(expression, prefix);
48+
}
49+
}

src/Components/Web/src/Forms/InputBase.cs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ public abstract class InputBase<TValue> : ComponentBase, IDisposable
2626

2727
[CascadingParameter] private EditContext? CascadedEditContext { get; set; }
2828

29+
[CascadingParameter] private HtmlFieldPrefix FieldPrefix { get; set; } = default!;
30+
2931
/// <summary>
3032
/// Gets or sets a collection of additional attributes that will be applied to the created element.
3133
/// </summary>
@@ -205,7 +207,8 @@ protected string NameAttributeValue
205207
{
206208
if (_formattedValueExpression is null && ValueExpression is not null)
207209
{
208-
_formattedValueExpression = ExpressionFormatter.FormatLambda(ValueExpression);
210+
_formattedValueExpression = FieldPrefix != null ? FieldPrefix.GetFieldName(ValueExpression) :
211+
ExpressionFormatter.FormatLambda(ValueExpression);
209212
}
210213

211214
return _formattedValueExpression ?? string.Empty;

0 commit comments

Comments
 (0)