該賞金到期in 4天。此問題的答案有資格獲得 500聲望獎勵。 noseratio正在從信譽良好的來源尋找答案:
我想了解這是否是 Blazor 的當前限制(可能值得解決),或者除了現有的兩個答案之外,是否有更好的方法來解決這個問題。
在 Blazor 中,如何撤消無效的用戶輸入,而不更改組件的狀態以觸發重新渲染?
這是一個簡單的 Blazor 計數器示例(在線試用):
<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 ;
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;
}
// this doesn't work
e.Value = count.ToString();
// this doesn't work either (no rererendering):
StateHasChanged();
}
}
}
對于input元素 A,我想復制input元素 B的行為,但不使用bind-xxx-style 資料系結屬性。
例如,當我123x在 A 中輸入時,我希望它123自動恢復,就像 B 發生的那樣。
我試過了,StateHasChanged但它不起作用,我想,因為count屬性實際上并沒有改變。
所以,基本上我需要重新渲染 A 以撤消無效的用戶輸入,即使狀態沒有改變。如果沒有bind-xxx魔法,我怎么能做到這一點?
當然,bind-xxx很好,但在某些情況下,可能需要非標準行為,圍繞托管事件處理程式(如ChangeEvent.
更新,為了比較,以下是我在 React 中的做法(在線嘗試):
function App() {
let [count, setCount] = useState(0);
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 非常接近(在線試用)。請注意,Svelte 也有bind:value一個自動系結選項,類似于 Blazor。
<script>
let count = 0;
const handleClick = () => count ;
const handleChange = async 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/>
B: <input bind:value={count} type=number/><br/>
uj5u.com熱心網友回復:
這是您的代碼的修改版本,可以執行您想要的操作:
@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. 就 Renderer 而言,它沒有改變,所以它不需要更新 DOM 的那一點。我們在渲染器 DOM 和實際 DOM 之間存在不匹配!
OnChangeasync Task現在更改為:
- 獲取舊值的副本
- 如果新值是一個數字,則更新
count。 - 如果不是數字
- 設定
count為另一個值 - 在這種情況下為零。 - 產量。Blazor 組件事件處理程式呼叫
StateHasChanged并產生。這使渲染器執行緒有時間為其佇列提供服務并重新渲染。輸入瞬間為零。 - 設定
count為舊值。 - 回傳 任務完成。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)運行OnChange。它開始同步運行。
如果isNewValue為 false,則設定count為 0,然后通過Task.Yield將未完成的任務傳遞回 BCEH 來讓步。然后,這可以進行并運行StateHasChanged將渲染片段排隊到渲染器的佇列中。請注意,它實際上并不渲染組件,只是將渲染片段排入佇列。此時 BCEH 正在占用執行緒,因此渲染器實際上無法為其佇列提供服務。然后它會檢查它task是否已完成。
如果 BCEH 完成,則渲染器會獲得一些執行緒時間并渲染組件。
If it's still running - it will be as we've kicked it to the back of the thread queue with Task.Yield - BCEH awaits it and yields. The Renderer gets some thread time and renders the component. OnChange then completes, BCEH gets a completed Task, stacks another render on the Render's queue with a call to StateHasChanged and completes. The Renderer, now with thread time services it's queue and renders the component a second time.
Note some people prefer to use Task.Delay(1), because there's some discussion on exactly how Task.Yield works!
uj5u.com熱心網友回復:
以下是我想出的(在線嘗試)。我希望ChangeEventArgs.Value雙向作業,但事實并非如此。沒有它,我只能想到這個JS.InvokeVoidAsynchack:
@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只是作業,所有這些都不是必需的,就像使用 Svelte 一樣。我在 ASP.NET repo 中將此作為Blazor 問題提出,希望它可能會引起一些注意。
轉載請註明出處,本文鏈接:https://www.uj5u.com/caozuo/361687.html
標籤:C# 网站 。网 西装外套 blazor-webassembly
上一篇:在對話框拖動上更改游標
