【问题标题】:Blazor Navigation Manager Go Back?Blazor 导航管理器返回?
【发布时间】:2020-10-15 02:50:30
【问题描述】:

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

我找到了这个issue,看起来它是死胡同?

这个功能太基础了,我不敢相信它不会退出。

是否有任何方法可以实现“返回”功能?

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

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

【问题讨论】:

    标签: blazor blazor-server-side blazor-client-side


    【解决方案1】:

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

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

    在客户端应用程序中,我添加了此类: 页面历史状态:

     公共类 PageHistoryState
        {
            私有列表 previousPages;
    
            公共页面历史状态()
            {
                previousPages = new List();
            }
            公共无效 AddPageToHistory(字符串页面名称)
            {
                previousPages.Add(pageName);
            }
    
            公共字符串 GetGoBackPage()
            {
                如果 (previousPages.Count > 1)
                {
                    // 你在初始化时添加了一个页面,所以你需要从最后一个返回第二个
                    返回 previousPages.ElementAt(previousPages.Count - 2);
                }
    
                // 无法返回,因为导航不够
                返回 previousPages.FirstOrDefault();
            }
    
            公共布尔 CanGoBack()
            {
                返回 previousPages.Count > 1;
            }
        }
    

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

    builder.Services.AddSingleton();
    

    在你的页面中注入它:

    @inject WasmBlazor.Client.PageHistoryState PageHistoryState
    

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

    @if (PageHistoryState.CanGoBack())
    {
        返回
    }
    

    我已经覆盖了 OnInitialized()

    受保护的覆盖无效 OnInitialized()
    {
        PageHistoryState.AddPageToHistory("/counter");
        base.OnInitialized();
    }
    

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

    【讨论】:

    • 在 GetGoBackPage 方法中,您可能希望删除列表中的最后一页 - 或者您应该在返回页面时提供一些其他方法来减少列表。否则,您将无法返回多个页面,在这种情况下,您不妨使用几个变量(currentPage、lastPage)而不是列表。
    • 好主意,我在想一个队列,所以你可以队列和出队,你怎么看?
    • 是的,类似的东西。
    • 我认为 Stack 是你想要的
    • 因为这是一个单例,我猜它会跟踪所有用户的页面,对吧?因此,如果多个用户使用它,您可能会返回到另一个用户之前访问过的页面?
    【解决方案2】:

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

    首先,创建一个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);
            }
    
        }
    

    这是您的每个页面将继承的内容。它处理注入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());
        }
    }
    
    

    在我的示例组件(为简洁起见)中,它向页面历史堆栈添加了一个新元素,除了从BasePageComponent 继承之外没有任何标记。 泰勒

    【讨论】:

      【解决方案3】:

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

      Navigation.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;
              }
          }
      }
      

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

      Program.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.
              }
          }
      }
      

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

      SomePage.cshtml

      @page "/SomePage"
      @inject Navigation Navigation
      
      <h3>SomePage</h3>
      
      <button @onclick="NavigateBackClick">Navigate Back</button>
      
      @code {
          private void NavigateBackClick()
          {
              Navigation.NavigateBack();
          }
      }
      

      SomeService.cs

      namespace MyApp
      {
          public class SomeService
          {
              private readonly Navigation _navigation;
      
              public SomeService(Navigation navigation)
              {
                  _navigation = navigation;
              }
      
              public void SomeMethod()
              {
                  // ...
                  _navigation.NavigateBack();
              }
          }
      }
      

      【讨论】:

      • 你为什么不简单地使用堆栈?
      • builder.Services.AddSingleton&lt;Navigation&gt;() 你确定,对于服务器 blazor,这样的单例实际上只适用于一个用户吗? (并非同时适用于所有用户)
      • @lealan,没有。我只是为客户端做的。对于服务器端,它应该以某种方式进行修改......;)
      【解决方案4】:

      使用 Javascript 怎么样?

      @inject IJSRuntime JSRuntime ......

      // go back in browser using Javascript on a Razor Page 
      private async void GoBack()
      {
          await JSRuntime.InvokeVoidAsync("history.back");
      }
      

      【讨论】:

      • 正如目前所写,您的答案尚不清楚。请edit 添加其他详细信息,以帮助其他人了解这如何解决所提出的问题。你可以找到更多关于如何写好答案的信息in the help center
      猜你喜欢
      • 2022-01-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-01-22
      • 2023-03-31
      • 2015-10-28
      • 1970-01-01
      • 2023-01-18
      相关资源
      最近更新 更多