我如何告诉或允许 Blazor 从未更改的绑定值中 "refresh" 一个 HTML 元素?

How do I tell or allow Blazor to "refresh" an HTML element from its bound value that hasn't changed?

当我在 HTML 元素上进行单向绑定并且在 onchange 上进行绑定时并不总是会更改它,那么我该如何告诉或允许 Blazor“刷新”该元素的值与其未更改的绑定值相比?

StateHasChanged() 不起作用,大概是因为 Blazor 检测到没有变化。

极简但荒谬的例子(注意现实世界的案例并不荒谬):

@page "/"
<select value="@Foo" @onchange="Update">
  <option>First</option>
  <option>Second</option>
</select> 
<div>
  Value of Foo: @Foo
</div>
@code {
  private string Foo {get;set;} = "First";
  private void Update(ChangeEventArgs args) {
    return; // doesn't actually change it
  }
}

因此 HTML 元素中的新值在更改时立即显示,但绑定值实际上从未更改。 Working demo here: 如何让下拉列表的值保持(或变回)“First”,即存储在 Foo 中的未更改值?

最简单的方法是给元素一个@key,当你想强制刷新时,你可以改变它。不过,这将完全拆除并重新创建该元素,因此成本可能很高。

@page "/"

<select @key="@Forced" value="@Foo" @onchange=@( e => Update(e) )>
  <option>(none)</option>
  <option>First</option>
  <option>Second</option>
</select> 
@code {
  private string Foo {get;set;} = "First";
  private int Forced {get;set;} = 0;
  private Task Update(ChangeEventArgs args) {
    if (1 == 1){ // placeholder for other logic 
      Forced++; // doesn't actually change it
    }
    else
    {
      Foo = args.Value.ToString() ?? "(none)";
    }
    return Task.CompletedTask; 
  }
}

Updated demo

问题出在渲染器和差异引擎上。

该值在浏览器 DOM 中已更改,但在渲染器 DOM 中未更改。这是两者在 Blazor 中“合法地”不同步的少数情况之一。因此,当您将值改回原来的值时,Diffing 引擎看不出有什么不同,因此不会向浏览器发送任何更新 DOM。在构建只允许特定输入字符的文本框时,您会看到类似的情况。

我知道的唯一解决方案是利用 Blazor UI 事件处理程序的异步行为。

  1. 将值设置为不同的值 - 我通常使用 String.Empty 2. 产量控制因此 UI 可以使用 Task.Delay
  2. 进行更新
  3. UI 处理程序调用 StateHasChanged 并更新 UI。
  4. 将其设置为您想要的值。
  5. UI 处理程序执行它是最终的 StateHasChanged 并将输入更新为正确的值。
@page "/"

<div>
Foo: <select value="@Foo" @onchange="Update">
  <option>(none)</option>
  <option>First</option>
  <option>Second</option>
</select> 
</div>

<div>
  Value of Foo: @Foo
</div>
@code {
    private string Foo {get;set;} = "First";

    private async Task Update(ChangeEventArgs args) {
        Foo = string.Empty;
        await Task.Delay(1);
        Foo = "First";
  }
}

如果我理解你的要求,试试这个代码,它会显示 Foo 的选定值

<select value="@Foo" @onchange="@(e=> Foo=e?.Value?.ToString())">
  <option value="(none)">(none)</option>
  <option value="First">First</option>
  <option value="Second">Second</option>
</select>