NavigateTo not immediately showing the new page

Viewed 1963

I have Blazor WebAssembly app with a List and an Edit page.

When I view a list of entities, and click on one to edit, using a NavLink component, browser navigates to the Edit page, displaying my Loader component, until the page is loaded.

That works nicely.

When I click Save on my Edit page, if successful, I navigate back to my List page.

private async Task OnValidSubmitAsync()
{
    this.IsBusy = true;

    var @event =
        await this.EventService.UpdateByIdAsync(
            this.Id,
            this.EventUpdateOptions);

    this.IsBusy = false;

    this.NavigationManager.NavigateTo($"/teams/{this.Event.Team.Id}/events");
}

What I would expect to happen is it would navigate to the List page, displaying my Loader component again and then render the List page.

What happens though, is it stays on the Edit page until the List page is rendered, I never see my Loader component, and it to the user, looks like it is just hanging on the Edit page.

Workaround would be to display my Loader in the OnValidateSubmitAsync, after I am done saving the entity.

I am new to Blazor, still getting the page life cycle down, so it is possible my approach is incorrect. Just a bit confused on what is going on here, and if there is a way to force the List page to show with the Loader.

Hopefully that made some sense.

1 Answers

Based on your comment to @enet's answer, one possible answer is that your list loading page never breaks the list loading logic free from the UI thread, so in essence everything is still running synchronously. There are 2 simple ways you can work around this if this is the case.

Method 1

You can use the OnInitializedAsync override to load the list when the page loads. The important part is that you need to do something in the method ahead of loading your list that will cause the runtime to build a task continuation and return control to the UI thread, and Task.Delay(); works great. So your method looks something like this:

protected override async Task OnInitializedAsync()
{
    // await the delay, breaks the UI thread free from following logic
    await Task.Delay(1);

    // Populate your list with a background task that you await
    // Assumes you already have the list initialized and just need values
    ListValues = await Someservice.GetListValuesAsync();

    // You may not need this call, but if so you might need to invoke it async
    // Doing so will synchronize back to the UI thread 
    await InvokeAsync(StateHasChanged);
}

Method 2

You can use the OnAfterRenderAsync override to load the data. The key point to this is you set your initial UI state to show the loading component as the default action, and then use the method like this:

protected override async Task OnAfterRenderAsync(bool firstRender)
{
    if (firstRender)
    {
        ListValues = await Someservice.GetListValuesAsync();

        // Since you awaited the list population, now you can update UI
        MethodToSwitchUiToListView();

        // this may be needed as well
        await InvokeAsync(StateHasChanged);
    }
}

I tend to prefer using method 2 over method 1, as it allows me to build UI's that are very simple to represent a loading state, and then load multiple sections in parallel and switch the UI as data becomes available. Second, in Blazor Server (which doesn't appear to apply to your case) the OnInitializedAsync method gets called twice, but the OnAfterRender only gets called once. Either way should work though.

Let me know if this helps.

P.S. - The NavigationManager.NavigateTo() method takes an optional second parameter as a boolean as well, to force load the page. If set to true it enforces a forced reload and refresh. I encourage experimenting with that if you haven't already, as it can be useful to navigate back to the current page. An example is for things like complex form resets where it makes more sense to start all over instead of trying to reverse state all over the place, and I've had a few frustrating circumstances that were solved with that simple second parameter. This might help in your case as well.

Related