Skip to content

Instantly share code, notes, and snippets.

@bcheung4589
Last active April 6, 2024 00:03
Show Gist options
  • Save bcheung4589/be0f762c7eeaab3655b4eb9041be4f0b to your computer and use it in GitHub Desktop.
Save bcheung4589/be0f762c7eeaab3655b4eb9041be4f0b to your computer and use it in GitHub Desktop.
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Forms;
using System.Collections.Concurrent;
using System.Linq.Expressions;
using System.Reflection;
namespace Pfx.Web.ComponentsLibrary.Forms;
/// <summary>
/// Displays a list of validation messages for a specified field within a cascaded <see cref="EditContext"/>.
/// </summary>
public class PfxValidationMessage<TValue> : ValidationMessage<TValue>
{
private static readonly ConcurrentDictionary<(Type ModelType, MemberInfo Member), Func<object, object>> _fieldAccessors = new();
private EditContext? _previousEditContext;
private Expression<Func<TValue>>? _previousFieldAccessor;
private readonly EventHandler<ValidationStateChangedEventArgs>? _validationStateChangedHandler;
private FieldIdentifier _fieldIdentifier;
[CascadingParameter] EditContext NestedEditContext { get; set; } = default!;
/// <summary>`
/// Constructs an instance of <see cref="PfxValidationMessage{TValue}"/>.
/// </summary>
public PfxValidationMessage()
{
_validationStateChangedHandler = (sender, eventArgs) => StateHasChanged();
}
/// <inheritdoc />
protected override void OnParametersSet()
{
if (NestedEditContext == null)
{
throw new InvalidOperationException($"{GetType()} requires a cascading parameter of type {nameof(EditContext)}. For example, you can use {GetType()} inside an {nameof(EditForm)}.");
}
if (For == null) // Not possible except if you manually specify T
{
throw new InvalidOperationException($"{GetType()} requires a value for the {nameof(For)} parameter.");
}
else if (For != _previousFieldAccessor)
{
_fieldIdentifier = FieldIdentifier.Create(For);
_previousFieldAccessor = For;
//# fix to validate nested properties.
NestedEditContext.NotifyFieldChanged(_fieldIdentifier);
}
if (NestedEditContext != _previousEditContext)
{
DetachValidationStateChangedListener();
NestedEditContext.OnValidationStateChanged += _validationStateChangedHandler;
_previousEditContext = NestedEditContext;
}
}
/// <inheritdoc />
protected override void BuildRenderTree(RenderTreeBuilder builder)
{
foreach (var message in NestedEditContext.GetValidationMessages(_fieldIdentifier))
{
builder.OpenElement(0, "div");
builder.AddAttribute(1, "class", "validation-message");
builder.AddMultipleAttributes(2, AdditionalAttributes);
builder.AddContent(3, message);
builder.CloseElement();
}
}
private void DetachValidationStateChangedListener()
{
if (_previousEditContext != null)
{
_previousEditContext.OnValidationStateChanged -= _validationStateChangedHandler;
}
}
}
@bcheung4589
Copy link
Author

bcheung4589 commented Apr 5, 2024

The addition of L49 ensures the EditContext triggers the validation on the specified (nested) field.

This trigger will ensure "NestedEditContext.GetValidationMessages(field)" has messages in case of invalid input on: L63

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment