(Mud)Blazor - 使用Fluent Validation验证嵌套组件

3

我正在学习Blazor(使用MudBlazor)和FluentValidation。主要参考MudBlazor文档中的模式和实践。我有一个顶级表单(主表单),其中包含一些基本输入字段和一些由API驱动的选择列表。我将选择列表转换为组件,因为我将在应用程序的其他地方重复使用它们。

我已经成功地将FluentValidation添加到了主表单中,并在保存时看到字段变为红色并显示错误消息。但是,我无法弄清楚如何验证并在嵌套/子组件中显示控件上的错误。我知道这是由于我的经验不足,但我在互联网上没有找到太多关于这种特定情况的信息。

接下来是代码。以下是我的问题示例的一小部分代码。可以在Try MudBlazor中找到一个工作示例。

编辑:如果这是一种不好的组件模式,那我没关系。请告诉我,我会放弃这种方法。

MainForm.razor

<MudForm Model="@formData" @ref="@form" Validation="@(modelValidator.ValidateValue)">
    <MudGrid>
        <MudItem xs=12>
            <MudTextField @bind-Value="formData.LastName" Label="Last Name" For="(() => formData.LastName)"
                            Variant="Variant.Text" MaxLength="50"></MudTextField>
        </MudItem>
        <MudItem xs=12>
            <MudTextField @bind-Value="formData.FirstName" Label="First Name" For="(() => formData.FirstName)"
                            Variant="Variant.Text" MaxLength="50"></MudTextField>
        </MudItem>
        <MudItem xs=12>
            <RaceSelector @bind-SelectedRaceId="@selectedRaceId" />
        </MudItem>
        <MudItem xs=12>
            <span>The Selected Race ID is: @selectedRaceId</span>
        </MudItem>
        <MudItem xs=12>
            <MudButton Variant="Variant.Filled" Color="Color.Primary" Class="ml-auto" OnClick="async () => await Submit() ">Save</MudButton>
        </MudItem>
    </MudGrid>
</MudForm>

@code {
    private Model formData = new();
    private string selectedRaceId = string.Empty;
    private ModelValidator modelValidator = new();
    private MudForm form;

    private async Task Submit()
    {
        await form.Validate();

        if (form.IsValid)
        {
            // Post to API
        }
    }
}

RaceSelector.razor

<MudSelect @bind-Value="SelectedRaceId" Placeholder="" T="string" 
            Label="Race" Variant="Variant.Outlined">
    @foreach (var race in RaceList)
    {
        <MudSelectItem T="string" Value="@race.Id.ToString()">@race.Name</MudSelectItem>
    }
</MudSelect>


@code {
    private List<Race>? RaceList { get; set; }
    private string selectedRaceId;

    [Parameter]
    public string SelectedRaceId 
    {
        get
        {
            return selectedRaceId;
        }
        set
        { 
            // Wire-up two way binding
            if (selectedRaceId != value)
            {
                selectedRaceId = value;

                if (SelectedRaceIdChanged.HasDelegate)
                {
                    SelectedRaceIdChanged.InvokeAsync(value);
                }
            }
        }
    }

    [Parameter]
    public EventCallback<string> SelectedRaceIdChanged { get; set; }
    
    protected override async Task OnInitializedAsync()
    {
        // Pretend this is a call to the API
        RaceList = new List<Race>
        {
            new Race(1, "American Ind/Alaskan"),
            new Race(2, "Asian or Pacific Isl"),
            new Race(3, "Black, not Hispanic"),
            new Race(4, "Hispanic"),
            new Race(5, "White, not Hispanic"),
            new Race(6, "Other"),
            new Race(7, "Multi-Racial"),
            new Race(8, "Unknown")
        };
    }   
}

Model.cs 和 Race.cs

public class Model
{
    public string FirstName {get; set;}
    public string LastName {get; set;}
    public string RaceId {get; set;}
}

public class Race
{
    public Race() {}

    public Race(int id, string name)
    {
        Id = id;
        Name = name;            
    }

    public int Id {get; set;}
    public string Name {get; set;}
}

ModelValidator.cs

using FluentValidation;

public class ModelValidator : AbstractValidator<Model>
{
    public ModelValidator()
    {
        RuleFor(x => x.LastName)
            .NotEmpty();

        RuleFor(x => x.FirstName)
            .NotEmpty();

        RuleFor(x => x.RaceId)
            .NotEmpty();              
    }

    public Func<object, string, Task<IEnumerable<string>>> ValidateValue => async (model, propertyName) =>
    {
        var result = await ValidateAsync(ValidationContext<Model>.CreateWithOptions((Model)model, x => x.IncludeProperties(propertyName)));
        if (result.IsValid)
            return Array.Empty<string>();
        return result.Errors.Select(e => e.ErrorMessage);
    };
}

你找到解决办法了吗?我也遇到了同样的问题,因为无法验证嵌套组件,所以基本上是重复了很多标记。 - chanban
我找到了一个解决方法,但我不喜欢它。如果我将formData模型作为参数传递到组件中,我就能够触发验证。但是,由于我需要与其他模型一起使用它,我不想让我的组件与该模型紧密耦合。 - Allen
你好,这件事还没有任何消息吗?我也遇到了同样的问题,并且像Allen一样想出了同样的解决方法,但我并不是很喜欢它 :( - Angevil
2个回答

2
这可能有点晚了,但也许还有其他人会寻找这个。你需要做的是将 For 参数转发到你的组件中。像这样:

RaceSelector.razor,需要进行两个更改:

  1. 在代码部分添加
[Parameter] public Expression<Func<T>> For<T> { get; set; }
  • MudSelector标签内部添加
  • For="@For"
    

    MainForm.razor,有一个更改:

    现在,在RaceSelector标签内部,您可以使用

    For="(() => formData.RaceId)""
    

    1
    可以确认这个有效,不错。 - fuzzy_logic

    0
    在RaceSelector.razor中:
    1- 添加新参数:
        [Parameter]
        public Expression<Func<int>> ForSelectedRaceId  { get; set; }
    

    2- 给 MudSelect 添加 For 属性,并将其设置为 ForSelectedRaceId 参数。
    <MudSelect @bind-Value="SelectedRaceId" Placeholder="" T="string" 
                Label="Race" Variant="Variant.Outlined"
                For="ForSelectedRaceId">
        @foreach (var race in RaceList)
        {
            <MudSelectItem T="string" Value="@race.Id.ToString()">@race.Name</MudSelectItem>
        }
    </MudSelect>
    

    在您的父组件MainForm.razor中,将ForSelectedRaceId参数值设置为ForSelectedRaceId="(() => formData.RaceId)":
     <RaceSelector @bind-SelectedRaceId="@selectedRaceId" ForSelectedRaceId="(() => formData.RaceId)"/>
    

    网页内容由stack overflow 提供, 点击上面的
    可以查看英文原文,
    原文链接