.NET MAUI Blazor 应用程序中的问题,MudBlazor 和 FluentValidation 在 MudForm 中。

huangapple go评论97阅读模式
英文:

Issues with .NET MAUI Blazor app, MudBlazor, and FluentValidation in a MudForm

问题

我目前正在开发我的第一个.NET MAUI Blazor应用程序,并使用MudBlazor用于UI组件。我正在尝试使用<MudForm>创建一个表单,该表单使用NewReservationViewModel,并希望使用FluentValidation进行验证。

以下是我的表单代码:

<MudForm Model="@ViewModel" Validation="???">
    <MudSelect Label="Building" @bind-Value="@ViewModel.BuildingId" T="int?" Disabled="@IsFloorListDisabled" Required="true" ValueChanged="OnBuildingSelectionChanged">
        <MudSelectItem T="int?" Value="null">Select Building</MudSelectItem>
        @if (ViewModel.Buildings != null && ViewModel.Buildings.Count > 0)
        {
            @foreach (var building in ViewModel.Buildings)
            {
                <MudSelectItem T="int?" Value="@building.Id">@building.Name</MudSelectItem>
            }
        }
    </MudSelect>

    <MudSelect Label="Floor" @bind-Value="@ViewModel.FloorId" T="int?" Disabled="@IsFloorListDisabled" Required="true">
        <MudSelectItem T="int?" Value="null">Select Floor</MudSelectItem>
        @if (ViewModel.Floors != null && ViewModel.Floors.Count > 0)
        {
            @foreach (var floor in ViewModel.Floors)
            {
                <MudSelectItem T="int?" Value="@floor.Id">@floor.Name</MudSelectItem>
            }
        }
    </MudSelect>

    <MudDatePicker Label="Start Date" @bind-Date="@ViewModel.Start" Required="true"></MudDatePicker>

    <MudDatePicker Label="Finish Date" @bind-Date="@ViewModel.Finish" Required="true"></MudDatePicker>

    <MudButton Color="Color.Primary" Variant="Variant.Filled" Type="ButtonType.Submit">Search</MudButton>
</MudForm>

这是NewReservationViewModel

public class NewReservationViewModel
{
    public int Id { get; set; }

    public DateTime? Start { get; set; }

    public DateTime? Finish { get; set; }

    public int? BuildingId { get; set; }
    public List<BuildingDto> Buildings { get; set; }

    public int? FloorId { get; set; }
    public List<FloorDto> Floors { get; set; }

    public string AreaTypes { get; set; }
}

我还为NewReservationViewModel实现了验证器:

public class NewReservationViewModelValidation : AbstractValidator<NewReservationViewModel>
{
    public NewReservationViewModelValidation()
    {
        RuleFor(model => model.Start).NotEmpty().WithMessage("bla bla bla");
        RuleFor(model => model.Finish).NotEmpty().WithMessage("bla bla bla")
            .GreaterThan(model => model.Start).WithMessage("bla bla bla");
        RuleFor(model => model.BuildingId).NotEmpty().WithMessage("bla bla bla");
        RuleFor(model => model.FloorId).NotEmpty().WithMessage("bla bla bla");
    }

    public Func<object, string, Task<IEnumerable<string>>> ValidateValue => async (model, propertyName) =>
    {
        try
        {
            var result = await ValidateAsync(ValidationContext<NewReservationViewModel>
                .CreateWithOptions((NewReservationViewModel)model, x => x.IncludeProperties(propertyName)));
            if (result.IsValid)
                return Array.Empty<string>();
            return result.Errors.Select(e => e.ErrorMessage);
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
            throw;
        }
    };
}

问题:

  1. 为什么我无法同时使用ValueChanged="OnBuildingSelectionChanged"@bind-Value="@ViewModel.BuildingId"?我需要知道何时选择了一个值,以调用OnBuildingSelectionChanged方法,在选择建筑物时填充楼层列表(级联下拉列表)。
  2. 如果存在任何表单错误,如何显示这些错误?

任何关于这些问题的帮助或见解将不胜感激。谢谢!

英文:

Description:

I'm currently working on my first .NET MAUI Blazor app and using MudBlazor for UI components. I'm trying to create a form using &lt;MudForm&gt; with the NewReservationViewModel, and I want to handle validation using FluentValidation.

Here's my form code:

&lt;MudForm Model=&quot;@ViewModel&quot; Validation=&quot;???&quot;&gt;

    &lt;MudSelect Label=&quot;Building&quot; @bind-Value=&quot;@ViewModel.BuildingId&quot; T=&quot;int?&quot; Disabled=&quot;@IsFloorListDisabled&quot; Required=&quot;true&quot; ValueChanged=&quot;OnBuildingSelectionChanged&quot;&gt;
        &lt;MudSelectItem T=&quot;int?&quot; Value=&quot;null&quot;&gt;Select Building&lt;/MudSelectItem&gt;
        @if (ViewModel.Buildings != null &amp;&amp; ViewModel.Buildings.Count &gt; 0)
        {
            @foreach (var building in ViewModel.Buildings)
            {
                &lt;MudSelectItem T=&quot;int?&quot; Value=&quot;@building.Id&quot;&gt;@building.Name&lt;/MudSelectItem&gt;
            }
        }
    &lt;/MudSelect&gt;

    &lt;MudSelect Label=&quot;Floor&quot; @bind-Value=&quot;@ViewModel.FloorId&quot; T=&quot;int?&quot; Disabled=&quot;@IsFloorListDisabled&quot; Required=&quot;true&quot;&gt;
        &lt;MudSelectItem T=&quot;int?&quot; Value=&quot;null&quot;&gt;Select Floor&lt;/MudSelectItem&gt;
        @if (ViewModel.Floors != null &amp;&amp; ViewModel.Floors.Count &gt; 0)
        {
            @foreach (var floor in ViewModel.Floors)
            {
                &lt;MudSelectItem T=&quot;int?&quot; Value=&quot;@floor.Id&quot;&gt;@floor.Name&lt;/MudSelectItem&gt;
            }
        }
    &lt;/MudSelect&gt;


    &lt;MudDatePicker Label=&quot;Start Date&quot; @bind-Date=&quot;@ViewModel.Start&quot; Required=&quot;true&quot;&gt;&lt;/MudDatePicker&gt;

    &lt;MudDatePicker Label=&quot;Finish Date&quot; @bind-Date=&quot;@ViewModel.Finish&quot; Required=&quot;true&quot;&gt;&lt;/MudDatePicker&gt;

    &lt;MudButton Color=&quot;Color.Primary&quot; Variant=&quot;Variant.Filled&quot; Type=&quot;ButtonType.Submit&quot;&gt;Search&lt;/MudButton&gt;
&lt;/MudForm&gt;

And here's the NewReservationViewModel:

public class NewReservationViewModel
{
    public int Id { get; set; }

    public DateTime? Start { get; set; }

    public DateTime? Finish { get; set; }

    public int? BuildingId { get; set; }
    public List&lt;BuildingDto&gt; Buildings { get; set; }

    public int? FloorId { get; set; }
    public List&lt;FloorDto&gt; Floors { get; set; }

    public string AreaTypes { get; set; }
}

I've also implemented a validator for the NewReservationViewModel:

public class NewReservationViewModelValidation : AbstractValidator&lt;NewReservationViewModel&gt;
{
    public NewReservationViewModelValidation()
    {
        RuleFor(model =&gt; model.Start).NotEmpty().WithMessage(&quot;bla bla bla&quot;);
        RuleFor(model =&gt; model.Finish).NotEmpty().WithMessage(&quot;bla bla bla&quot;)
            .GreaterThan(model =&gt; model.Start).WithMessage(&quot;bla bla bla&quot;);
        RuleFor(model =&gt; model.BuildingId).NotEmpty().WithMessage(&quot;bla bla bla&quot;);
        RuleFor(model =&gt; model.FloorId).NotEmpty().WithMessage(&quot;bla bla bla&quot;);
    }

    public Func&lt;object, string, Task&lt;IEnumerable&lt;string&gt;&gt;&gt; ValidateValue =&gt; async (model, propertyName) =&gt;
    {
        try
        {
            var result = await ValidateAsync(ValidationContext&lt;NewReservationViewModel&gt;
                .CreateWithOptions((NewReservationViewModel)model, x =&gt; x.IncludeProperties(propertyName)));
            if (result.IsValid)
                return Array.Empty&lt;string&gt;();
            return result.Errors.Select(e =&gt; e.ErrorMessage);
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
            throw;
        }
    };
}

Questions:

Why am I unable to use ValueChanged=&quot;OnBuildingSelectionChanged&quot; together with @bind-Value=&quot;@ViewModel.BuildingId&quot;? I need know when a value is selected, to call the OnBuildingSelectionChanged method to populate the list of floors when a building is selected (Cascading drop down lists).

How can I display the form errors if there are any?

Any help or insights into these issues would be greatly appreciated. Thank you!

答案1

得分: 2

>为什么我无法同时使用ValueChanged="OnBuildingSelectionChanged"@bind-Value="@ViewModel.BuildingId"

@bind-Value实际上是使用ValueValueChanged的简写方式。MS Docs

<SomeComponent Value="_myValue" ValueChanged="HandleValueChanged"/>
<SomeComponent @bind-Value="_myValue"/>

当您使用@bind-Value时,它会在触发ValueChangedEventCallback时处理_myValue字段的设置。而当您同时使用ValueValueChanged时,您需要在处理程序方法中手动处理_myValue字段的设置,即HandleValueChanged

这就是为什么您不能同时使用@bind-ValueValueChanged的原因。因此,如果您想在Value属性更改时添加自定义逻辑,那么应该使用ValueValueChanged


>如何在出现错误时显示表单错误?

您可以使用MudForm中的Errors属性。

<MudForm @ref="form" @bind-Errors="@errors" @bind-IsValid="@success">
    //...
</MudForm>
@code{
    bool success;
    string[] errors = { };
}

在您特定的代码中(不确定是否是有意的),您忽略了Validation属性的赋值,每个要验证的属性都需要一个For属性。

例如:

<MudForm Model="@ViewModel" Validation="@(viewModelValidator.ValidateValue)">
    <MudSelect For="(() => ViewModel.BuildingId)" @bind-Value="@ViewModel.BuildingId" Label="Building" T="int?" Disabled="@IsFloorListDisabled" Required="true" ValueChanged="OnBuildingSelectionChanged">
        <MudSelectItem T="int?" Value="null">Select Building</MudSelectItem>
        @if (ViewModel.Buildings != null && ViewModel.Buildings.Count > 0)
        {
            @foreach (var building in ViewModel.Buildings)
            {
                <MudSelectItem T="int?" Value="@building.Id">@building.Name</MudSelectItem>
            }
        }
    </MudSelect>
    //...
</MudForm>
@code{
    NewReservationViewModelValidation viewModelValidator = new();
}
英文:

>Why am I unable to use ValueChanged="OnBuildingSelectionChanged" together with @bind-Value="@ViewModel.BuildingId"?

@bind-Value is kind of the short hand of using Value and ValueChanged. MS Docs.

&lt;SomeComponent Value=&quot;_myValue&quot; ValueChanged=&quot;HandleValueChanged&quot;/&gt;
&lt;SomeComponent @bind-Value=&quot;_myValue&quot;/&gt;

When you use @bind-Value it handles the setting of the _myValue field when the ValueChanged EventCallback is fired. Whereas, when you use Value & ValueChanged together, you need to handle the setting of the _myValue field in the handler method i.e. HandleValueChanged.

That's why you can't use both @bind-Value and ValueChanged at the same time. So if you want to add your own custom logic when the Value property changes then you should use Value & ValueChanged


>How can I display the form errors if there are any?

You can use the Errors property in MudForm.

&lt;MudForm @ref=&quot;form&quot; @bind-Errors=&quot;@errors&quot; @bind-IsValid=&quot;@success&quot;&gt;
    //...
&lt;/MudForm&gt;
@code{
    bool success;
    string[] errors = { };
}

In your specific code (unsure if intentional) you're missing assignment for the Validation property and each property you're validating needs to have a For property.

e.g.

&lt;MudForm Model=&quot;@ViewModel&quot; Validation=&quot;@(viewModelValidator.ValidateValue&quot;&gt; 
    &lt;MudSelect For=&quot;@(() =&gt; ViewModel.BuildingId)&quot; @bind-Value=&quot;@ViewModel.BuildingId&quot; Label=&quot;Building&quot; T=&quot;int?&quot; Disabled=&quot;@IsFloorListDisabled&quot; Required=&quot;true&quot; ValueChanged=&quot;OnBuildingSelectionChanged&quot;&gt;
        &lt;MudSelectItem T=&quot;int?&quot; Value=&quot;null&quot;&gt;Select Building&lt;/MudSelectItem&gt;
        @if (ViewModel.Buildings != null &amp;&amp; ViewModel.Buildings.Count &gt; 0)
        {
            @foreach (var building in ViewModel.Buildings)
            {
                &lt;MudSelectItem T=&quot;int?&quot; Value=&quot;@building.Id&quot;&gt;@building.Name&lt;/MudSelectItem&gt;
            }
        }
    &lt;/MudSelect&gt;
    //...
&lt;/MudForm&gt;
@code{
NewReservationViewModelValidation viewModelValidator= new();

}

huangapple
  • 本文由 发表于 2023年7月23日 20:33:58
  • 转载请务必保留本文链接:https://go.coder-hub.com/76748260.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定