Atr*_*rox 6 c# throttling debounce blazor blazor-server-side
如何在Blazor中为事件(OnInput)添加延迟?
例如,如果用户正在文本字段中输入,而您要等到用户完成输入。
Blazor.Templates :: 3.0.0-preview8.19405.7
码:
@page "/"
<input type="text" @bind="Data" @oninput="OnInputHandler"/>
<p>@Data</p>
@code {
public string Data { get; set; }
public void OnInputHandler(UIChangeEventArgs e)
{
Data = e.Value.ToString();
}
}
Run Code Online (Sandbox Code Playgroud)
dan*_*era 36
您的问题没有单一的解决方案。以下代码只是一种方法。看一看并根据您的要求进行调整。代码在每个 上重置一个计时器keyup,只有最后一个计时器引发OnUserFinish事件。记得通过实现来处理定时器IDisposable
@using System.Timers;
@implements IDisposable;
...
<input type="text" @bind-value="Data" @bind-value:event="oninput"
@onkeyup="@HandleKeyUp"/>
<p>@Data2</p>
@code {
public string Data { get; set; }
public string Data2 { get; set; }
private Timer aTimer;
protected override void OnInitialized()
{
aTimer = new Timer(1000);
aTimer.Elapsed += OnUserFinish;
aTimer.AutoReset = false;
}
void HandleKeyUp(KeyboardEventArgs e)
{
// remove previous one
aTimer.Stop();
// new timer
aTimer.Start();
}
private void OnUserFinish(Object source, ElapsedEventArgs e)
{
InvokeAsync( () =>
{
Data2 = $"User has finished: {Data}";
StateHasChanged();
});
}
void IDisposable.Dispose()
{
aTimer?.Dispose();
}
}
Run Code Online (Sandbox Code Playgroud)
此代码用例的一个示例是避免后端请求,因为在用户停止输入之前不会发送请求。
这个答案是之前答案之间的中间地带,即在DIY和使用成熟的反应式 UI 框架之间。
它利用了强大的Reactive.Extensions库(又名Rx),在我看来,这是在正常情况下解决此类问题的唯一合理方法。
安装 NuGet 包后,System.Reactive您可以在组件中导入所需的命名空间:
@using System.Reactive.Subjects
@using System.Reactive.Linq
Run Code Online (Sandbox Code Playgroud)
Subject在您的组件上创建一个字段,作为输入事件和Observable管道之间的粘合剂:
@code {
private Subject<ChangeEventArgs> searchTerm = new();
// ...
}
Run Code Online (Sandbox Code Playgroud)
Subject与您的连接input:
<input type="text" class="form-control" @oninput=@searchTerm.OnNext>
Run Code Online (Sandbox Code Playgroud)
最后,定义Observable管道:
@code {
// ...
private Thing[]? things;
protected override async Task OnInitializedAsync() {
searchTerm
.Throttle(TimeSpan.FromMilliseconds(200))
.Select(e => (string?)e.Value)
.Select(v => v?.Trim())
.DistinctUntilChanged()
.SelectMany(SearchThings)
.Subscribe(ts => {
things = ts;
StateHasChanged();
});
}
private Task<Thing[]> SearchThings(string? searchTerm = null)
=> HttpClient.GetFromJsonAsync<Thing[]>($"api/things?search={searchTerm}")
}
Run Code Online (Sandbox Code Playgroud)
上面的示例管道将...
ChangeEventArgs, 中选择键入的值things,如果您的标记中有类似以下内容,您会在键入时看到它正在更新:
@foreach (var thing in things) {
<ThingDisplay Item=@thing @key=@thing.Id />
}
Run Code Online (Sandbox Code Playgroud)
您应该像这样正确处理事件订阅:
@implements IDisposable // top of your component
// markup
@code {
// ...
private IDisposable? subscription;
public void Dispose() => subscription?.Dispose();
protected override async Task OnInitializedAsync() {
subscription = searchTerm
.Throttle(TimeSpan.FromMilliseconds(200))
// ...
.Subscribe(/* ... */);
}
}
Run Code Online (Sandbox Code Playgroud)
Subscribe()实际上返回一个IDisposable你应该与你的组件一起存储和处理的。但千万不能使用using它,因为这会过早破坏订阅。
有些事情我还没有弄清楚:
StateHasChanged()?Subscribe()直接调用和绑定到Observable标记内部?asyncSubject? Rx 支持Observable从 C# Events创建s ,但如何获取oninput事件的 C# 对象?