我有一个名为 EditOffice 的 Blazor 组件。看起来如下:
<EditForm Model="@Office" OnValidSubmit="@HandleValidSubmit">
<DataAnnotationsValidator />
<ValidationSummary />
<InputTextRow Label="Name" @bind-Value="@Office.Name" Placeholder="Enter name" />
<InputTextRow Label="ABN" @bind-Value="@Office.ABN" Placeholder="Enter ABN" />
...
<button type="submit" class="btn btn-primary edit-btn">Save office</button>
</EditForm>
我创建了名为 InputTextRow 的子组件,试图Tidy我的代码。它们看起来如下:
<div class="form-group row">
<label for="@Id" class="col-sm-3">@Label: </label>
<InputText id="@Id" @oninput="OnValueChanged" @bind-Value="@Value" class="form-control col-sm-8" placeholder="@Placeholder"></InputText>
<ValidationMessage class="offset-sm-3 col-sm-8" For="@(() => Value)" />
</div>
@code {
public string Id => Label.ToLower().Replace(" ", "");
[Parameter]
public string Label { get; set; }
[Parameter]
public string Value { get; set; }
[Parameter]
public string Placeholder { get; set; }
[Parameter] public EventCallback<string> ValueChanged { get; set; }
Task OnValueChanged(ChangeEventArgs e)
{
Value = e.Value.ToString();
return ValueChanged.InvokeAsync(Value);
}
}
ValidationMessage 在我的子组件中不起作用。知道为什么吗?
我知道我有点晚了,但这是我的答案:)
所以现在有更好的解决方案。
请注意 - 这是 实验性的 ,但软件包已经在候选版本中,所以我想不用担心。
使用 Microsoft.AspNetCore.Components.DataAnnotations.Validation 包和
<ObjectGraphDataAnnotationsValidator />
而不是 <DataAnnotationsValidator />
并使用这个东西:
using System.ComponentModel.DataAnnotations;
public class YourComplexModel
{
// other properties
[ValidateComplexType] // <--life saver
public ChildModel ChildModel { get; set; } = new ChildModel();
}
链接Microsoft 文档:
Blazor 支持使用内置 DataAnnotationsValidator 的数据注释来验证表单输入。但是,DataAnnotationsValidator 仅验证绑定到表单的模型的顶级属性,这些属性不是集合或复杂类型属性。
要验证绑定模型的整个对象图,包括集合类型和复杂类型属性,请使用实验性 Microsoft.AspNetCore.Components.DataAnnotations.Validation 包提供的 ObjectGraphDataAnnotationsValidator:
<EditForm Model="@model" OnValidSubmit="@HandleValidSubmit">
<ObjectGraphDataAnnotationsValidator />
...
</EditForm>
使用 [ValidateComplexType] 注释模型属性。在以下模型类中,ShipDescription 类包含附加数据注释,用于验证模型何时绑定到表单:
Starship.cs:
using System;
using System.ComponentModel.DataAnnotations;
public class Starship
{
...
[ValidateComplexType]
public ShipDescription ShipDescription { get; set; } =
new ShipDescription();
...
}
ShipDescription.cs:
using System;
using System.ComponentModel.DataAnnotations;
public class ShipDescription
{
[Required]
[StringLength(40, ErrorMessage = "Description too long (40 char).")]
public string ShortDescription { get; set; }
[Required]
[StringLength(240, ErrorMessage = "Description too long (240 char).")]
public string LongDescription { get; set; }
}
如果您想使用
FluentValidations
,您只需像这样更新您的 InputTextRow.razor
:
<div class="form-group row">
<label for="@Id" class="col-sm-3">@Label: </label>
<InputText id="@Id" @oninput="OnValueChanged" @bind-Value="@Value" class="form-control col-sm-8" placeholder="@Placeholder"></InputText>
<ValidationMessage class="offset-sm-3 col-sm-8" For="ValueExpression" />
</div>
@code {
[Parameter] public string Label { get; set; }
[Parameter] public string Value { get; set; }
[Parameter] public EventCallback<string> ValueChanged { get; set; }
[Parameter] public Expression<Func<string>> ValueExpression { get; set; } = default!;
[Parameter] public string Placeholder { get; set; }
[CascadingParameter] public EditContext? EditContext { get; set; }
public string Id => Label.ToLower().Replace(" ", "");
Task OnValueChanged(ChangeEventArgs e)
{
Value = e.Value.ToString();
return ValueChanged.InvokeAsync(Value);
}
}
因此进行了 3 处更改:
ValueExpression
参数。当您将属性绑定到该参数时,该参数将被填充。您需要它作为 ValidationMessage
EditContext
添加为 CascadingParameter
。当您的组件位于 EditForm
ValueExpression
现在作为 ValidationMessage's
For
argument如果您想要条件检查是否有可用的 ValidationMessage,您可以这样检查:
private bool _hasValidationMessages => EditContext is null || ValueExpression is null
? false
: EditContext.GetValidationMessages(FieldIdentifier.Create(ValueExpression)).Any();
我遇到了完全相同的问题。我的代码与你的非常相似。我的子组件确实进行了验证,但未显示验证错误消息。
我确实使用了这个扩展方法:
using System;
using Microsoft.AspNetCore.Components.Forms;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
namespace TimeRecording.Extensions
{
public static class EditContextExtensions
{
static PropertyInfo IsModifiedProperty;
static MethodInfo GetFieldStateMethod;
/// <summary>
/// Validates an entire object tree
/// </summary>
/// <param name="editContext">The EditContext to validate the Model of</param>
/// <returns>True if valid, otherwise false</returns>
public static bool ValidateObjectTree(this EditContext editContext)
{
var validatedObjects = new HashSet<object>();
ValidateObject(editContext, editContext.Model, validatedObjects);
editContext.NotifyValidationStateChanged();
return !editContext.GetValidationMessages().Any();
}
public static void ValidateProperty(this EditContext editContext, FieldIdentifier fieldIdentifier)
{
if (fieldIdentifier.Model == null)
return;
var propertyInfo = fieldIdentifier.Model.GetType().GetProperty(
fieldIdentifier.FieldName,
BindingFlags.Public | BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Static);
var validatedObjects = new HashSet<object>();
ValidateProperty(editContext, fieldIdentifier.Model, propertyInfo, validatedObjects);
}
private static void ValidateObject(
EditContext editContext,
object instance,
HashSet<object> validatedObjects)
{
if (instance == null)
return;
if (validatedObjects.Contains(instance))
return;
if (instance is IEnumerable && !(instance is string))
{
foreach (object value in (IEnumerable)instance)
ValidateObject(editContext, value, validatedObjects);
return;
}
if (instance.GetType().Assembly == typeof(string).Assembly)
return;
validatedObjects.Add(instance);
var properties = instance.GetType().GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
foreach (PropertyInfo property in properties)
ValidateProperty(editContext, instance, property, validatedObjects);
}
private static void ValidateProperty(
EditContext editContext,
object instance,
PropertyInfo property,
HashSet<object> validatedObjects)
{
NotifyPropertyChanged(editContext, instance, property.Name);
object value = property.GetValue(instance);
ValidateObject(editContext, value, validatedObjects);
}
private static void NotifyPropertyChanged(
EditContext editContext,
object instance,
string propertyName)
{
if (GetFieldStateMethod == null)
{
GetFieldStateMethod = editContext.GetType().GetMethod(
"GetFieldState",
BindingFlags.NonPublic | BindingFlags.Instance);
}
var fieldIdentifier = new FieldIdentifier(instance, propertyName);
object fieldState = GetFieldStateMethod.Invoke(editContext, new object[] { fieldIdentifier, true });
if (IsModifiedProperty == null)
{
IsModifiedProperty = fieldState.GetType().GetProperty(
"IsModified",
BindingFlags.Public | BindingFlags.Instance);
}
object originalIsModified = IsModifiedProperty.GetValue(fieldState);
editContext.NotifyFieldChanged(fieldIdentifier);
IsModifiedProperty.SetValue(fieldState, originalIsModified);
}
}
}