0
votes

I have a requirement to load new data into my form when a user clicks the up or down arrow. And the expectation is whatever cell has the focus, retains the focus after new data is loaded.

The following code can trap the keyboard input, and load the correct record into my form using data binding. However I find, the form loses focus as soon as new data gets bound.

MyComponent.razor:

<span @onkeypress="@KeyHandler" @onkeydown="@KeyHandler">
    <EditForm Model="@CurrentRecord">
        <DataAnnotationsValidator />
        <ValidationSummary />
        @Content
    </EditForm>
</span>

@code {
[Parameter]
public RenderFragment Content { get; set; }
public List<MyModel> Data { get; set; } = new List<MyModel>();
private int currentIndex;

protected async Task KeyHandler(KeyboardEventArgs e)
{
    if (e.Key == "DownArrow") await LoadNextRecord();
}

async Task LoadNextRecord()
{
    CurrentRecord = Data.ElementAt(++currentIndex);
    // Form loses focus here!!
}
}

Now I can implement a simple javascript to focus the first element when the data is re-binded. But my requirement is to keep the focus where it is. I could not find a generic way to get the focused element and then re-focus without having to assign an Id or an @ref for every element in my form.

Is there another way to solve this problem?

1
When you say "the form loses focus as soon as new data gets bound" you mean the input loses focus, right? - Vencovsky
As per my code above, when I set the CurrentRecord, the form is re-rendered. When that happens, my input loses focus. I want to keep focus where it is (or put the focus back to the same cell) AfterRenderAsync for example - Jason D
Not sure why that happens, but I think that happens because you are using a RenderFragment, try passing the form directly inside MyComponent. Probably it will work - Vencovsky
I tried putting the form content directly into MyComponent (and comment out the @Content). Behaviour is same. When form re-renders, focus goes away. - Jason D
Try not changing the CurrentRecord directly, but only it's properties. AutoMapper would be usefull in this case. I think it's losing focus because it's changing the reference of CurrentRecord, but if you only change the properties values, maybe it won't. - Vencovsky

1 Answers

1
votes

Is there another way to solve this problem?

No, there isn't... There is no generic way to do that, not right now, and probably not in the near future.

Currently, at most you can set the focus of an input in Blazor, as follows:

<button @onclick="() => textInput.FocusAsync()">Set focus</button>
<input @ref="textInput"/>

Note that the code snippet above works in Asp.Net Core 5.0 (preview) only.

The only solution I can propose is to devise a way to discover what input element had the focus last, and then re-focus it from the OnAfterRender/Async methods. You'll have to apply the @ref directive to each of your input elements or the id attribute. There is no other way to do that.