From d0731c337d1fa73eb65740666ca712a72b472f10 Mon Sep 17 00:00:00 2001 From: Steve Sanderson <SteveSandersonMS@users.noreply.github.com> Date: Fri, 16 Mar 2018 11:19:09 +0000 Subject: [PATCH] Add IUriHelper.NavigateTo --- .../src/Services/UriHelper.ts | 19 ++++++++++++++++--- .../Services/BrowserUriHelper.cs | 11 +++++++++++ .../Services/IUriHelper.cs | 9 ++++++++- .../Tests/RoutingTest.cs | 10 ++++++++++ .../BasicTestApp/RouterTest/Links.cshtml | 8 +++++++- 5 files changed, 52 insertions(+), 5 deletions(-) diff --git a/src/Microsoft.AspNetCore.Blazor.Browser.JS/src/Services/UriHelper.ts b/src/Microsoft.AspNetCore.Blazor.Browser.JS/src/Services/UriHelper.ts index 53442dae621..8e77f1a6b5d 100644 --- a/src/Microsoft.AspNetCore.Blazor.Browser.JS/src/Services/UriHelper.ts +++ b/src/Microsoft.AspNetCore.Blazor.Browser.JS/src/Services/UriHelper.ts @@ -1,6 +1,6 @@ import { registerFunction } from '../Interop/RegisteredFunction'; import { platform } from '../Environment'; -import { MethodHandle } from '../Platform/Platform'; +import { MethodHandle, System_String } from '../Platform/Platform'; const registeredFunctionPrefix = 'Microsoft.AspNetCore.Blazor.Browser.Services.BrowserUriHelper'; let notifyLocationChangedMethod: MethodHandle; let hasRegisteredEventListeners = false; @@ -24,8 +24,7 @@ registerFunction(`${registeredFunctionPrefix}.enableNavigationInteception`, () = const href = anchorTarget.getAttribute('href'); if (isWithinBaseUriSpace(toAbsoluteUri(href))) { event.preventDefault(); - history.pushState(null, /* ignored title */ '', href); - handleInternalNavigation(); + performInternalNavigation(href); } } }); @@ -33,6 +32,20 @@ registerFunction(`${registeredFunctionPrefix}.enableNavigationInteception`, () = window.addEventListener('popstate', handleInternalNavigation); }); +registerFunction(`${registeredFunctionPrefix}.navigateTo`, (uriDotNetString: System_String) => { + const href = platform.toJavaScriptString(uriDotNetString); + if (isWithinBaseUriSpace(toAbsoluteUri(href))) { + performInternalNavigation(href); + } else { + location.href = href; + } +}); + +function performInternalNavigation(href: string) { + history.pushState(null, /* ignored title */ '', href); + handleInternalNavigation(); +} + function handleInternalNavigation() { if (!notifyLocationChangedMethod) { notifyLocationChangedMethod = platform.findMethod( diff --git a/src/Microsoft.AspNetCore.Blazor.Browser/Services/BrowserUriHelper.cs b/src/Microsoft.AspNetCore.Blazor.Browser/Services/BrowserUriHelper.cs index 28cf8e497fb..171679febb9 100644 --- a/src/Microsoft.AspNetCore.Blazor.Browser/Services/BrowserUriHelper.cs +++ b/src/Microsoft.AspNetCore.Blazor.Browser/Services/BrowserUriHelper.cs @@ -104,6 +104,17 @@ namespace Microsoft.AspNetCore.Blazor.Browser.Services throw new ArgumentException($"The URI '{absoluteUri}' is not contained by the base URI '{baseUriPrefix}'."); } + /// <inheritdoc /> + public void NavigateTo(string uri) + { + if (uri == null) + { + throw new ArgumentNullException(nameof(uri)); + } + + RegisteredFunction.InvokeUnmarshalled<object>($"{_functionPrefix}.navigateTo", uri); + } + private static void EnsureBaseUriPopulated() { // The <base href> is fixed for the lifetime of the page, so just cache it diff --git a/src/Microsoft.AspNetCore.Blazor/Services/IUriHelper.cs b/src/Microsoft.AspNetCore.Blazor/Services/IUriHelper.cs index 12e8fd0dade..472b7956f56 100644 --- a/src/Microsoft.AspNetCore.Blazor/Services/IUriHelper.cs +++ b/src/Microsoft.AspNetCore.Blazor/Services/IUriHelper.cs @@ -13,7 +13,7 @@ namespace Microsoft.AspNetCore.Blazor.Services /// <summary> /// Gets the current absolute URI. /// </summary> - /// <returns>The browser's current absolute URI.</returns> + /// <returns>The current absolute URI.</returns> string GetAbsoluteUri(); /// <summary> @@ -44,5 +44,12 @@ namespace Microsoft.AspNetCore.Blazor.Services /// <param name="absoluteUri">An absolute URI that is within the space of the base URI prefix.</param> /// <returns>A relative URI path.</returns> string ToBaseRelativePath(string baseUriPrefix, string locationAbsolute); + + /// <summary> + /// Navigates to the specified URI. + /// </summary> + /// <param name="uri">The destination URI. This can be absolute, or relative to the base URI + /// prefix (as returned by <see cref="GetBaseUriPrefix"/>).</param> + void NavigateTo(string uri); } } diff --git a/test/Microsoft.AspNetCore.Blazor.E2ETest/Tests/RoutingTest.cs b/test/Microsoft.AspNetCore.Blazor.E2ETest/Tests/RoutingTest.cs index 8970de915cd..9b6cfd15768 100644 --- a/test/Microsoft.AspNetCore.Blazor.E2ETest/Tests/RoutingTest.cs +++ b/test/Microsoft.AspNetCore.Blazor.E2ETest/Tests/RoutingTest.cs @@ -110,6 +110,16 @@ namespace Microsoft.AspNetCore.Blazor.E2ETest.Tests Assert.Equal("This is the default page.", app.FindElement(By.Id("test-info")).Text); } + [Fact] + public void CanNavigateProgrammatically() + { + SetUrlViaPushState($"{ServerPathBase}/RouterTest/"); + + var app = MountTestComponent<TestRouter>(); + app.FindElement(By.TagName("button")).Click(); + Assert.Equal("This is another page.", app.FindElement(By.Id("test-info")).Text); + } + public void Dispose() { // Clear any existing state diff --git a/test/testapps/BasicTestApp/RouterTest/Links.cshtml b/test/testapps/BasicTestApp/RouterTest/Links.cshtml index e7bbca5d120..9490da17253 100644 --- a/test/testapps/BasicTestApp/RouterTest/Links.cshtml +++ b/test/testapps/BasicTestApp/RouterTest/Links.cshtml @@ -1,4 +1,6 @@ -<ul> +@inject Microsoft.AspNetCore.Blazor.Services.IUriHelper uriHelper + +<ul> <li><a href="/subdir/RouterTest/">Default</a></li> <li><a href="/subdir/RouterTest/?abc=123">Default with query</a></li> <li><a href="/subdir/RouterTest/#blah">Default with hash</a></li> @@ -7,3 +9,7 @@ <li><a href="/subdir/RouterTest/Other?abc=123">Other with query</a></li> <li><a href="/subdir/RouterTest/Other#blah">Other with hash</a></li> </ul> + +<button onclick=@{ uriHelper.NavigateTo("RouterTest/Other"); }> + Programmatic navigation +</button> -- GitLab