My code, below, is a fluenteditform form. After I submit, it says that the fields still need to be filled out even though I filled them out. What is wrong with my code?
@page "/movieform"
@using Assignment10.Entities
@using System.ComponentModel.DataAnnotations
<h3>MovieForm</h3>
<FluentCard>
<FluentEditForm FormName="MovieForm" Model="@movie">
<DataAnnotationsValidator />
<FluentValidationSummary />
<FluentGrid>
<FluentGridItem xs="12">
<FluentTextField Name="MovieNameField" Id="movieNameField" @bind-Value="movie.MovieName" Label="Name: " Required/>
<ValidationMessage For="@(() => movie.MovieName)" />
</FluentGridItem>
<FluentGridItem xs="12">
<FluentTextField Name="MoviePublisherField" Id="moviePublisherField" @bind-Value="movie.Publisher" Label="Publisher: " Required/>
<ValidationMessage For="@(() => movie.Publisher)" />
</FluentGridItem>
<FluentGridItem xs="12" >
<FluentTextField Name="MovieDescriptionField" Id="movieDescriptionField" @bind-Value="movie.MovieDescription" Label="Description: " Required/>
<ValidationMessage For="@(() => movie.MovieDescription)" />
</FluentGridItem>
<FluentGridItem xs="12" >
<FluentButton Type="ButtonType.Submit" Appearance="Appearance.Accent">Submit</FluentButton>
</FluentGridItem>
</FluentGrid>
</FluentEditForm>
</FluentCard>
@code {
private Movie movie = new Movie();
}
My code, below, is a fluenteditform form. After I submit, it says that the fields still need to be filled out even though I filled them out. What is wrong with my code?
@page "/movieform"
@using Assignment10.Entities
@using System.ComponentModel.DataAnnotations
<h3>MovieForm</h3>
<FluentCard>
<FluentEditForm FormName="MovieForm" Model="@movie">
<DataAnnotationsValidator />
<FluentValidationSummary />
<FluentGrid>
<FluentGridItem xs="12">
<FluentTextField Name="MovieNameField" Id="movieNameField" @bind-Value="movie.MovieName" Label="Name: " Required/>
<ValidationMessage For="@(() => movie.MovieName)" />
</FluentGridItem>
<FluentGridItem xs="12">
<FluentTextField Name="MoviePublisherField" Id="moviePublisherField" @bind-Value="movie.Publisher" Label="Publisher: " Required/>
<ValidationMessage For="@(() => movie.Publisher)" />
</FluentGridItem>
<FluentGridItem xs="12" >
<FluentTextField Name="MovieDescriptionField" Id="movieDescriptionField" @bind-Value="movie.MovieDescription" Label="Description: " Required/>
<ValidationMessage For="@(() => movie.MovieDescription)" />
</FluentGridItem>
<FluentGridItem xs="12" >
<FluentButton Type="ButtonType.Submit" Appearance="Appearance.Accent">Submit</FluentButton>
</FluentGridItem>
</FluentGrid>
</FluentEditForm>
</FluentCard>
@code {
private Movie movie = new Movie();
}
Share
Improve this question
edited Nov 18, 2024 at 16:19
Mike Brind
30.2k6 gold badges64 silver badges92 bronze badges
asked Nov 16, 2024 at 20:27
user3776241user3776241
5439 silver badges21 bronze badges
1 Answer
Reset to default 1you use <DataAnnotationsValidator />
- that's the reason your validator haven't receive data - it try to pass data to DataAnnotations infrastructure.
In my project I don't use libraries. Instead I use custom implementation of FluentValidationValidator
component, and code looks like that:
<EditForm Model="Model" OnValidSubmit="OnValidSubmit">
<FluentValidationValidator TValidator="MyModelValidator" />
<ValidationSummary />
// your inputs, with validation messages, like below:
<ValidationMessage For="@(() => Model.Thought)" />
</EditForm>
@code{
private EditContext editContext;
private ValidationMessageStore messageStore;
protected override void OnInitialized()
{
Model = new MyModel();
editContext = new EditContext(Model);
messageStore = new(editContext);
base.OnInitialized();
}
private async Task OnValidSubmit()
{
// your save logic here
}
}
And component itself (code found on stackoverflow):
public class FluentValidationValidator<TValidator> : ComponentBase where TValidator : IValidator, new()
{
private readonly static char[] separators = new[] { '.', '[' };
private TValidator validator;
[CascadingParameter]
private EditContext EditContext { get; set; }
protected override void OnInitialized()
{
validator = new TValidator();
var messages = new ValidationMessageStore(EditContext);
/* Re-validate when any field changes or when the entire form requests validation.*/
EditContext.OnFieldChanged += (sender, eventArgs)
=> ValidateModel((EditContext)sender!, messages, false);
EditContext.OnValidationRequested += (sender, eventArgs)
=> ValidateModel((EditContext)sender!, messages, true);
}
private void ValidateModel(EditContext editContext, ValidationMessageStore messages, bool submit)
{
if (submit)
editContext.Properties["submitted"] = true;
if (!editContext.Properties.TryGetValue("submitted", out _))
return;
var context = new ValidationContext<object>(editContext.Model);
var validationResult = validator.Validate(context);
messages.Clear();
foreach (var error in validationResult.Errors)
{
var fieldIdentifier = ToFieldIdentifier(editContext, error.PropertyName);
messages.Add(fieldIdentifier, error.ErrorMessage);
}
editContext.NotifyValidationStateChanged();
}
private static FieldIdentifier ToFieldIdentifier(EditContext editContext, string propertyPath)
{
var obj = editContext.Model;
while (true)
{
var nextTokenEnd = propertyPath.IndexOfAny(separators);
if (nextTokenEnd < 0)
{
return new FieldIdentifier(obj, propertyPath);
}
var nextToken = propertyPath.Substring(0, nextTokenEnd);
propertyPath = propertyPath.Substring(nextTokenEnd + 1);
object? newObj;
if (nextToken.EndsWith("]"))
{
nextToken = nextToken.Substring(0, nextToken.Length - 1);
var prop = obj.GetType().GetProperty("Item");
var indexerType = prop!.GetIndexParameters()[0].ParameterType;
var indexerValue = Convert.ChangeType(nextToken, indexerType);
newObj = prop.GetValue(obj, [indexerValue]);
}
else
{
var prop = obj.GetType().GetProperty(nextToken);
if (prop == null)
{
throw new InvalidOperationException($"Could not find property named {nextToken} in object of type {obj.GetType().FullName}.");
}
newObj = prop.GetValue(obj);
}
if (newObj == null)
{
return new FieldIdentifier(obj, nextToken);
}
obj = newObj;
}
}
}
It works for me on several projects. Pros is you can extend component and add some custom logic, if necessary. Cons is you need to support it in your project. So if you decide to not support, you can try to use something from this link.
Hope it helpful.