|
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.Linq.Expressions;
using Microsoft.AspNetCore.Components.Rendering;
namespace Microsoft.AspNetCore.Components.Forms;
/// <summary>
/// Displays a list of validation messages for a specified field within a cascaded <see cref="EditContext"/>.
/// </summary>
public class ValidationMessage<TValue> : ComponentBase, IDisposable
{
private EditContext? _previousEditContext;
private Expression<Func<TValue>>? _previousFieldAccessor;
private readonly EventHandler<ValidationStateChangedEventArgs>? _validationStateChangedHandler;
private FieldIdentifier _fieldIdentifier;
/// <summary>
/// Gets or sets a collection of additional attributes that will be applied to the created <c>div</c> element.
/// </summary>
[Parameter(CaptureUnmatchedValues = true)] public IReadOnlyDictionary<string, object>? AdditionalAttributes { get; set; }
[CascadingParameter] EditContext CurrentEditContext { get; set; } = default!;
/// <summary>
/// Specifies the field for which validation messages should be displayed.
/// </summary>
[Parameter] public Expression<Func<TValue>>? For { get; set; }
/// <summary>`
/// Constructs an instance of <see cref="ValidationMessage{TValue}"/>.
/// </summary>
public ValidationMessage()
{
_validationStateChangedHandler = (sender, eventArgs) => StateHasChanged();
}
/// <inheritdoc />
protected override void OnParametersSet()
{
if (CurrentEditContext == 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;
}
if (CurrentEditContext != _previousEditContext)
{
DetachValidationStateChangedListener();
CurrentEditContext.OnValidationStateChanged += _validationStateChangedHandler;
_previousEditContext = CurrentEditContext;
}
}
/// <inheritdoc />
protected override void BuildRenderTree(RenderTreeBuilder builder)
{
foreach (var message in CurrentEditContext.GetValidationMessages(_fieldIdentifier))
{
builder.OpenElement(0, "div");
builder.AddAttribute(1, "class", "validation-message");
builder.AddMultipleAttributes(2, AdditionalAttributes);
builder.AddContent(3, message);
builder.CloseElement();
}
}
/// <summary>
/// Called to dispose this instance.
/// </summary>
/// <param name="disposing"><see langword="true"/> if called within <see cref="IDisposable.Dispose"/>.</param>
protected virtual void Dispose(bool disposing)
{
}
void IDisposable.Dispose()
{
DetachValidationStateChangedListener();
Dispose(disposing: true);
}
private void DetachValidationStateChangedListener()
{
if (_previousEditContext != null)
{
_previousEditContext.OnValidationStateChanged -= _validationStateChangedHandler;
}
}
}
|