Description
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:
- 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
viaNavigationManager.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.
- 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 withOnParametersSet
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.
- 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 hookpopstate
,href
s 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)