Blazor - 如何使子组件显示验证消息?

5

如果嵌套组件未正确填写,我需要显示验证消息。该组件由其他父组件使用,并且它们需要获取有关是否存在验证问题的反馈。

我尝试了以下代码来处理嵌套组件并使用CanSubmit方法。虽然该方法正确地指示是否存在验证问题,但验证消息未显示。

下面的所有代码都可以在blzorrepl上进行测试: https://blazorrepl.com/repl/GvOQlvvv1789ra1G37

@if(editContext != null) {
    <EditForm EditContext="@editContext">
        <input type="text" @bind="testModel.Name" />
        <DataAnnotationsValidator />
        <ValidationSummary />
    </EditForm>
}

@code {
    [Parameter]
    public TestModel testModel
    {
        get { return (TestModel)editContext?.Model; }
        set { editContext = new EditContext(value); }
    }

    EditContext editContext;

    public bool CanSubmit()
    {
        return editContext.Validate();
    }
}

这是我的父组件代码,略有缩减但能够重现问题:

<ChildComponent @ref="myTestComponent" testModel="testModel" />
<input type="button" @onclick="buttonClick" value="validate programmatically" />
<div>@testMessage</div>

@code {
    TestModel testModel = new TestModel();
    ChildComponent myTestComponent;
    string testMessage;

    void buttonClick()
    {
        testMessage = "not passed validation";

        if (myTestComponent.CanSubmit())
        {
            testMessage = "passed validation!";
        }
    }
}

testMessage仅用于显示验证状态。

我该怎么做才能使父组件导致嵌套组件显示验证消息? 我只能将submit放在父组件中。

如所请求,这里是我正在进行的更完整的示例,一个可以内联编辑的项目列表以及一个添加更多实例的表单。 https://blazorrepl.com/repl/mlYwlQPm34bekYE824


你需要一个Custom-Validator。 EditForm只能与InputCheckBox,InputDate等内置表单组件一起使用。你可以查看Microsoft文档。 - JoeGER94
@JoeGER94 如果我在EditForm中放置并点击一个submit输入,我可以显示验证消息。我不明白为什么我应该使用内置组件-它们如何使验证消息开始在这种情况下显示? - jakubiszon
你使用 <input> 而不是 <InputText> 有什么理由吗?从 InputBase 继承的组件有许多内置功能,比如验证。如果你改成 <InputText @bind-Value="testModel.Name" />,应该就能正常工作了。请记住,在 ValidationSummary 显示内容之前,该字段必须被修改。因此,添加文本、删除文本或者将焦点设置到其他地方都应该触发验证。 - Just the benno
@Justthebenno 我希望在运行 CanSubmit 方法时显示错误消息。将 input 更新为 InputText 可以在模糊事件上显示验证消息,但是当我按下提交按钮时,该消息再次隐藏。 - jakubiszon
@jakubiszon,我真的很想帮助你。但是我真的不理解你的问题。你不想使用“submit”吗?如果你想从ChildComponent返回一个值,也许可以使用EventCallback。为了更好地帮助你,我需要一个清晰的描述。 - JoeGER94
显示剩余5条评论
3个回答

5
我将尝试解释为什么您的方法不起作用,并提出解决方法。希望我正确理解了您的意图。
首先,您需要将<input type="text" ...>更改为<InputText @bind-Value="..." /> 当您的父组件中的方法buttonClick完成后,Blazor将调用您的组件上的StateHasChanged。这是EventHandler内置逻辑的一部分。这将触发子组件的组件生命周期。在该周期期间,再次将调用子组件属性testModel的setter。Blazor不会进行任何相等性测试(唯一强大的检查引擎是渲染周期结束时的DifferentialRenderTree)。这意味着将创建一个新的EditContext。但是,此上下文不知道验证错误。因此,消息消失了。为了证明这一点,请在setter中设置一个计数器变量,并在页面上显示它。您将看到此结果。

enter image description here.

为了避免这种情况,您可以在设置参数时创建EditContext
@code {

    [Parameter]
    public TestModel testModel { get; set; }

    EditContext editContext;

   protected override void OnParametersSet()
   {
       base.OnParametersSet();
       if(editContext == null)
       {
           editContext = new EditContext(testModel);
       }
   }

    public bool CanSubmit()
    {
        return editContext.Validate();
    }
}

如果您需要更新模型但保留验证状态,请写下评论,我们可以从那里开始。

enter image description here


谢谢,非常有用。我更新了ChildComponentsetter,只在给定TestModel的新实例时设置EditContext。现在它完全符合我的需求,尽管保留了<input type="text">。这是更新后的repl https://blazorrepl.com/repl/QFuGvmkX53o67uV758。这是最好的解决方案吗?还是您有其他建议? - jakubiszon
我认为你在考虑一个非常特殊的问题,并选择了一种独特的解决方案。因为我看不到或理解你的上下文,所以我无法建议“更好”的方式。 @JoeGER94 的级联参数解决方案也可能是一个不错的解决方案。此外,我认为写下来会增加误解的程度 :) 我能提供的是一个电话/聊天,在这个过程中你可以向我展示上下文,希望之后我能回答你的问题。 - Just the benno
@jakubiszon,我个人认为这个解决方案难以阅读,并且做了很多不必要的事情,如果您使用我的方法,这些都是不必要的。此外,如果您在不同的父级中使用它,可能会出现问题,就像您在问题中描述的那样。对Cascading-Value的更改将在每个消费子级中更新并反转。 - JoeGER94
我在我的问题底部添加了另一个repl链接。基本上,我需要在页面上两次引用相同的编辑器,并且每种情况下的按钮集是不同的。 - jakubiszon

4

原始问题是如何分配 EditContext。需要访问它,但可以通过以下示例中的editForm.EditContext来完成。

工作示例:https://blazorrepl.com/repl/Glkmbcbr323MP1VS55

父组件:

<ChildComponent @ref="myTestComponent" testModel="testModel" />
<input type="button" @onclick="buttonClick" value="validate programmatically" />
<div>@testMessage</div>

@code {
    TestModel testModel = new TestModel();
    ChildComponent myTestComponent;
    string testMessage;

    void buttonClick()
    {
        testMessage = "not passed validation";

        if (myTestComponent.CanSubmit())
        {
            testMessage = "passed validation!";
        }
    }
}

子组件:

@if(testModel != null) {
    <EditForm @ref="editForm" Model="@testModel">
        <input type="text" @bind="testModel.Name" />
        <DataAnnotationsValidator />
        <ValidationSummary />
    </EditForm>
}

@code {
    [Parameter]
    public TestModel testModel
    {
        get; set;
    }

    EditForm editForm;

    public bool CanSubmit()
    {
        return editForm.EditContext.Validate();
    }
}

2
我有一种不同的方法.. 你的父元素必须像这样
<CascadingValue Value="testModel">
    <EditForm Model="testModel">
        <ChildComponent @ref="myTestComponent" />
        <ObjectGraphDataAnnotationsValidator />
        <ValidationMessage For="@(()=>testModel.Name)" />
        <br />
        <button type="submit">test</button>
    </EditForm>
</CascadingValue>

现在您在 Child 中需要做的唯一一件事就是这样。
@if (testModel != null)
{
  <InputText @bind-Value="testModel.Name" />
}

@code {

    [CascadingParameter]
    public TestModel testModel { get; set; }
}

我个人认为这样写的代码量会更少...


非常有趣。这是我使用您的方法编写的工作 REPL https://blazorrepl.com/repl/wFkmbclL55yO5QFn38 它通过提交单个 EditForm 来工作。我认为这有点不灵活。我在我的答案中使用的方法将允许使用两个独立的子组件(例如联系人详细信息+交货地址)在一个事件上被接受。我所需要做的就是在每个子控件上实现和调用验证方法(在本例中命名为 CanSubmit ),以知道是否可以处理两个模型。否则,我需要组合另一个模型或使用更多的提交按钮,我是正确的吗? - jakubiszon
@jakubiszon 嗯...我的主要方法会比你的更加通用。首先,我不会“硬编码”Childs。我会动态地呈现它们,这样更容易扩展,并且我会在Childs中使用一些多态代码,以防止一直编写相同的代码。我们都采用了不同的方式-两种方式都是可行的... - JoeGER94
@JoeGET94 那听起来确实很有趣。你知道我能在网上找到哪些类似方法的例子吗? - jakubiszon
1
@jakubiszon,您可以使用RenderTreeBuilder动态渲染Razor组件。首先,请查看https://blazor-university.com/templating-components-with-renderfragements/passing-data-to-a-renderfragement/,然后按照Steve Sanderson在此处的说明生成它:https://github.com/dotnet/aspnetcore/issues/16104#issuecomment-385713669。 - JoeGER94

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