-
Notifications
You must be signed in to change notification settings - Fork 10.5k
Add NavigationManager.GetUriWithHash() extension method #64615
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
|
Greetings! You've submitted a PR that modifies code that is shared with https://github.com/dotnet/runtime . Please make sure you synchronize this code with the changes in that repo! |
Co-authored-by: javiercn <6995051+javiercn@users.noreply.github.com>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
This PR adds a new GetUriWithHash() extension method to NavigationManager to address a longstanding issue with hash fragment URLs in Blazor applications. When a <base> element is used in the HTML document, relative hash URLs resolve against the base URI rather than the current document location, making it difficult to create in-page navigation links. The new API solves this by constructing absolute URIs with hash fragments relative to the current location.
Key Changes:
- Added
NavigationManager.GetUriWithHash()extension method that adds, updates, or removes hash fragments from the current URI - Comprehensive XML documentation with usage examples
- Full test coverage including edge cases for hash manipulation
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated no comments.
| File | Description |
|---|---|
src/Components/Components/src/NavigationManagerExtensions.cs |
Implements the new GetUriWithHash() method with proper null checking, hash prepending logic, and URI manipulation |
src/Components/Components/test/NavigationManagerTest.cs |
Adds comprehensive test coverage for the new method including hash addition, replacement, removal, and null parameter handling |
src/Components/Components/src/PublicAPI.Unshipped.txt |
Documents the new public API surface for tracking purposes |
|
|
||
| /// <summary> | ||
| /// Returns a URI constructed from <see cref="NavigationManager.Uri"/> with a hash | ||
| /// added, updated, or removed. | ||
| /// </summary> | ||
| /// <param name="navigationManager">The <see cref="NavigationManager"/>.</param> | ||
| /// <param name="hash">The hash string. If empty or null, the hash will be removed from the URI.</param> | ||
| /// <returns>The URI with the specified hash.</returns> | ||
| /// <remarks> | ||
| /// <para> | ||
| /// If <paramref name="hash"/> does not start with <c>#</c>, then <c>#</c> will be prepended. | ||
| /// </para> | ||
| /// <para> | ||
| /// This method is useful when the document's <c>baseURI</c> differs from its location, | ||
| /// such as when a <c><base></c> element is used, since relative hash URLs are resolved | ||
| /// relative to the <c>baseURI</c>. | ||
| /// </para> | ||
| /// <example> | ||
| /// <code> | ||
| /// @inject NavigationManager Nav | ||
| /// <a href="@Nav.GetUriWithHash("section1")">Go to section 1</a> | ||
| /// </code> | ||
| /// </example> | ||
| /// </remarks> | ||
| public static string GetUriWithHash(this NavigationManager navigationManager, string? hash) | ||
| { | ||
| ArgumentNullException.ThrowIfNull(navigationManager); | ||
|
|
||
| var uri = navigationManager.Uri; | ||
| var hashStartIndex = uri.IndexOf('#'); | ||
|
|
||
| // Get URI without the existing hash | ||
| var uriWithoutHash = hashStartIndex < 0 ? uri : uri.Substring(0, hashStartIndex); | ||
|
|
||
| if (string.IsNullOrEmpty(hash)) | ||
| { | ||
| return uriWithoutHash; | ||
| } | ||
|
|
||
| // Ensure hash starts with '#' | ||
| if (hash[0] != '#') | ||
| { | ||
| return string.Concat(uriWithoutHash, "#", hash); | ||
| } | ||
|
|
||
| return string.Concat(uriWithoutHash, hash); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@copilot can you search on the docs and in other helpers on the repo how to do this with minimal allocations? (Using the Span APIs)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Refactored to use string.Create for minimal allocations (e49d709). The implementation now:
- Pre-calculates the exact buffer length needed
- Uses
string.Createwith the existing strings and length values - Copies data using Span APIs directly into the pre-allocated buffer
- Only creates one string allocation for the result
Co-authored-by: javiercn <6995051+javiercn@users.noreply.github.com>
|
@javiercn, it happened again. Copilot is producing descriptions with random issue number next to "fixes" keyword. This one has nothing to do with 41773 (I won't be linking it to avoid confusion). |
| [InlineData("scheme://host/path?query=value#oldHash", "section1", "scheme://host/path?query=value#section1")] | ||
| [InlineData("scheme://host/path#oldHash", "newHash", "scheme://host/path#newHash")] | ||
| [InlineData("scheme://host/path#old", "#new", "scheme://host/path#new")] | ||
| public void GetUriWithHash_AddsOrReplacesHash(string baseUri, string hash, string expectedUri) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
None of these tests really checks base uri different than root. In the issue we had an example of a case with <base href="/subdir/">, we should test it.
| @@ -1 +1,2 @@ | |||
| #nullable enable | |||
| static Microsoft.AspNetCore.Components.NavigationManagerExtensions.GetUriWithHash(this Microsoft.AspNetCore.Components.NavigationManager! navigationManager, string? hash) -> string! | |||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't see a point of hash being nullable.
- name
GetUriWithHashdoes not make sense then, we don't get uri with hash calling this method. You see codenavigationManager.GetUriWithHash()and you expect some hash to be retuned by that method. - if we really want to cheat the system and get the uri without hash, we can pass
String.Emptyto that param
Add NavigationManager.GetUriWithHash() extension method
Add
GetUriWithHash()to construct URIs with hash fragments relative to current locationDescription
Relative hash URLs resolve against
baseURI, not the document location. When using<base href="/subdir/">, a link like<a href="#section1">navigates to/subdir/#section1instead of the current page's hash.API Addition:
Behavior:
#if not present in inputnullor empty string passedImplementation:
string.Createwith Span APIs for minimal allocationsUsage:
Fixes #53499
Original prompt
NavigationManager.GetUriWithHash()#53499💬 We'd love your input! Share your thoughts on Copilot coding agent in our 2 minute survey.