如何在 blazor 中实现两种方式的数据绑定?

Dbl*_*Dbl 1 blazor blazor-server-side

我有以下类似于TextBox组件的组件。然而由于某种原因,内容似乎Value永远不会改变。

我本希望它可以在没有手动 JavaScript 的情况下与数据绑定一起使用,不是吗?


<div class="form-group">
    <input type="text" class="form-control" @bind="Value" @onkeyup="OnKeyUp" placeholder="@Placeholder"/>
</div>

@code {
    
    [Parameter]
    public string Value { get; set; }

    [Parameter]
    public EventCallback<string> ValueChanged { get; set; }

    [Parameter]
    public string Placeholder { get; set; }

    private void OnKeyUp(KeyboardEventArgs obj)
    {
        ValueChanged.InvokeAsync(Value);
    }

}
Run Code Online (Sandbox Code Playgroud)

Mis*_*goo 8

您不应该绑定到 a [Parameter],因为它可能会导致副作用(请参阅此处 Steve Sanderson 的评论

您似乎希望对每个更改进行两种方式绑定,对此我会使用oninput而不是,但如果这确实是您需要的,则onkeyup可以使用。onkeyup

主要的事情是绑定到本地并且永远不要手动更改公共参数:

<div class="form-group">
    <input type="text" class="form-control" 
           @bind-value=@InternalValue 
           @bind-value:event="oninput" 
           placeholder="@Placeholder"/>
</div>

@code {

    string InternalValue
    {
        get => Value;
        set 
        {
            if (value!=Value)
            {
                ValueChanged.InvokeAsync(value);        
            }
        }
    }

    [Parameter]
    public string Value { get; set; }

    [Parameter]
    public EventCallback<string> ValueChanged { get; set; }

    [Parameter]
    public string Placeholder { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

编辑:添加史蒂夫·桑德森的笔记,以防它们消失......

引用 Steve Sanderson 在上面链接的 GitHub 问题上的内容:

好的,我已经更详细地查看了,看看发生了什么。下面有一个解释,但在讨论之前,我将重申我的观点,即核心问题是子组件覆盖其自己的 [Parameter] 属性值。通过避免这样做,您可以避免这个问题。

在这种情况下以及在其他情况下,Blazor 依赖于安全的父子参数分配(不会覆盖您想要保留的信息)并且不会产生副作用,例如触发更多渲染(因为那样您可能会陷入无限循环) )。我们可能应该加强文档中的指导,而不仅仅是说“不要写入您自己的参数”,并将其扩展以警告不要在此类属性设置器中产生副作用。Blazor 使用 C# 属性来表示从父组件到子组件的通信通道对于开发人员来说非常方便,并且在源代码中看起来不错,但副作用的能力是有问题的。我们已经有了一个编译时分析器,如果您在 [Parameter] 属性上没有正确的可访问性修饰符,它会发出警告 - 如果参数不是简单的 { get; ,则也许我们应该扩展它以给出警告/错误。放; } 汽车属性。

因此,在这种情况下,解决方案非常简单:用简单的 { get; 替换 Component1 的 Value 属性。放; 一,让 Component1 通过触发 ValueChanged 来响应按钮点击,而不是尝试写入它。这样就没有任何递归渲染周期,并且不会发生数据覆盖。

private void DecrementValue()
{
    //Value--; <-- Don't do this

    // Do this instead:
    ValueChanged.InvokeAsync(Value - 1);
}
Run Code Online (Sandbox Code Playgroud)