首页 > 解决方案 > Blazor EditForm 验证仅在使用“验证器组件”时部分有效(在“业务逻辑验证”函数中)

问题描述

问题是我无法获得针对字段显示的验证消息。尽管错误消息确实出现在验证摘要中。

显示我希望看到的屏幕截图

这个问题也进一步仅限于复杂的模型属性。在这种情况下,使用“ValidateComplexType”、“ObjectGraphDataAnnotationsValidator”解决复杂模型属性验证限制的方法没有帮助。

下面我为演示该问题而创建的示例代码实际上是来自 Microsoft 文档网站 ( https://docs.microsoft.com/en-us/aspnet/core/blazor/forms-validation ) 的所有代码。

索引.razor

@page "/"
@using System.ComponentModel.DataAnnotations;
<h1>Starfleet Starship Database</h1>

<h2>New Ship Entry Form</h2>

<EditForm Model="@starship" OnValidSubmit="@HandleValidSubmit">
    <DataAnnotationsValidator />
    <CustomValidator @ref="customValidator" />
    <ValidationSummary />
    @*<ObjectGraphDataAnnotationsValidator />*@     @* does not work!*@
    <p>
        <label>
            Identifier:
            <InputText @bind-Value="starship.Identifier" disabled="@disabled" />
            <ValidationMessage For="@(() => starship.Identifier)" />
        </label>
    </p>
    <p>
        <label>
            Description (optional):
            <InputTextArea @bind-Value="starship.Description" disabled="@disabled" />
            <ValidationMessage For="@(() => starship.Description)" />
        </label>
    </p>
    <p>
        <label>
            Primary Classification:
            <InputSelect @bind-Value="starship.Classification" disabled="@disabled">
                <option value="">Select classification ...</option>
                <option value="Exploration">Exploration</option>
                <option value="Diplomacy">Diplomacy</option>
                <option value="Defense">Defense</option>
            </InputSelect>
            <ValidationMessage For="@(() => starship.Classification)" />
        </label>
    </p>
    <p>
        <label>
            Maximum Accommodation:
            <InputNumber @bind-Value="starship.MaximumAccommodation" disabled="@disabled" />
            <ValidationMessage For="@(() => starship.MaximumAccommodation)" />
        </label>
    </p>
    <p>
        <label>
            Engineering Approval:
            <InputCheckbox @bind-Value="starship.IsValidatedDesign" disabled="@disabled" />
            <ValidationMessage For="@(() => starship.IsValidatedDesign)" />
        </label>
    </p>
    <p>
        <label>
            Production Date:
            <InputDate @bind-Value="starship.ProductionDate" disabled="@disabled" />
            <ValidationMessage For="@(() => starship.ProductionDate)" />
        </label>
    </p>

    <p>
        <strong>Captain's Name</strong>
    </p>

    <p>
        <label>
            Firstname:
            <InputText @bind-Value="starship.CaptainsName.Firstname" disabled="@disabled" />
            <ValidationMessage For="@(() => starship.CaptainsName.Firstname)" />
        </label>
    </p>

    <p>
        <label>
            Lastname:
            <InputText @bind-Value="starship.CaptainsName.Lastname" disabled="@disabled" />
            <ValidationMessage For="@(() => starship.CaptainsName.Lastname)" />
        </label>
    </p>

    <button type="submit" disabled="@disabled">Submit</button>

    <p style="@messageStyles">
        @message
    </p>

    <p>
        <a href="http://www.startrek.com/">Star Trek</a>,
        &copy;1966-2019 CBS Studios, Inc. and
        <a href="https://www.paramount.com">Paramount Pictures</a>
    </p>
</EditForm>

@code {
    private bool disabled;
    private string message;
    private string messageStyles = "visibility:hidden";
    private CustomValidator customValidator;
    private Starship starship = new Starship() { ProductionDate = DateTime.UtcNow };


    protected override void OnInitialized()
    {
        base.OnInitialized();

        customValidator = new CustomValidator();

        starship = new Starship()
        {
            ProductionDate = DateTime.UtcNow,
            Identifier = "1",
            Classification = "Defense",
            MaximumAccommodation = 100,
            IsValidatedDesign = true,
            CaptainsName = new PersonsName { Firstname = "John", Lastname = "Kirk" }
        };
    }

    private async Task HandleValidSubmit(EditContext editContext)
    {
        bool isValid = editContext.Validate(); // Data Annotations validation

        if (isValid)
        {
            BusinessLogicValidation();
        }
    }

    private bool BusinessLogicValidation()
    {
        customValidator.ClearErrors();

        var errors = new Dictionary<string, List<string>>();

        if (starship.Classification == "Defense" &&
                string.IsNullOrEmpty(starship.Description))
        {
            errors.Add(nameof(starship.Description),
                new List<string>() { "For a 'Defense' ship classification, 'Description' is required." });
        }

        if (starship.CaptainsName.Firstname != "James")
        {
            //errors.Add(nameof(starship.CaptainsName.Firstname), new List<string>() { "Firstname must be James" });  // does not work (as expected)!
            errors.Add("CaptainsName.Firstname", new List<string>() { "Firstname must be James" });
        }


        if (errors.Count() > 0)
        {
            customValidator.DisplayErrors(errors);
            return true;
        }
        return false;
    }
}

星际飞船.cs

using System;
using System.ComponentModel.DataAnnotations;

namespace BlazorTest1.Client.Shared
{
    public class Starship
    {
        [Required]
        [StringLength(16, ErrorMessage = "Identifier too long (16 character limit).")]
        public string Identifier { get; set; }

        public string Description { get; set; }

        [Required]
        public string Classification { get; set; }

        [Range(1, 100000, ErrorMessage = "Accommodation invalid (1-100000).")]
        public int MaximumAccommodation { get; set; }

        [Required]
        [Range(typeof(bool), "true", "true", ErrorMessage = "This form disallows unapproved ships.")]
        public bool IsValidatedDesign { get; set; }

        [Required]
        public DateTime ProductionDate { get; set; }


        // [ValidateComplexType]      // does not work!
        public PersonsName CaptainsName { get; set; }
    }
}

人名.cs

namespace BlazorTest1.Client.Shared
{
    public class PersonsName
    {
        public string Firstname { get; set; }
        public string Lastname { get; set; }
    }
}

CustomValidator.cs

using System;
using System.Collections.Generic;
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Forms;

public class CustomValidator : ComponentBase
{
    private ValidationMessageStore messageStore;

    [CascadingParameter]
    private EditContext CurrentEditContext { get; set; }

    protected override void OnInitialized()
    {
        if (CurrentEditContext == null)
        {
            throw new InvalidOperationException(
                $"{nameof(CustomValidator)} requires a cascading " +
                $"parameter of type {nameof(EditContext)}. " +
                $"For example, you can use {nameof(CustomValidator)} " +
                $"inside an {nameof(EditForm)}.");
        }

        messageStore = new ValidationMessageStore(CurrentEditContext);

        CurrentEditContext.OnValidationRequested += (s, e) => 
            messageStore.Clear();
        CurrentEditContext.OnFieldChanged += (s, e) => 
            messageStore.Clear(e.FieldIdentifier);
    }

    public void DisplayErrors(Dictionary<string, List<string>> errors)
    {
        foreach (var err in errors)
        {
            messageStore.Add(CurrentEditContext.Field(err.Key), err.Value);
        }

        CurrentEditContext.NotifyValidationStateChanged();
    }

    public void ClearErrors()
    {
        messageStore.Clear();
        CurrentEditContext.NotifyValidationStateChanged();
    }
}

知道为什么我没有收到针对该字段的验证消息吗?

标签: c#validationrazorblazor

解决方案


推荐阅读