Blazor 导航管理器返回?

Ven*_*sky 7 blazor blazor-server-side blazor-client-side

在使用 blazor 时,我希望能够“返回”到我之前所在的页面。

我发现了这个问题,看起来它是一个死胡同?

这个功能太基础了,我不敢相信它不存在。

是否有任何走动可以使此“返回”功能?

请注意,我不能使用window.goBackhistory.goBack因为我的应用程序没有创建任何历史记录,也不应该创建任何历史记录。

“创建”历史记录的唯一方法是使用forceLoad选项,Navigation.NavigateTo但如果我这样做,它会尝试再次加载我的整个应用程序,这很慢,我不想。

小智 32

使用 JavaScript 怎么样?

@inject IJSRuntime JSRuntime

// Go back in browser using Javascript on a Razor Page 
private async Task GoBack()
{
    await JSRuntime.InvokeVoidAsync("history.back");
}
Run Code Online (Sandbox Code Playgroud)

或者,如果不需要绘制按钮并处理事件,则使用标准链接:

<a onclick="history.back();">Return</a>
Run Code Online (Sandbox Code Playgroud)

  • 坏@Community 机器人!这是一个完全可以接受的答案,恕我直言,这是最简单、最优雅的答案,并且直接解决了所提出的问题。这应该是公认的答案。 (3认同)

Pav*_*kov 17

我最终得到了一个稍微改进的解决方案,它包装/封装了 NavigationManager,将所有内容保留在一个位置,并且不依赖于页面或其他东西。它还将历史缓冲区大小保持在某个合理的范围内。

导航.cs

using System;
using System.Collections.Generic;
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Routing;

namespace MyApp
{
    public class Navigation : IDisposable
    {
        private const int MinHistorySize = 256;
        private const int AdditionalHistorySize = 64;
        private readonly NavigationManager _navigationManager;
        private readonly List<string> _history;

        public Navigation(NavigationManager navigationManager)
        {
            _navigationManager = navigationManager;
            _history = new List<string>(MinHistorySize + AdditionalHistorySize);
            _history.Add(_navigationManager.Uri);
            _navigationManager.LocationChanged += OnLocationChanged;
        }

        /// <summary>
        /// Navigates to the specified url.
        /// </summary>
        /// <param name="url">The destination url (relative or absolute).</param>
        public void NavigateTo(string url)
        {
            _navigationManager.NavigateTo(url);
        }

        /// <summary>
        /// Returns true if it is possible to navigate to the previous url.
        /// </summary>
        public bool CanNavigateBack => _history.Count >= 2;

        /// <summary>
        /// Navigates to the previous url if possible or does nothing if it is not.
        /// </summary>
        public void NavigateBack()
        {
            if (!CanNavigateBack) return;
            var backPageUrl = _history[^2];
            _history.RemoveRange(_history.Count - 2, 2);
            _navigationManager.NavigateTo(backPageUrl);
        }

        // .. All other navigation methods.

        private void OnLocationChanged(object sender, LocationChangedEventArgs e)
        {
            EnsureSize();
            _history.Add(e.Location);
        }

        private void EnsureSize()
        {
            if (_history.Count < MinHistorySize + AdditionalHistorySize) return;
            _history.RemoveRange(0, _history.Count - MinHistorySize);
        }

        public void Dispose()
        {
            _navigationManager.LocationChanged -= OnLocationChanged;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

然后,您可以将此类作为单例服务添加到依赖注入并初始化。

程序.cs

using System.Threading.Tasks;
using Microsoft.AspNetCore.Components.WebAssembly.Hosting;
using Microsoft.Extensions.DependencyInjection;

namespace MyApp
{
    public class Program
    {
        public static async Task Main(string[] args)
        {
            var builder = WebAssemblyHostBuilder.CreateDefault(args);
            builder.RootComponents.Add<App>("app");

            builder.Services.AddSingleton<Navigation>();
            // .. other services.
            
            var host = builder.Build();
            await Initialize(host);
            await host.RunAsync();
        }

        private static async Task Initialize(WebAssemblyHost host)
        {
            host.Services.GetService<Navigation>();
            // .. other initialization calls.
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

之后,您可以使用 Inject 指令/属性在任何您想要的地方使用它。

SomePage.cshtml

@page "/SomePage"
@inject Navigation Navigation

<h3>SomePage</h3>

<button @onclick="NavigateBackClick">Navigate Back</button>

@code {
    private void NavigateBackClick()
    {
        Navigation.NavigateBack();
    }
}
Run Code Online (Sandbox Code Playgroud)

SomeService.cs

namespace MyApp
{
    public class SomeService
    {
        private readonly Navigation _navigation;

        public SomeService(Navigation navigation)
        {
            _navigation = navigation;
        }

        public void SomeMethod()
        {
            // ...
            _navigation.NavigateBack();
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

  • `builder.Services.AddSingleton&lt;Navigation&gt;()` 您确定,对于服务器 blazor 来说,这样的单例实际上仅适用于一个用户?(并非同时适用于所有用户) (4认同)
  • 为什么不简单地使用堆栈? (3认同)
  • @JóhannØsterø 在 EnsureSize() 方法中从“堆栈底部”删除 (2认同)

Tay*_*ite 12

我将上面迪奥戈的答案修改为我认为更优雅的解决方案。

首先创建一个BasePageComponent.cs类,继承自该类ComponentBase

// Using statements/namespace go here

[Authorize]
public class BasePageComponent: ComponentBase
{
    [Inject]
    protected NavigationManager _navManager { get; set; }
    [Inject]
    protected PageHistoryState _pageState { get; set; }

    public BasePageComponent(NavigationManager navManager, PageHistoryState pageState)
    {
        _navManager = navManager;
        _pageState = pageState;
    }

    public BasePageComponent()
    {
    }

    protected override void OnInitialized()
    {
        base.OnInitialized();
        _pageState.AddPage(_navManager.Uri);
    }
}
Run Code Online (Sandbox Code Playgroud)

这就是您的每个页面都将继承的内容。它处理注入PageHistoryState服务以及附加新导航的页面。它在实际页面的“幕后”完成这一切。

现在,在给定页面中,您继承自BasePageComponent

@page "/workouts/new"
@inherits BasePageComponent

/* ...RenderFragments/Razor view here...*/

@code {
    /* ...properties here...*/

    // This is an example of how to consume the _navManager and _pageState objects if desired, without any boilerplate code.
    private void OnCancel()
    {
        _navManager.NavigateTo(_pageState.PreviousPage());
    }
}
Run Code Online (Sandbox Code Playgroud)

在我的示例组件中(为了简洁而删除),它向页面历史堆栈添加了一个新元素,除了继承自BasePageComponent. 泰勒


Dio*_*ves 11

你需要的是一个页面历史状态管理器:

对于以下示例,我使用的是 Blazor Wasm,但您也可以在 Blazor Server 中使用此示例。

在客户端应用程序中,我添加了这个类:PageHistoryState:

 公共类 PageHistoryState
    {
        private List<string> previousPages;

        公共 PageHistoryState()
        {
            previousPages = new List<string>();
        }
        public void AddPageToHistory(string pageName)
        {
            previousPages.Add(pageName);
        }

        公共字符串 GetGoBackPage()
        {
            if (previousPages.Count > 1)
            {
                // 你在初始化时添加了一个页面,所以你需要从最后一个返回第二个
                返回 previousPages.ElementAt(previousPages.Count - 2);
            }

            // 无法返回,因为你导航不够
            返回 previousPages.FirstOrDefault();
        }

        public bool CanGoBack()
        {
            返回 previousPages.Count > 1;
        }
    }

然后将此类作为单例添加到服务中:

builder.Services.AddSingleton<PageHistoryState>();

将其注入您的页面:

@inject WasmBlazor.Client.PageHistoryState PageHistoryState
Run Code Online (Sandbox Code Playgroud)

在我的标记中,我检查了是否可以返回页面:

@if (PageHistoryState.CanGoBack())
{
    <a href="@PageHistoryState.GetGoBackPage()">返回</a>
}

我已经覆盖了 OnInitialized()

protected override void OnInitialized()
{
    PageHistoryState.AddPageToHistory("/counter");
    base.OnInitialized();
}

我在“获取数据”页面中做了同样的事情,我可以在不需要 JSInterop 的情况下返回。

  • 由于该服务已在客户端计算机上注册,因此不会发生这种情况。这里的假设是这是 Blazor WASM。 (4认同)
  • 我认为 Stack 就是你想要的 (3认同)
  • 由于这是一个单例,我想它会跟踪所有用户的页面,对吧?因此,如果多个用户使用它,您可能会返回到其他用户之前访问过的页面? (3认同)
  • 在 GetGoBackPage 方法中,您可能想要删除列表中的最后一页 - 或者您应该提供一些其他方法来在返回页面时减少列表。否则,您将无法返回多个页面,在这种情况下,您不妨使用几个变量(currentPage、lastPage)而不是列表。 (2认同)