I'm generating an object at runtime to use as the EditForm model. Validation is working but I'm unsure how to set up the ValidationMessage component which requires an Expression<Func<object>>
.
I want to provide it with a property via reflection. Something like this:
<ValidationMessage For="@(() => modelType.GetProperty("MyString").GetValue(model))" />
How can I get an Expression from an object property generated at runtime?
EDIT:
Here is my code:
<EditForm Model="@GeneratedModel" OnInvalidSubmit="@HandleInvalidSubmit" OnValidSubmit="@OnValidSubmit">
<DataAnnotationsValidator />
<input @bind="TestPropBind" type="text" />
<ValidationMessage For="@ValidationFor" />
</EditForm>
@code
{
private object GeneratedModel { get; set; }
private string TestPropBind
{
get
{
PropertyInfo? propertyInfo = GeneratedModel.GetType().GetProperty("Test");
MethodInfo? getMethod = propertyInfo.GetGetMethod();
return getMethod.Invoke(GeneratedModel, new object?[0]) as string;
}
set
{
PropertyInfo? propertyInfo = GeneratedModel.GetType().GetProperty("Test");
MethodInfo? setMethod = propertyInfo.GetSetMethod();
setMethod.Invoke(GeneratedModel, new[] { value });
}
}
protected override void OnInitialized()
{
//GeneratedModel created and instantiated here at runtime
}
}
ValidationMessage
is a fairly simple component. Internally it uses the For
to build a FieldIdentifier
object which it uses to lookup validation messages in the EditContext's
Validation Message Store.
You can shortcut the whole reflection/expression builder process by building your own ValidationMessage
that takes a FieldIdentifier
as a parameter:
@foreach (var message in editContext.GetValidationMessages(Identifier))
{
<div class="validation-message" @attributes=this.AdditionalAttributes>
@message
</div>
}
@code {
[CascadingParameter] private EditContext editContext { get; set; } = default!;
[Parameter, EditorRequired] public FieldIdentifier Identifier { get; set; } = new FieldIdentifier();
[Parameter(CaptureUnmatchedValues = true)] public IReadOnlyDictionary<string, object>? AdditionalAttributes { get; set; }
protected override void OnInitialized()
{
ArgumentNullException.ThrowIfNull(editContext);
}
}
Your demo page:
@page "/"
@using System.ComponentModel.DataAnnotations;
@using System.Reflection;
<PageTitle>Index</PageTitle>
<EditForm Model=this.model OnValidSubmit=this.OnValidSubmit >
<DataAnnotationsValidator/>
<div class="mb-3">
<span class="form-label">Value</span>
<input class="form-control" @bind=this.model.Value />
<MyValidationMessage Identifier=TestPropIdentifier />
</div>
<div class="mb-3">
<button class="btn btn-primary">Submit</button>
</div>
</EditForm>
@code {
private Model model = new();
private FieldIdentifier TestPropIdentifier => new FieldIdentifier(model, "Value");
private string TestPropBind
{
get
{
PropertyInfo? propertyInfo = model.GetType().GetProperty("Value");
MethodInfo? getMethod = propertyInfo?.GetGetMethod();
return getMethod?.Invoke(model, new object?[0]) as string ?? string.Empty ;
}
set
{
PropertyInfo? propertyInfo = model.GetType().GetProperty("Value");
MethodInfo? setMethod = propertyInfo?.GetSetMethod();
setMethod?.Invoke(model, new[] { value });
}
}
private void OnValidSubmit()
{
}
public class Model
{
[Required]
public string? Value { get; set; }
}
}