Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions Demo.Forms/Models/NestedForm.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,14 @@ public class NestedForm
public List<ChildForm> SubArray { set; get; }

public MustNotValidate NotAForm { set; get; }

public void Clear()
{
Email = String.Empty;
Child = new ChildForm();
SubArray = new List<ChildForm> { new ChildForm { }, new ChildForm { } };
NotAForm = new MustNotValidate();
}
}

public class ChildForm
Expand Down
14 changes: 13 additions & 1 deletion Demo/Pages/Index.razor
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
<h1>Form 2: Dependency Injection Validation</h1>

<EditForm Model="Form2">
<FluentValidator></FluentValidator>
<FluentValidator @ref="Form2FluentValidator"></FluentValidator>
<div class="form-group">
<label for="email">Email</label>
<InputText id="email" class="form-control" @bind-Value="Form2.Email"></InputText>
Expand Down Expand Up @@ -54,6 +54,9 @@
</button>
</div>
</EditForm>
<button class="btn btn-primary" @onclick="ClearForm2">
Clear Form
</button>

<h1>Submitting / Editing This Form Will Crash The App</h1>

Expand Down Expand Up @@ -85,4 +88,13 @@
};

CrashForm CrashForm = new CrashForm();

FluentValidator Form2FluentValidator;

void ClearForm2()
{
Form2.Clear();
Form2FluentValidator.ClearMessages();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ Version 5.0.0 BREAKING CHANGES:
- Package sub-dependency Microsoft.AspNetCore.Components.Web is changed to Microsoft.AspNetCore.Components.Forms to allow using the library in Xamarin Blazor project!
- Package is now compatible with both .NET 5.0 and .NET Core 3.1
</PackageReleaseNotes>
<PackageId>ClearableFluentValidation.Blazor</PackageId>
</PropertyGroup>

<ItemGroup>
Expand Down
35 changes: 26 additions & 9 deletions FluentValidation.Blazor/FluentValidator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,11 @@ public class FluentValidator : ComponentBase, IDisposable
[Parameter]
public Dictionary<Type, IValidator> ChildValidators { set; get; } = new Dictionary<Type, IValidator>();

/// <summary>
/// <see cref="ValidationMessageStore"/> that gathers all validation messages linked to <see cref="FluentValidator"/>.
/// </summary>
protected ValidationMessageStore MessageStore { get; set; }

/// <summary>
/// Attach to parent EditForm context enabling validation.
/// </summary>
Expand All @@ -57,6 +62,8 @@ protected override void OnInitialized()

this.ServiceScope = ServiceProvider.CreateScope();

MessageStore = new ValidationMessageStore(CurrentEditContext);

if (this.Validator == null)
{
this.SetFormValidator();
Expand All @@ -65,6 +72,16 @@ protected override void OnInitialized()
this.AddValidation();
}

/// <summary>
/// Clear all validation messages.
/// </summary>
public void ClearMessages()
{
MessageStore.Clear();
CurrentEditContext.MarkAsUnmodified();
CurrentEditContext.NotifyValidationStateChanged();
}

/// <summary>
/// Try setting the EditContext form model typed validator implementation from the DI.
/// </summary>
Expand Down Expand Up @@ -122,28 +139,27 @@ private IValidationContext CreateValidationContext(object model, IValidatorSelec
/// </summary>
private void AddValidation()
{
var messages = new ValidationMessageStore(CurrentEditContext);

// Perform object-level validation on request
CurrentEditContext.OnValidationRequested +=
(sender, eventArgs) => ValidateModel((EditContext)sender, messages);
(sender, eventArgs) => ValidateModel((EditContext)sender);

// Perform per-field validation on each field edit
CurrentEditContext.OnFieldChanged +=
(sender, eventArgs) => ValidateField(CurrentEditContext, messages, eventArgs.FieldIdentifier);
(sender, eventArgs) => ValidateField(CurrentEditContext, eventArgs.FieldIdentifier);
}

/// <summary>
/// Validate the whole form and trigger client UI update.
/// </summary>
/// <param name="editContext"></param>
/// <param name="messages"></param>
private async void ValidateModel(EditContext editContext, ValidationMessageStore messages)
private async void ValidateModel(EditContext editContext)
{
// <EditForm> should now be able to run async validations:
// https://github.com/dotnet/aspnetcore/issues/11914
var validationResults = await TryValidateModel(editContext);
messages.Clear();
MessageStore.Clear();

var graph = new ModelGraphCache(editContext.Model);
foreach (var error in validationResults.Errors)
Expand All @@ -153,7 +169,7 @@ private async void ValidateModel(EditContext editContext, ValidationMessageStore
if (propertyValue != null)
{
var fieldID = new FieldIdentifier(propertyValue, propertyName);
messages.Add(fieldID, error.ErrorMessage);
MessageStore.Add(fieldID, error.ErrorMessage);
}
}

Expand Down Expand Up @@ -238,7 +254,7 @@ private IValidator TryGetFieldValidator(EditContext editContext, in FieldIdentif
/// <param name="editContext"></param>
/// <param name="messages"></param>
/// <param name="fieldIdentifier"></param>
private async void ValidateField(EditContext editContext, ValidationMessageStore messages, FieldIdentifier fieldIdentifier)
private async void ValidateField(EditContext editContext, FieldIdentifier fieldIdentifier)
{
var fieldValidator = TryGetFieldValidator(editContext, fieldIdentifier);
if (fieldValidator == null)
Expand All @@ -248,11 +264,11 @@ private async void ValidateField(EditContext editContext, ValidationMessageStore
}

var validationResults = await TryValidateField(fieldValidator, editContext, fieldIdentifier);
messages.Clear(fieldIdentifier);
MessageStore.Clear(fieldIdentifier);

foreach (var error in validationResults.Errors)
{
messages.Add(fieldIdentifier, error.ErrorMessage);
MessageStore.Add(fieldIdentifier, error.ErrorMessage);
}

editContext.NotifyValidationStateChanged();
Expand All @@ -277,6 +293,7 @@ protected virtual void Dispose(bool disposing)
ServiceScope = null;
Validator = null;
ChildValidators = null;
MessageStore = null;

disposedValue = true;
}
Expand Down