I'm experimenting with Blazor. I want to make a hidden input box visible, then set the focus to the input box.
Here's the test page:
@page "/test"
<div>
<button class="btn btn-success" @onclick="JumpToInputBox">Jump to Inputbox</button>
<button class="btn btn-primary" @onclick="@HideInputBox">Hide Inputbox</button>
</div>
<p> </p>
<form>
<input type="text" id="textinput" class=@InputBoxClass @ref=InputBox @bind="InputValue" />
</form>
<p> </p>
<p>Input: @InputValue</p>
My first, naive, attempt didn't work. Although it rendered the input box visible, it didn't set the focus to it. Here's that version of the code:
@code {
private string? InputValue { get; set; }
private ElementReference InputBox;
private bool InputBoxIsVisible { get; set; } = false;
private string InputBoxClass => InputBoxIsVisible ? "textbox-large show" : "textbox-large hide";
private async Task JumpToInputBox()
{
InputBoxIsVisible = true;
await InputBox.FocusAsync();
}
private void HideInputBox()
{
InputValue = null;
InputBoxIsVisible = false;
}
}
where the two CSS classes are:
.show {
visibility: visible;
}
.hide {
visibility: hidden;
}
I assume this didn't work because changing the input box class to make the input box visible caused the the page to re-render, and the JumpToInputBox
method was trying to set the focus before the page finished rendering.
I came across the following article that describes a way to get around this problem. The fix is to pass actions that have to happen after rendering into the OnAfterRender
method to execute:
https://swimburger.net/blog/dotnet/how-to-run-code-after-blazor-component-has-rendered
Based on this, I changed my code:
@code {
private List<Func<Task>> _asyncActionsToRunAfterRender = new List<Func<Task>>();
protected override async Task OnAfterRenderAsync(bool firstRender)
{
foreach (var asyncActionToRun in _asyncActionsToRunAfterRender)
{
await asyncActionToRun();
}
_asyncActionsToRunAfterRender.Clear();
await base.OnAfterRenderAsync(firstRender);
}
private string? InputValue { get; set; }
private ElementReference InputBox;
private bool InputBoxIsVisible { get; set; } = false;
private string InputBoxClass => InputBoxIsVisible ? "textbox-large show" : "textbox-large hide";
private void JumpToInputBox()
{
InputBoxIsVisible = true;
_asyncActionsToRunAfterRender.Add(() => InputBox.FocusAsync().AsTask());
}
private void HideInputBox()
{
InputValue = null;
InputBoxIsVisible = false;
}
}
This now works but it seems a complex and messy way to wait until the page has rendered before calling await InputBox.FocusAsync()
. Is there a cleaner way of waiting in the middle of a method until the page has rendered?