nos*_*tio 8 .net c# asp.net blazor blazor-webassembly
在 Blazor 中,如何撤消无效的用户输入,而不更改组件的状态以触发重新渲染?
这是一个简单的 Blazor 计数器示例(在线尝试):
<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();
}
}
}
Run Code Online (Sandbox Code Playgroud)
对于input元素 A,我想复制元素 B 的行为input,但不使用bind-xxx-style 数据绑定属性。
例如,当我123x在 A 中输入内容时,我希望它自动恢复为123B 中的情况。
我已经尝试过StateHasChanged,但我想它不起作用,因为count属性实际上并没有改变。
所以,基本上我需要重新渲染 A 来撤消无效的用户输入,即使状态没有改变。没有魔法我怎么能做到这一点呢bind-xxx?
当然,bind-xxx这很棒,但在某些情况下,可能需要非标准行为,围绕托管事件处理程序(如ChangeEvent.
更新后,为了比较,我可以在 React 中完成以下操作(在线尝试):
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/>
</>
);
}
Run Code Online (Sandbox Code Playgroud)
另外,我可以在 Svelte 中完成此操作,我发现它在概念上非常接近 Blazor(在线尝试)。
<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/>
Run Code Online (Sandbox Code Playgroud)
更新了,澄清一下,我只是想通过处理更改事件来撤消我认为无效的输入,在发生后回顾性输入,而不改变组件的状态本身(counter此处)。
也就是说,没有 Blazor 特定的双向数据绑定、HTML 本机type=number或模式匹配属性。我这里只是以数字格式要求为例;我希望能够撤消任何类似的任意输入。
我想要的用户体验(通过 JS 互操作 hack 完成):https://blazorrepl.telerik.com/wPbvcvvi128Qtzvu03
与其他框架相比,Blazor 中的这一点令人惊讶,而且我无法使用它StateHasChanged来简单地强制重新渲染当前状态的组件。
所以,基本上我需要重新渲染 A 来撤消无效的用户输入,即使状态没有改变。如果没有 bind-xxx 魔法,我该如何做到这一点?
您可以通过更改指令中的值来强制重新创建和重新渲染任何元素/组件@key:
<input @oninput=OnChange value="@count" @key="version" />
Run Code Online (Sandbox Code Playgroud)
<input @oninput=OnChange value="@count" @key="version" />
Run Code Online (Sandbox Code Playgroud)
请注意,它将重新渲染整个元素(及其子树),而不仅仅是属性。
您的代码的问题是,组件的 BuildRendrerTree 方法生成完全相同的 RenderTree,因此 diffing 算法在实际 dom 中找不到任何要更新的内容。
那么为什么 @bind 指令有效呢?
注意生成的 BuildRenderTree 代码:
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;
}
}
}
Run Code Online (Sandbox Code Playgroud)
诀窍是,@bind 指令添加了:
__builder.SetUpdatesAttributeName("value");
Run Code Online (Sandbox Code Playgroud)
您现在无法在 EventCallback 的标记中执行此操作,但有一个未解决的问题: https: //github.com/dotnet/aspnetcore/issues/17281
但是,您仍然可以创建 Component 或 RenderFragment 并__builder手动编写代码。
| 归档时间: |
|
| 查看次数: |
2048 次 |
| 最近记录: |