Reputation: 133
If I have a basic <input />
field in a razor page, I bind it using @bind-value="MyModel.Name"
, is there anyway at all to get to know what that input's binding field name is? (i.e I want know it's bound to a field called Name within MyModel.
Upvotes: 1
Views: 1279
Reputation: 20591
Assuming you have a custom component, or are willing to wrap the input as a custom component, then there's a way to do this without reflection.
Component.razor
@inherits InputText
...markup...
@code {
public string FieldName => base.FieldIdentifier.FieldName;
}
Parent.razor
<Component @ref=_component ... />
...other markup...
@code {
private EditContext _editContext; // create this in OnInitialized
private Component _component;
//...
private void Foo() {
var fieldName = _component.FieldName;
var fieldIdentifier = _editContext.Field(_component.FieldName);
}
}
Upvotes: 1
Reputation: 30046
There is no underlying field. It's bound directly to the assigned field.
Razor is markup. The Razor markup gets compiled into a c# class.
This:
@page "/"
<input type="number" @bind-value=this.value />
@code {
private int value;
}
gets compiled into this Render Fragment:
protected override void BuildRenderTree(Microsoft.AspNetCore.Components.Rendering.RenderTreeBuilder __builder)
{
__builder.OpenElement(2, "input");
__builder.AddAttribute(3, "type", "number");
__builder.AddAttribute(4, "value", Microsoft.AspNetCore.Components.BindConverter.FormatValue(this.value, culture: global::System.Globalization.CultureInfo.InvariantCulture));
__builder.AddAttribute(5, "onchange", Microsoft.AspNetCore.Components.EventCallback.Factory.CreateBinder(this, __value => this.value = __value, this.value, culture: global::System.Globalization.CultureInfo.InvariantCulture));
__builder.SetUpdatesAttributeName("value");
__builder.CloseElement();
}
You can wrap the input up as a component and then use some expression code to get the actual fieldname or the object/fieldname you are binding to like this:
@using System.Linq;
@using System.Linq.Expressions;
<input value="@this.Value" @oninput=this.OnValueChange class="@this.CssClass" />
<div class="p-2">
FieldName: @this.FieldName
</div>
@code {
private string? FieldName;
[Parameter] public string? Value { get; set; }
[Parameter] public EventCallback<string> ValueChanged { get; set; }
[Parameter] public Expression<Func<string>> ValueExpression { get; set; } = default!;
[Parameter] public string? CssClass { get; set; }
private void OnValueChange(ChangeEventArgs e)
{
this.ValueChanged.InvokeAsync(e.Value?.ToString() ?? null);
}
protected override void OnInitialized()
{
this.FieldName = ParseFieldName();
}
private string ParseFieldName()
{
if (this.ValueExpression is null)
throw new ArgumentException($"You must provide a ValueExpression for this component.");
var accessorBody = this.ValueExpression.Body;
if (accessorBody is UnaryExpression unaryExpression
&& unaryExpression.NodeType == ExpressionType.Convert
&& unaryExpression.Type == typeof(object))
{
accessorBody = unaryExpression.Operand;
}
if (!(accessorBody is MemberExpression memberExpression))
throw new ArgumentException($"The provided expression is not supported.");
return memberExpression.Member.Name;
}
private void ParseAccessor(out object model, out string fieldName)
{
if (this.ValueExpression is null)
throw new ArgumentException($"You must provide a ValueExpression for this component.");
var accessorBody = this.ValueExpression.Body;
if (accessorBody is UnaryExpression unaryExpression
&& unaryExpression.NodeType == ExpressionType.Convert
&& unaryExpression.Type == typeof(object))
{
accessorBody = unaryExpression.Operand;
}
if (!(accessorBody is MemberExpression memberExpression))
throw new ArgumentException($"The provided expression is not supported.");
fieldName = memberExpression.Member.Name;
if (memberExpression.Expression is ConstantExpression constantExpression)
{
if (constantExpression.Value is null)
{
throw new ArgumentException("The provided expression must evaluate to a non-null value.");
}
model = constantExpression.Value;
return;
}
if (memberExpression.Expression != null)
{
var modelLambda = Expression.Lambda(memberExpression.Expression);
var modelLambdaCompiled = (Func<object?>)modelLambda.Compile();
var result = modelLambdaCompiled();
if (result is null)
{
throw new ArgumentException("The provided expression must evaluate to a non-null value.");
}
model = result;
return;
}
throw new ArgumentException($"The provided expression is not supported.");
}
}
My test page looks like this:
@page "/"
<PageTitle>Index</PageTitle>
<h1>Hello, world!</h1>
Welcome to your new app.
<div class="p-2">
<TextInput @bind-Value="@mySpecialValue"/>
</div>
<div class="p-2">
Value : @mySpecialValue
</div>
@code {
public string? mySpecialValue;
public void SetValue(string e)
=> mySpecialValue = e;
}
Upvotes: 5