Skip to content

Blazor Server - NavigationManager push to history #51042

@lofcz

Description

@lofcz

Is there an existing issue for this?

  • I have searched the existing issues

Is your feature request related to a problem? Please describe the problem.

There is no clear way to implement a user-friendly list & detail page pattern. Consider an application consisting of two pages:

/articles
/article/{id}

The /articles endpoint enumerates articles, and then the user opens one. After the user is finished with the article, they navigate back to the /articles endpoint. This will result in the /articles page fetching and rendering the articles again and the vertical scroll position of the page incorrectly reset.

Now the user has to scroll to the place where they left off again. This is a bad user experience.

Currently, our options are:

  1. We can remedy the scrolling issue by storing the scroll position when the navigation is about to happen and restoring it after we are brought back to /articles via NavigationManager.RegisterLocationChangingHandler, NavigationManager.LocationChanged, and some JS.

However, this approach is flawed. In order to scroll back to the correct position we need to wait for the articles to be fetched, rendered and only after that we can scroll.

  1. We can exploit the fact that navigation where only the query string is changed eg. from /articles to /articles?detail={id} doesn't result in the original content being lost and with OnParametersSet implement a switch where we either render the articles or the requested detail. We still need to fix the scroll the same way as in (1) but this time we avoid fetching the articles again hence removing the delay and improving the end-user experience.

However, this way we make a compromise with the URLs. This isn't a sustainable way to solve the issue.

  1. We avoid Blazor's navigation manager altogether and solve the issue with a combination of JS's history API calls. This is fairly hard to get working correctly if we want to intercept only certain navigation events (considering there could be more endpoints and we don't want to get these involved). We can hook popstate, hrefs being clicked, and so on before Blazor's JS is loaded so we can snatch these events as needed.

Again, this is pretty hard and the framework is a hindrance actively fighting against you at that point.

Now consider the articles could have a parameter attached to them, say a number of likes and we want that number to be updated when we navigate back from an article detail to the list.

A real-world example to grasp this concept is simple: navigate to Reddit, scroll a little, open a post, and navigate back. Observe the likes updated, and the scroll position kept intact.

To do this, the ideal way is to hide the content of a container listing the articles when we navigate to the detail of an article, rendering the article itself in another container such as:

<div class="articles"></div>
<div class="articleDetail"></div>

This can be done in quite a few ways in Blazor, however, we can't easily change the URL while preserving the original content, essentially what history.pushState does. If we call this function ourselves, Blazor will avoid intercepting the navigation, see https://github.com/dotnet/aspnetcore/blob/main/src/Components/Web.JS/src/Services/NavigationManager.ts#L184 as to why and how.

Similar issues:
#42443

Describe the solution you'd like

The clearest way to solve this would be to introduce a new API:

+NavigationManager.PushState(string url, string? historyEntryState = null)

Metadata

Metadata

Assignees

No one assigned

    Labels

    area-blazorIncludes: Blazor, Razor ComponentsenhancementThis issue represents an ask for new feature or an enhancement to an existing onefeature-blazor-navigation

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions