You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多
文档控制台
免费开始使用

Blazor EditForm自动填充字段仍显示必填验证消息求助

Blazor表单自动填充字段仍触发必填验证的解决方案

问题背景

熟悉ASP.NET和C#,刚接触Blazor与Entity Framework,开发了一个Smock checkout表单:

  • 用户输入SmockId,点击搜索按钮后系统自动从数据库填充FName和LName字段,这两个字段设为禁用状态
  • 用户选择Service下拉选项后提交表单
  • 为模型添加数据注解验证后,即使FName和LName已被正确自动填充,仍会显示“Required”必填验证消息

问题原因

Blazor的InputText等表单组件默认仅监听用户手动输入事件来更新验证状态,直接通过代码修改模型属性(自动填充)不会触发组件的验证更新逻辑,导致验证组件依然认为字段未被填充。

解决方案

方法1:手动触发字段验证状态更新

在填充FName和LName后,通过EditContext通知表单重新验证这两个字段。

修改步骤:

  1. EditForm添加引用,用于获取EditContext
<EditForm Model="@addSmockModel" OnValidSubmit="AddSmock" @ref="editForm">
    <DataAnnotationsValidator />
    <!-- 原表单内容 -->
</EditForm>
  1. 在@code块中声明EditContext变量:
private EditContext? editForm;
  1. 修改FindSmock方法,填充字段后触发验证更新:
private async Task FindSmock()
{
    var smockId = addSmockModel.SmockId;

    if (smockId > 0)
    {
        // 改用FirstOrDefault获取单条记录,更符合业务逻辑
        var userRecord = await dbContext.Users.FirstOrDefaultAsync(x => x.SmockId == smockId);
        
        if (userRecord != null)
        {
            addSmockModel.FName = userRecord.FName;
            addSmockModel.LName = userRecord.LName;
            addSmockModel.SmockId = userRecord.SmockId;

            // 通知表单更新FName和LName的验证状态
            editForm?.NotifyFieldChanged(FieldIdentifier.Create(() => addSmockModel.FName));
            editForm?.NotifyFieldChanged(FieldIdentifier.Create(() => addSmockModel.LName));
        }
    }   
}

方法2:优化模型验证逻辑(可选)

如果FName和LName仅通过SmockId关联获取,可考虑在业务层验证SmockId对应的用户存在性,而非依赖前端表单验证。但方法1更直接适配当前需求。

修改后的完整表单代码示例

<EditForm Model="@addSmockModel" OnValidSubmit="AddSmock" @ref="editForm">
    <DataAnnotationsValidator />
    <div class="container-fluid">            
        <div class="row border border-1 rounded p-3">
            <h5 class="text-secondary"><strong>Smock Checkout</strong></h5>
            <div class="col-lg-3">
                <label for="smockid">Smock Id</label>
                <div class="input-group">
                    <InputNumber id="smockid" @bind-Value="addSmockModel.SmockId" class="form-control form-control-lg" />
                    <button class="btn btn-lg btn-primary" @onclick="FindSmock">
                        <span class="bi bi-search" aria-hidden="false"></span> Search
                    </button>
                    <button class="btn btn-lg btn-warning" @onclick="ClearForm">
                        <span class="bi bi-x-lg" aria-hidden="false"></span> Clear
                    </button>
                </div>
                <ValidationMessage For="@(() => addSmockModel.SmockId)" />
            </div>
            <div class="col-lg-3">
                <label for="fname">First Name</label>
                <InputText id="fname" @bind-Value="addSmockModel.FName" disabled class="form-control form-control-lg" />
                <ValidationMessage For="@(() => addSmockModel.FName)" />                       
            </div>
            <div class="col-lg-3">
                <label for="lname">Last Name</label>
                <InputText id="lname" @bind-Value="addSmockModel.LName" disabled class="form-control form-control-lg" />
                <ValidationMessage For="@(() => addSmockModel.LName)" />
            </div>
            <div class="col-lg-3">
                <label for="service">Service</label>
                <div class="input-group">
                    <InputSelect id="service" @bind-Value="addSmockModel.Service" class="form-select form-select-lg">
                        <option value="" disabled selected>--Select--</option>
                        <option value="Clean">Clean</option>
                        <option value="Repair">Repair</option>
                        <option value="Clean/Repair">Clean+Repair</option>
                    </InputSelect>                           
                    <button type="submit" class="btn btn-lg btn-primary">
                        <span class="bi bi-plus-circle" aria-hidden="false"></span> Checkout
                    </button>                               
                </div>
                <ValidationMessage For="@(() => addSmockModel.Service)" />
            </div>                
        </div>
    </div>
</EditForm>

@code
{
    private List<Smock>? smocks;
    private User? userRecord; // 修改为单条记录变量
    private Smock addSmockModel = new();
    private EditContext? editForm; // 添加EditContext引用

    [CascadingParameter]
    public HttpContext? HttpContext { get; set; }

    protected override async Task OnInitializedAsync()
    {        
        await base.OnInitializedAsync();
        await LoadSmocks();
    }

    private async Task LoadSmocks()
    {
        smocks = await dbContext.Smocks.ToListAsync();
    }

    private async Task FindSmock()
    {
        var smockId = addSmockModel.SmockId;

        if (smockId > 0)
        {
            userRecord = await dbContext.Users.FirstOrDefaultAsync(x => x.SmockId == smockId);

            if (userRecord != null)
            {
                addSmockModel.FName = userRecord.FName;
                addSmockModel.LName = userRecord.LName;
                addSmockModel.SmockId = userRecord.SmockId;

                // 触发字段验证更新
                editForm?.NotifyFieldChanged(FieldIdentifier.Create(() => addSmockModel.FName));
                editForm?.NotifyFieldChanged(FieldIdentifier.Create(() => addSmockModel.LName));
            }
        }   
    }

    private async Task AddSmock()
    {
        addSmockModel.CheckedOut = "True";
        await dbContext.Smocks.AddAsync(addSmockModel);
        await dbContext.SaveChangesAsync();
        await LoadSmocks();
        addSmockModel = new();
    }

    private async Task EditSmock(Smock smock)
    {        
        dbContext.Update(smock);
        await dbContext.SaveChangesAsync();
        await LoadSmocks();
    }

    private async Task DeleteSmock(Smock smock)
    {
        bool confirmed = await JsRuntime.InvokeAsync<bool>("confirm", "Are you sure?");

        if (confirmed)
        {
            dbContext.Remove(smock);
            await dbContext.SaveChangesAsync();
            await LoadSmocks();
        }       
    }

    private void ClearForm()
    {
        addSmockModel = new();
    }
}

内容的提问来源于stack exchange,提问作者Wilock

火山引擎 最新活动