1

Basically the scenario like in How to tell if browser/tab is active, but with Blazor Web Assembly. The idea in my case specifically is that when the Blazor app comes to the front, re-load data from the back end.

I haven't found any links to this specific scenario (most all Blazor & focus is about the now-solved setfocus problem). What is the best way to go about this when you have multiple pages in your Blazor app, and only one or a few of these want to react to focus/blur/document.hidden? JSInvokable from static to instance is going to be problematic if some pages don't have the functionality but others do. (my initial idea: a JS func "registerFocusEvent('mycallback')" that stores the string, on when focus happens, only call back to .NET when string is set)

Christoph Wille
  • 311
  • 3
  • 16

2 Answers2

1

I don't know of a way other then JS interop. So what you can do ( based my work on the docs here and here:

Add a JS Invokable update method in your razor component (i'm using Index.razor). Note, this is a bit weird, as the JSInvokable method needs to be static.

private static Func<Task> updateAsyncAction;
protected override void OnInitialized()
{
    base.OnInitialized();
    updateAsyncAction = async () => { await InvokeAsync(StateHasChanged); }; // triggers a rerender.
}

[JSInvokable]
public static async Task Update()
{
    await updateAsyncAction.Invoke();
}

Add a caller in a e.g. wwwroot\Interop.js file:

window.IndexFunctions = {
    EnableOnFocusUpdate: function () {
        window.addEventListener("focus", function () {
            DotNet.invokeMethodAsync('{APP ASSEMBLY}', 'Update'); // The placeholder {APP ASSEMBLY} is the app's app assembly name.
        });
    }
}

In index.html, add the script before the closing body tag using

<script src="Interop.js"></script>

Finally, you have to enable the js event listener in the razor page

@inject IJSRuntime JS
...
protected override async Task OnAfterRenderAsync(bool firstRender)
{
    await base.OnAfterRenderAsync(firstRender);
    if (firstRender)
    {
        await JS.InvokeVoidAsync("IndexFunctions.EnableOnFocusUpdate");
    }
}

However, you need to click the blazor window once for this to start working... so maybe some more magic is needed.

edit: As noted in the comments, Update will remain registered even if you navigate away from the page, thus rerendering on focus. I'm not seeing an exception though, so in WASM it seems the object/instance is not destroyed.

It would be nice if a razor component would have some sort of OnNavigateAway or destructor, which we could use to remove the instance from updateAsyncAction. The best thing we can now do is do another JSInterop call using removeEventListener. I will maybe ask this on the Blazor github.

JHBonarius
  • 10,824
  • 3
  • 22
  • 41
  • Haven't tried it yet, but... if I navigate away from that Index, isn't the listener kept around? To stick with the boilerplate, if I go to counter, and do switch tab there, where would the call go to? – Christoph Wille Feb 25 '21 at 19:06
  • @christophwille good point! Need more code to remove the event listener. Would be nice to have a native blazor solution – JHBonarius Feb 25 '21 at 19:54
0

I have found a ready-made solution in https://github.com/zxyao145/BQuery/

Christoph Wille
  • 311
  • 3
  • 16