【问题标题】:Data binding in Blazor: How to propagate values out of a component?Blazor 中的数据绑定:如何将值传播出组件?
【发布时间】:2020-01-21 06:34:41
【问题描述】:

我有一个相当简单的组件,如下所示:

<form>
    <div class="form-group">
        <label for="sampleText">Sample Text</label>
        <input type="text"
               class="form-control"
               id="sampleText"
               aria-describedby="sampleTextHelp"
               placeholder="Enter a text"
               @bind="@Value" />
        <small id="sampleTextHelp" class="form-text text-muted">Enter a text to test the databinding</small>
    </div>
</form>
<p>You entered here: @Value</p>

@code {

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

我现在将它添加到这样的页面中:

<MVVMTest Value="@_value" />
<p>
    You entered in MVVMTest:
    @_value
</p>

@functions {
    private string _value;
}

当我在输入字段中输入文本时,它会在 您在此处输入: 处正确更新,但不会传播到 *您在 MVVMTest 中输入”:

我必须做什么才能正确传播?

我可以考虑将Action&lt;string&gt; 连接为第二个[Parameter],当文本更改时我会在组件内触发它,但这似乎是一种迂回和迂回的方式。是必须这样做,还是有更好的方法?

跟进 1

Issac 的答案不起作用,因为它在@bind-value:oninput="@((e) =&gt; ValueChanged.Invoke(e.Value))"Cannot convert lambda expression to type 'object' because it is not a delegate type 上绊倒了。

我不得不以这种迂回的方式来做:

<form>
    <div class="form-group">
        <label for="sampleText">Sample Text</label>
        <input type="text"
               class="form-control"
               id="sampleText"
               aria-describedby="sampleTextHelp"
               placeholder="Enter a text"
               @bind="@Value" />
        <small id="sampleTextHelp" class="form-text text-muted">Enter a text to test the databinding</small>
    </div>
</form>
<p>You entered here: @Value</p>

@code {

    private string _value;

    [Parameter]
    public string Value {
        get => _value;
        set {
            if(Equals(value, _value)) {
                return;
            }
            _value = value;
            OnValueChanged.InvokeAsync(value).GetAwaiter().GetResult();
        }
    }

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

并像这样使用它:

@inject HttpClient http
@page "/test"
<div class="top-row px-4">
    Test Page
</div>

<div class="content px-4">
    <MVVMTest Value="@_value" OnValueChanged="@(v => _value = v)" />
    <p>
        You entered in MVVMTest:
        @_value
    </p>
</div>

@functions {
    private string _value;
}

不漂亮,但它有效。我的“真实”组件更复杂,因为更改的值会触发对底层 ASP.net 核心服务的调用,因此我必须仔细检测谁更改了什么以避免无限循环。

如果 Blazor 支持类似 XAML/WPF 的 MVVM 肯定会更好...

跟进 2

我回到了 Issac 的解决方案,加上一个额外的演员,我得到了它的工作:

<form>
    <div class="form-group">
        <label for="sampleText">Sample Text</label>
        <input type="text"
               class="form-control"
               id="sampleText"
               aria-describedby="sampleTextHelp"
               placeholder="Enter a text"
               value="@Value"
               oninput="@((Func<ChangeEventArgs, Task>)(async e => await OnValueChanged.InvokeAsync(e.Value as string)))" />
        <small id="sampleTextHelp" class="form-text text-muted">Enter a text to test the databinding</small>
    </div>
</form>
<p>You entered here: @Value</p>

@code {

    private string _value;

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

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

用法:

<MVVMTest Value="@_value" OnValueChanged="@(v => _value = v)" />
<p>
    You entered in MVVMTest:
    @_value
</p>

@functions {
    private string _value;
}

这已经是 Blazor 中的一个已知问题。我上周已经绊倒了,我也发布了解决方法in the VS developer forum

【问题讨论】:

  • 请注意,使用 EventCallback 会在父组件中引发 StateHasChanged(),这将触发所有父组件的子组件的渲染,因此除非您希望每个组件在每次击键或数据更改时都重新渲染- 可以考虑您的架构 - 查看 ShouldRender() 覆盖并实现代码以确保每个组件仅在需要时呈现,或者考虑使用不依赖 UI 传播状态更改的状态管理模式。
  • 这也是一个好点。是否也可以只重新渲染单个组件?然后,我可以使用ActionFunc 而不是EventCallback,并让组件决定何时重新渲染自己。
  • 是的,如果你使用Action/Func而不是EventCallback,那么父级可以决定是否调用StateHasChanged,但是如果调用了,你仍然会触发所有子级的重新渲染。这就是 UI 之外的状态管理的亮点 - 无论您选择哪种方法,组件都应该能够“注册”自己,以便在数据更改时收到通知,以便它可以自己调用 StateHasChanged。
  • 这可以像 DI 中的简单状态服务一样简单,它具有在数据更改时触发的操作/事件,或者它可以变得像您喜欢的任何状态管理库一样复杂。或者您可以使用 ShouldRender() 作为选择退出重新渲染的被动方式。在小型个人项目中,我发现实现事件的状态服务最简单。
  • 等一下!您提到了避免无限循环。我偶然发现了那个确切的问题。简单案例:我只是将输入值从复选框向上传递到父元素。这会导致无限循环和崩溃。我有类似的元素,当我在 JS 中读取值并且我有一个阻止循环的标志时,因为我知道来自 JS 的调用是我应该调用ValueChanged 的唯一情况。现在我没有这个区别,那我该如何实现死循环预防呢?

标签: c# mvvm data-binding blazor


【解决方案1】:

您需要添加一个 EventCallback 参数来执行此操作:

@code {

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

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

阅读Data binding/组件参数部分

【讨论】:

    猜你喜欢
    • 2019-10-01
    • 2020-12-19
    • 2020-03-18
    • 2022-11-23
    • 1970-01-01
    • 1970-01-01
    • 2021-11-11
    • 1970-01-01
    • 2020-09-30
    相关资源
    最近更新 更多