使用子组件时,Blazor EditForm 验证不起作用

问题描述 投票:0回答:3

我有一个名为 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 在我的子组件中不起作用。知道为什么吗?

validation blazor
3个回答
15
投票

我知道我有点晚了,但这是我的答案:)

所以现在有更好的解决方案。

TL:懒人的灾难恢复解决方案

请注意 - 这是 实验性的 ,但软件包已经在候选版本中,所以我想不用担心。

使用 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();
}

来自 MS Docs 的片段

链接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; }
}

1
投票

如果您想使用

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();

0
投票

我遇到了完全相同的问题。我的代码与你的非常相似。我的子组件确实进行了验证,但未显示验证错误消息。

我确实使用了这个扩展方法:

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);
        }

    }
}
© www.soinside.com 2019 - 2024. All rights reserved.