Blazor's FocusAsync doesn't always set focus

Viewed 321

I can't make .NET 6's Blazor AsyncFocus method work.

Near as I can tell, AsyncFocus only works when a component value isn't null. The model ensures the component value isn't null.

Model:

namespace BlazorAppWithService.Models;

public class TagModel
{
    public string? TagName { get; set; } = "";
}

Blazor markup:

<EditForm Model="@newTag" OnValidSubmit="AddTag">
    <InputText @ref="ele "@bind-Value="newTag.TagName"></InputText>
    <button type="submit">Add tag</button>
</EditForm>

This code:

@code {
    InputText ele;

    private List<TagModel> tagList = new List<TagModel>();

    private TagModel newTag = new TagModel();


    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        if (firstRender) 
            if (ele.Element != null)
                await ele.Element.Value.FocusAsync();
    }

    private async void AddTag()
    {
        tagList.Add(newTag);
        newTag = new TagModel();
        if (ele.Element != null)
        {
            await ele.Element.Value.FocusAsync();            
        }
    }
}

This code correctly sets focus on the first render, but the component is not focused when the AddTag() method is called.

FWIW, this doesn't position cursor either:

<button @onclick="() => ele.Element.Value.FocusAsync()" type="submit">Add tag</button>

Blazor: Making the hard things easy and the easy things hard!

1 Answers

To explain what is going on FocusAsync under the hood is calling JavaScript, but Blazor is rerendering after this happens. Therefore, you need to call FocusAsyncin the OnAfterRenderAsync method like so:

protected override async Task OnAfterRenderAsync(bool firstRender)
{
    await ele.Element.Value.FocusAsync();
}

Probably it isn't very apparent that FocusAsync uses JavaScript, but this is the reason why it doesn't work. There is this excerpt from the documentation about calling JavaScript:

To delay JavaScript interop calls until a point where such calls are guaranteed to work, override the OnAfterRender{Async} lifecycle event. This event is only called after the app is fully rendered.

source

Related