Estou tentando criar um controle de entrada de pesquisa simples com funcionalidade de rejeição baseada em um temporizador.
Search.razor
:
<input type="search" placeholder="Search" @oninput=@((e) => SearchChanged(e)) />
@code {
[Parameter]
public EventCallback<string> OnSearchChanged { get; set; }
private System.Timers.Timer? _debounceTimer;
string Text = string.Empty;
public void SearchChanged(ChangeEventArgs e)
{
Text = e.Value == null ? string.Empty : e.Value.ToString()!;
// Debounce search
_debounceTimer?.Stop();
_debounceTimer?.Dispose();
_debounceTimer = new System.Timers.Timer(200);
_debounceTimer.Elapsed += DebounceTimerElapsed;
_debounceTimer.Enabled = true;
_debounceTimer.Start();
}
private void DebounceTimerElapsed(object? sender, EventArgs e)
{
_debounceTimer?.Dispose();
OnSearchChanged.InvokeAsync(Text); // <<< Dispatcher exception
}
}
Acho que faz sentido incluir a funcionalidade Debounce com este componente para não precisar repeti-la para cada pai. Mas não consigo usar um EventCallback para informar ao componente pai que o temporizador expirou. Isso lança a The current thread is not associated with the Dispatcher
exceção infame.
Esta página na Blazor University explica que eventos não-UI, como Timer.Elapsed
spawn em outro thread. O Timer é de fato o culpado porque obviamente funciona se eu ligar OnSearchChanged.InvokeAsync()
diretamente de SearchChanged
. Mas InvokeAsync()
não parece sincronizar de volta com o thread da interface do usuário neste caso. No entanto, acho que deveria haver uma maneira simples de fazer isso funcionar.
Existe uma maneira (simples) de informar um componente pai que um temporizador decorreu em um componente filho?