在 Blazor 中,有没有办法在不更改状态的情况下撤消无效的用户输入?
In Blazor, is there a way to undo invalid user input, without changing the state?
在 Blazor 中,如何撤消无效的用户输入,而不更改组件的状态以触发重新呈现?
这是一个简单的 Blazor 计数器示例 (try it online):
<label>Count:</label>
<button @onclick=Increment>@count times!</button><br>
A: <input @oninput=OnChange value="@count"><br>
B: <input @bind-value=count @bind-value:event="oninput">
@code {
int count = 1;
void Increment() => count++;
void OnChange(ChangeEventArgs e)
{
var userValue = e.Value?.ToString();
if (int.TryParse(userValue, out var v))
{
count = v;
}
else
{
if (String.IsNullOrWhiteSpace(userValue))
{
count = 0;
}
// if count hasn't changed here,
// I want to re-render "A"
// this doesn't work
e.Value = count.ToString();
// this doesn't work either
StateHasChanged();
}
}
}
对于 input
元素 A,我想复制 input
元素 B 的行为,但不使用 bind-xxx
样式的数据绑定属性。
例如,当我在 A 中键入 123x
时,我希望它自动恢复为 123
,就像在 B 中一样。
我试过 StateHasChanged
但它不起作用,我想,因为 count
属性 实际上并没有改变。
所以,基本上我需要重新渲染 A 来撤消无效的用户输入,即使状态没有改变。如果没有 bind-xxx
魔法,我该怎么做?
当然,bind-xxx
很棒,但在某些情况下可能需要非标准行为,围绕 ChangeEvent
.
等托管事件处理程序构建
已更新,为了比较,这是我在 React 中的做法 (try it online):
function App() {
let [count, setCount] = useState(1);
const handleClick = () => setCount((count) => count + 1);
const handleChange = (e) => {
const userValue = e.target.value;
let newValue = userValue ? parseInt(userValue) : 0;
if (isNaN(newValue)) newValue = count;
// re-render even when count hasn't changed
setCount(newValue);
};
return (
<>
Count: <button onClick={handleClick}>{count}</button><br/>
A: <input value={count} onInput={handleChange}/><br/>
</>
);
}
此外,这是我在 Svelte 中的实现方式,我发现它在概念上非常接近 Blazor (try it online)。
<script>
let count = 1;
const handleClick = () => count++;
const handleChange = e => {
const userValue = e.target.value;
let newValue = userValue? parseInt(userValue): 0;
if (isNaN(newValue)) newValue = count;
if (newValue === count)
e.target.value = count; // undo user input
else
count = newValue;
}
};
</script>
Count: <button on:click={handleClick}>{count}</button><br/>
A: <input value={count} on:input={handleChange}/><br/>
已更新,澄清一下,我只是想撤消任何我认为无效的输入,追溯发生后,通过处理更改事件,而不改变组件的状态本身(counter
这里)。
也就是说,没有 Blazor 特定的双向数据绑定,HTML 本机 type=number
或模式匹配属性。我这里只是简单的以数字格式要求为例;我希望能够撤销 any 这样的任意输入。
我想要的用户体验(通过 JS 互操作 hack 完成):https://blazorrepl.telerik.com/wPbvcvvi128Qtzvu03
令我惊讶的是,与其他框架相比,Blazor 中的这一点如此困难,而且我无法使用 StateHasChanged
简单地强制重新呈现处于当前状态的组件。
这是您的代码的修改版本,可以满足您的要求:
@page "/"
<label>Count:</label>
<button @onclick=Increment>@count times!</button>
<br>
A:
<input @oninput=OnChange value="@count">
<br>
B:
<input @bind-value=count @bind-value:event="oninput">
@code {
int count = 1;
void Increment() => count++;
async Task OnChange(ChangeEventArgs e)
{
var oldvalue = count;
var isNewValue = int.TryParse(e.Value?.ToString(), out var v);
if (isNewValue)
count = v;
else
{
count = 0;
// this one line may precipitate a few commments!
await Task.Yield();
count = oldvalue;
}
}
}
所以“发生了什么事?”
首先 razor 代码被预编译成 C# 类,所以你看到的并不是实际得到的代码 运行。这里就不细说了,网上有很多文章。
value="@count"
是一种单向绑定,并作为字符串传递。
您可以更改屏幕上输入的实际值,但在 Blazor 组件中该值仍然是旧值。没有回调告诉它其他情况。
当您在 22 之后键入 22x 时,OnChange
不会更新 count
。就渲染器而言,它没有改变,所以它不需要更新 DOM 的那个位。渲染器 DOM 和实际 DOM!
不匹配
OnChange
更改为 async Task
现在:
- 获取旧值的副本
- 如果新值是一个数字更新
count
。
- 如果不是数字
- 将
count
设置为另一个值 - 在本例中为零。
- 产量。 Blazor 组件事件处理程序调用
StateHasChanged
并产生。这使渲染器线程有时间为其队列提供服务并重新渲染。输入暂时为零。
- 将
count
设置为旧值。
- Returns 任务完成。 Blazor 组件事件处理程序 运行 完成第二次调用
StateHasChanged
。渲染器更新显示值。
更新为什么使用 Task.Yield
基本的 Blazor 组件事件处理程序 [BCEH from this point] 如下所示:
var task = InvokeAsync(EventMethod);
StateHasChanged();
if (!task.IsCompleted)
{
await task;
StateHasChanged();
}
将 OnChange
放入此上下文中。
var task = InvokeAsync(EventMethod)
运行s OnChange
。同步开始 运行。
如果 isNewValue
为假,它会将 count
设置为 0,然后通过 Task.Yield
将未完成的任务传递回 BCEH 来产生。然后可以进行 运行s StateHasChanged
将渲染片段排队到渲染器的队列中。请注意,它实际上并不渲染组件,只是将渲染片段排队。此时 BCEH 正在占用线程,因此渲染器实际上无法为其队列提供服务。然后它检查 task
看它是否完成。
如果 BCEH 完成,渲染器将获得一些线程时间并渲染组件。
如果它仍然是 运行ning - 就像我们用 Task.Yield 将它踢到线程队列的后面一样 - BCEH 等待它并产生。渲染器获取一些线程时间并渲染组件。 OnChange
然后完成,BCEH 获得一个已完成的任务,通过调用 StateHasChanged
将另一个渲染堆叠在渲染队列中并完成。渲染器,现在有了线程时间服务,它正在排队并第二次渲染组件。
请注意,有些人更喜欢使用 Task.Delay(1)
,因为有人讨论了 Task.Yield
的确切工作原理!
下面是我想出的(try online). I wish ChangeEventArgs.Value
两种方法都有效,但没有。没有它,我只能想到这个 JS.InvokeVoidAsync
hack:
@inject IJSRuntime JS
<label>Count:</label>
<button @onclick=Increment>@count times!</button>
<br>
A:
<input @oninput=OnChange value="@count" id="@id">
<br>
B:
<input @bind-value=count @bind-value:event="oninput">
@code {
int count = 1;
string id = Guid.NewGuid().ToString("N");
void Increment() => count++;
async Task OnChange(ChangeEventArgs e)
{
var userValue = e.Value?.ToString();
if (int.TryParse(userValue, out var v))
{
count = v;
}
else
{
if (String.IsNullOrWhiteSpace(userValue))
{
count = 0;
}
// this doesn't work
e.Value = count.ToString();
// this doesn't work either (no rererendering):
StateHasChanged();
// using JS interop as a workaround
await JS.InvokeVoidAsync("eval",
$"document.getElementById('{id}').value = Number('{count}')");
}
}
}
明确地说,我意识到这是一个可怕的黑客攻击(最后但并非最不重要的一点,因为它使用 eval
);我可以通过使用 ElementReference
和一个独立的 JS 导入来改进它,但如果 e.Value = count
正常工作,那么所有这些都不是必需的,like it does with Svelte. I've raised this as a Blazor issue in ASP.NET repo,希望它能引起一些关注。
从代码来看,您似乎想要清理用户输入?例如。只能输入数字、日期格式等...我同意如果你现在想在这方面手动使用事件处理程序有点困难,但仍然可以使用扩展属性和 Blazor 的绑定系统来验证输入。
你想要的看起来像这样:(try it here)
<label>Count: @Count</label>
<button @onclick=Increment>@Count times!</button><br>
<input @bind-value=Count @bind-value:event="oninput">
@code {
int _count = 1;
public string Count
{
get => _count.ToString();
set
{
if(int.TryParse(value, out var val)) {
_count = val;
}
else {
if(string.IsNullOrEmpty(value)) {
_count = 0;
}
}
}
}
void Increment() => _count++;
}
您可以使用 @on{DOM EVENT}:preventDefault
来阻止事件的默认操作。有关详细信息,请参阅 Microsoft 文档:https://docs.microsoft.com/en-us/aspnet/core/blazor/components/event-handling?view=aspnetcore-6.0#prevent-default-actions
更新
使用 preventDefault 的示例
<label>Count:</label>
<button @onclick=Increment>@count times!</button><br />
<br>
A: <input value="@count" @onkeydown=KeyHandler @onkeydown:preventDefault><br>
B: <input @bind-value=count @bind-value:event="oninput">
@code {
int count = 1;
string input = "";
void Increment() => count++;
private void KeyHandler(KeyboardEventArgs e)
{
//Define your pattern
var pattern = "abcdefghijklmnopqrstuvwxyz";
if (!pattern.Contains(e.Key) && e.Key != "Backspace")
{
//This is just a sample and needs exception handling
input = input + e.Key;
count = int.Parse(input);
}
if (e.Key == "Backspace")
{
input = input.Remove(input.Length - 1);
count = int.Parse(input);
}
}
}
您将不得不使用 @on{DOM EVENT}-preventDefault
。
它来自 javascript 世界,它阻止了按钮的默认行为。
在 blazor 和 razor 中,验证或 DDL 触发回发,这意味着请求由服务器处理并重新呈现。
执行此操作时,您需要确保您的事件不会 'bubble' 因为您正在阻止事件执行 postback。
如果你发现你的事件冒泡,意思是元素事件上升到它嵌套的元素,请使用:
stopPropagation="true";
了解更多信息:
这是一个非常有趣的问题。我以前从未使用过 Blazor,但我知道什么可能对这里有帮助,尽管这也是一个 hacky 答案。
我注意到如果您将元素更改为绑定到 count 变量,它会在控件失去焦点时更新值。所以我添加了一些代码来交换焦点元素。这似乎允许在不更改输入字段的情况下键入非数字字符,这是我认为这里需要的。
显然,这不是一个很好的解决方案,但我想我会提供它以防万一。
<label>Count:</label>
<button @onclick=Increment>@count times!</button><br>
A: <input @oninput=OnChange @bind-value=count @ref="textInput1"><br>
B: <input @bind-value=count @bind-value:event="oninput" @ref="textInput2">
@code {
ElementReference textInput1;
ElementReference textInput2;
int count = 1;
void Increment() => count++;
void OnChange(ChangeEventArgs e)
{
var userValue = e.Value?.ToString();
if (int.TryParse(userValue, out var v))
{
count = v;
}
else
{
if (String.IsNullOrWhiteSpace(userValue))
count = 0;
e.Value = count.ToString();
textInput2.FocusAsync();
textInput1.FocusAsync();
}
}
}
So, basically I need to re-render A to undo invalid user input, even
thought the state hasn't changed. How can I do that without the
bind-xxx magic?
您可以通过更改 @key
指令中的值强制重新创建和重新呈现任何 element/component:
<input @oninput=OnChange value="@count" @key="version" />
void OnChange(ChangeEventArgs e)
{
var userValue = e.Value?.ToString();
if (int.TryParse(userValue, out var v))
{
count = v;
}
else
{
version++;
if (String.IsNullOrWhiteSpace(userValue))
{
count = 0;
}
}
}
注意,它将重新呈现整个元素(及其子树),而不仅仅是属性。
您的代码存在问题,您组件的 BuildRenderrTree 方法生成了完全相同的 RenderTree,因此 diffing 算法在实际 dom.
中找不到任何要更新的内容
那么为什么 @bind 指令有效?
注意生成的 BuildRenderTree 代码:
//A
__builder.OpenElement(6, "input");
__builder.AddAttribute(7, "oninput", Microsoft.AspNetCore.Components.EventCallback.Factory.Create<Microsoft.AspNetCore.Components.ChangeEventArgs>(this, OnChange));
__builder.AddAttribute(8, "value", count);
__builder.CloseElement();
//B
__builder.AddMarkupContent(9, "<br>\r\nB: ");
__builder.OpenElement(10, "input");
__builder.AddAttribute(11, "value", Microsoft.AspNetCore.Components.BindConverter.FormatValue(count));
__builder.AddAttribute(12, "oninput", Microsoft.AspNetCore.Components.EventCallback.Factory.CreateBinder(this, __value => count = __value, count));
__builder.SetUpdatesAttributeName("value");
__builder.CloseElement();
诀窍是,@bind 指令添加:
__builder.SetUpdatesAttributeName("value");
您现在不能在 EventCallback 的标记中执行此操作,但有一个未解决的问题:https://github.com/dotnet/aspnetcore/issues/17281
但是您仍然可以创建一个 Component 或 RenderFragment 并手动编写 __builder
代码。
在 Blazor 中,如何撤消无效的用户输入,而不更改组件的状态以触发重新呈现?
这是一个简单的 Blazor 计数器示例 (try it online):
<label>Count:</label>
<button @onclick=Increment>@count times!</button><br>
A: <input @oninput=OnChange value="@count"><br>
B: <input @bind-value=count @bind-value:event="oninput">
@code {
int count = 1;
void Increment() => count++;
void OnChange(ChangeEventArgs e)
{
var userValue = e.Value?.ToString();
if (int.TryParse(userValue, out var v))
{
count = v;
}
else
{
if (String.IsNullOrWhiteSpace(userValue))
{
count = 0;
}
// if count hasn't changed here,
// I want to re-render "A"
// this doesn't work
e.Value = count.ToString();
// this doesn't work either
StateHasChanged();
}
}
}
对于 input
元素 A,我想复制 input
元素 B 的行为,但不使用 bind-xxx
样式的数据绑定属性。
例如,当我在 A 中键入 123x
时,我希望它自动恢复为 123
,就像在 B 中一样。
我试过 StateHasChanged
但它不起作用,我想,因为 count
属性 实际上并没有改变。
所以,基本上我需要重新渲染 A 来撤消无效的用户输入,即使状态没有改变。如果没有 bind-xxx
魔法,我该怎么做?
当然,bind-xxx
很棒,但在某些情况下可能需要非标准行为,围绕 ChangeEvent
.
已更新,为了比较,这是我在 React 中的做法 (try it online):
function App() {
let [count, setCount] = useState(1);
const handleClick = () => setCount((count) => count + 1);
const handleChange = (e) => {
const userValue = e.target.value;
let newValue = userValue ? parseInt(userValue) : 0;
if (isNaN(newValue)) newValue = count;
// re-render even when count hasn't changed
setCount(newValue);
};
return (
<>
Count: <button onClick={handleClick}>{count}</button><br/>
A: <input value={count} onInput={handleChange}/><br/>
</>
);
}
此外,这是我在 Svelte 中的实现方式,我发现它在概念上非常接近 Blazor (try it online)。
<script>
let count = 1;
const handleClick = () => count++;
const handleChange = e => {
const userValue = e.target.value;
let newValue = userValue? parseInt(userValue): 0;
if (isNaN(newValue)) newValue = count;
if (newValue === count)
e.target.value = count; // undo user input
else
count = newValue;
}
};
</script>
Count: <button on:click={handleClick}>{count}</button><br/>
A: <input value={count} on:input={handleChange}/><br/>
已更新,澄清一下,我只是想撤消任何我认为无效的输入,追溯发生后,通过处理更改事件,而不改变组件的状态本身(counter
这里)。
也就是说,没有 Blazor 特定的双向数据绑定,HTML 本机 type=number
或模式匹配属性。我这里只是简单的以数字格式要求为例;我希望能够撤销 any 这样的任意输入。
我想要的用户体验(通过 JS 互操作 hack 完成):https://blazorrepl.telerik.com/wPbvcvvi128Qtzvu03
令我惊讶的是,与其他框架相比,Blazor 中的这一点如此困难,而且我无法使用 StateHasChanged
简单地强制重新呈现处于当前状态的组件。
这是您的代码的修改版本,可以满足您的要求:
@page "/"
<label>Count:</label>
<button @onclick=Increment>@count times!</button>
<br>
A:
<input @oninput=OnChange value="@count">
<br>
B:
<input @bind-value=count @bind-value:event="oninput">
@code {
int count = 1;
void Increment() => count++;
async Task OnChange(ChangeEventArgs e)
{
var oldvalue = count;
var isNewValue = int.TryParse(e.Value?.ToString(), out var v);
if (isNewValue)
count = v;
else
{
count = 0;
// this one line may precipitate a few commments!
await Task.Yield();
count = oldvalue;
}
}
}
所以“发生了什么事?”
首先 razor 代码被预编译成 C# 类,所以你看到的并不是实际得到的代码 运行。这里就不细说了,网上有很多文章。
value="@count"
是一种单向绑定,并作为字符串传递。
您可以更改屏幕上输入的实际值,但在 Blazor 组件中该值仍然是旧值。没有回调告诉它其他情况。
当您在 22 之后键入 22x 时,OnChange
不会更新 count
。就渲染器而言,它没有改变,所以它不需要更新 DOM 的那个位。渲染器 DOM 和实际 DOM!
OnChange
更改为 async Task
现在:
- 获取旧值的副本
- 如果新值是一个数字更新
count
。 - 如果不是数字
- 将
count
设置为另一个值 - 在本例中为零。 - 产量。 Blazor 组件事件处理程序调用
StateHasChanged
并产生。这使渲染器线程有时间为其队列提供服务并重新渲染。输入暂时为零。 - 将
count
设置为旧值。 - Returns 任务完成。 Blazor 组件事件处理程序 运行 完成第二次调用
StateHasChanged
。渲染器更新显示值。
更新为什么使用 Task.Yield
基本的 Blazor 组件事件处理程序 [BCEH from this point] 如下所示:
var task = InvokeAsync(EventMethod);
StateHasChanged();
if (!task.IsCompleted)
{
await task;
StateHasChanged();
}
将 OnChange
放入此上下文中。
var task = InvokeAsync(EventMethod)
运行s OnChange
。同步开始 运行。
如果 isNewValue
为假,它会将 count
设置为 0,然后通过 Task.Yield
将未完成的任务传递回 BCEH 来产生。然后可以进行 运行s StateHasChanged
将渲染片段排队到渲染器的队列中。请注意,它实际上并不渲染组件,只是将渲染片段排队。此时 BCEH 正在占用线程,因此渲染器实际上无法为其队列提供服务。然后它检查 task
看它是否完成。
如果 BCEH 完成,渲染器将获得一些线程时间并渲染组件。
如果它仍然是 运行ning - 就像我们用 Task.Yield 将它踢到线程队列的后面一样 - BCEH 等待它并产生。渲染器获取一些线程时间并渲染组件。 OnChange
然后完成,BCEH 获得一个已完成的任务,通过调用 StateHasChanged
将另一个渲染堆叠在渲染队列中并完成。渲染器,现在有了线程时间服务,它正在排队并第二次渲染组件。
请注意,有些人更喜欢使用 Task.Delay(1)
,因为有人讨论了 Task.Yield
的确切工作原理!
下面是我想出的(try online). I wish ChangeEventArgs.Value
两种方法都有效,但没有。没有它,我只能想到这个 JS.InvokeVoidAsync
hack:
@inject IJSRuntime JS
<label>Count:</label>
<button @onclick=Increment>@count times!</button>
<br>
A:
<input @oninput=OnChange value="@count" id="@id">
<br>
B:
<input @bind-value=count @bind-value:event="oninput">
@code {
int count = 1;
string id = Guid.NewGuid().ToString("N");
void Increment() => count++;
async Task OnChange(ChangeEventArgs e)
{
var userValue = e.Value?.ToString();
if (int.TryParse(userValue, out var v))
{
count = v;
}
else
{
if (String.IsNullOrWhiteSpace(userValue))
{
count = 0;
}
// this doesn't work
e.Value = count.ToString();
// this doesn't work either (no rererendering):
StateHasChanged();
// using JS interop as a workaround
await JS.InvokeVoidAsync("eval",
$"document.getElementById('{id}').value = Number('{count}')");
}
}
}
明确地说,我意识到这是一个可怕的黑客攻击(最后但并非最不重要的一点,因为它使用 eval
);我可以通过使用 ElementReference
和一个独立的 JS 导入来改进它,但如果 e.Value = count
正常工作,那么所有这些都不是必需的,like it does with Svelte. I've raised this as a Blazor issue in ASP.NET repo,希望它能引起一些关注。
从代码来看,您似乎想要清理用户输入?例如。只能输入数字、日期格式等...我同意如果你现在想在这方面手动使用事件处理程序有点困难,但仍然可以使用扩展属性和 Blazor 的绑定系统来验证输入。
你想要的看起来像这样:(try it here)
<label>Count: @Count</label>
<button @onclick=Increment>@Count times!</button><br>
<input @bind-value=Count @bind-value:event="oninput">
@code {
int _count = 1;
public string Count
{
get => _count.ToString();
set
{
if(int.TryParse(value, out var val)) {
_count = val;
}
else {
if(string.IsNullOrEmpty(value)) {
_count = 0;
}
}
}
}
void Increment() => _count++;
}
您可以使用 @on{DOM EVENT}:preventDefault
来阻止事件的默认操作。有关详细信息,请参阅 Microsoft 文档:https://docs.microsoft.com/en-us/aspnet/core/blazor/components/event-handling?view=aspnetcore-6.0#prevent-default-actions
更新
使用 preventDefault 的示例
<label>Count:</label>
<button @onclick=Increment>@count times!</button><br />
<br>
A: <input value="@count" @onkeydown=KeyHandler @onkeydown:preventDefault><br>
B: <input @bind-value=count @bind-value:event="oninput">
@code {
int count = 1;
string input = "";
void Increment() => count++;
private void KeyHandler(KeyboardEventArgs e)
{
//Define your pattern
var pattern = "abcdefghijklmnopqrstuvwxyz";
if (!pattern.Contains(e.Key) && e.Key != "Backspace")
{
//This is just a sample and needs exception handling
input = input + e.Key;
count = int.Parse(input);
}
if (e.Key == "Backspace")
{
input = input.Remove(input.Length - 1);
count = int.Parse(input);
}
}
}
您将不得不使用 @on{DOM EVENT}-preventDefault
。
它来自 javascript 世界,它阻止了按钮的默认行为。
在 blazor 和 razor 中,验证或 DDL 触发回发,这意味着请求由服务器处理并重新呈现。
执行此操作时,您需要确保您的事件不会 'bubble' 因为您正在阻止事件执行 postback。
如果你发现你的事件冒泡,意思是元素事件上升到它嵌套的元素,请使用:
stopPropagation="true";
了解更多信息:
这是一个非常有趣的问题。我以前从未使用过 Blazor,但我知道什么可能对这里有帮助,尽管这也是一个 hacky 答案。
我注意到如果您将元素更改为绑定到 count 变量,它会在控件失去焦点时更新值。所以我添加了一些代码来交换焦点元素。这似乎允许在不更改输入字段的情况下键入非数字字符,这是我认为这里需要的。
显然,这不是一个很好的解决方案,但我想我会提供它以防万一。
<label>Count:</label>
<button @onclick=Increment>@count times!</button><br>
A: <input @oninput=OnChange @bind-value=count @ref="textInput1"><br>
B: <input @bind-value=count @bind-value:event="oninput" @ref="textInput2">
@code {
ElementReference textInput1;
ElementReference textInput2;
int count = 1;
void Increment() => count++;
void OnChange(ChangeEventArgs e)
{
var userValue = e.Value?.ToString();
if (int.TryParse(userValue, out var v))
{
count = v;
}
else
{
if (String.IsNullOrWhiteSpace(userValue))
count = 0;
e.Value = count.ToString();
textInput2.FocusAsync();
textInput1.FocusAsync();
}
}
}
So, basically I need to re-render A to undo invalid user input, even thought the state hasn't changed. How can I do that without the bind-xxx magic?
您可以通过更改 @key
指令中的值强制重新创建和重新呈现任何 element/component:
<input @oninput=OnChange value="@count" @key="version" />
void OnChange(ChangeEventArgs e)
{
var userValue = e.Value?.ToString();
if (int.TryParse(userValue, out var v))
{
count = v;
}
else
{
version++;
if (String.IsNullOrWhiteSpace(userValue))
{
count = 0;
}
}
}
注意,它将重新呈现整个元素(及其子树),而不仅仅是属性。
您的代码存在问题,您组件的 BuildRenderrTree 方法生成了完全相同的 RenderTree,因此 diffing 算法在实际 dom.
中找不到任何要更新的内容那么为什么 @bind 指令有效?
注意生成的 BuildRenderTree 代码:
//A
__builder.OpenElement(6, "input");
__builder.AddAttribute(7, "oninput", Microsoft.AspNetCore.Components.EventCallback.Factory.Create<Microsoft.AspNetCore.Components.ChangeEventArgs>(this, OnChange));
__builder.AddAttribute(8, "value", count);
__builder.CloseElement();
//B
__builder.AddMarkupContent(9, "<br>\r\nB: ");
__builder.OpenElement(10, "input");
__builder.AddAttribute(11, "value", Microsoft.AspNetCore.Components.BindConverter.FormatValue(count));
__builder.AddAttribute(12, "oninput", Microsoft.AspNetCore.Components.EventCallback.Factory.CreateBinder(this, __value => count = __value, count));
__builder.SetUpdatesAttributeName("value");
__builder.CloseElement();
诀窍是,@bind 指令添加:
__builder.SetUpdatesAttributeName("value");
您现在不能在 EventCallback 的标记中执行此操作,但有一个未解决的问题:https://github.com/dotnet/aspnetcore/issues/17281
但是您仍然可以创建一个 Component 或 RenderFragment 并手动编写 __builder
代码。