如何动态切换 blazor 页面中的子内容

How to dynamically switch child content in blazor pages

因为我是 Web 开发的绝对初学者,所以我开始研究 Blazor 并学习如何使用它来轻松开始 Web 开发,但现在遇到了一个问题。

我构建了一个主/详细信息页面,该页面使用一个主组件(员工列表)和 2 个不同的详细信息组件(员工只读详细信息视图和员工编辑视图)。

主详情页面使用以下路由:

我努力实现了这些目标:

  1. 当用户单击主组件中的列表条目时,这应该像 https://localhost:44344/masterdetail/2 一样显示在 URL 中,然后将员工只读详细信息视图加载到详细信息中面积

  2. 当用户单击位于员工只读详细信息视图上的编辑按钮时,主详细信息页面应切换到详细信息区域内的员工编辑视图,并在 URL 中显示喜欢 https://localhost:44344/masterdetail/2/edit

  3. 当用户单击位于员工编辑视图上的保存按钮时,主详细信息页面应切换到详细信息区域内的员工只读详细信息视图,并在 URL 中显示喜欢 https://localhost:44344/masterdetail/2

我遇到的问题: 当用户处于只读视图并单击编辑按钮时,我的代码调用 NavigationManager.NavigateTo($"/masterdetail/{Id}/edit"); 切换浏览器地址栏中的 URL 但不调用 OnParametersSet() 生命周期主从页面的方法。 如果 [Parameter] Id 没有更改它的值,Blazor 似乎会重用该实例。

当用户在 /masterdetail/{Id}/edit route(通过浏览器地址栏输入)然后点击保存按钮时,也会发生同样的情况。

我在研究问题时学到的东西:

这是一个显示问题的简化示例:

  1. 在 Visual Studio
  2. 中创建一个新的 "Blazor App" 项目
  3. 选择"Blazor Server App"
  4. 添加一个新的 .razor 文件并将代码片段粘贴到
  5. 看看评论和代码
  6. 导航至 https://localhost:44344/masterdetail/ 并自行尝试
@*Default route for this page when no entry is selected in the master list*@
@page "/masterdetail"
@*Route for this page when an entry is selected in the master list. The detail area should show a readonly view / component*@
@page "/masterdetail/{id:int}"
@*Route for this page when an entry is selected in the master list and the user clicked the edit button in the readonly view / component. The detail area should show a edit view / component*@
@page "/masterdetail/{id:int}/edit"
@using Microsoft.AspNetCore.Components

@inject NavigationManager NavigationManager

<h1>MyMasterDetailPage</h1>

<br />
<br />
<br />

<div>
    <h1>Master Area</h1>

    <ul class="nav flex-column">
        <li class="nav-item px-3">
            <button @onclick=@(mouseEventArgs => ShowListItemDetails(1))>Item 1</button>
        </li>
        <li class="nav-item px-3">
            <button @onclick=@(mouseEventArgs => ShowListItemDetails(2))>Item 2</button>
        </li>
        <li class="nav-item px-3">
            <button @onclick=@(mouseEventArgs => ShowListItemDetails(3))>Item 3</button>
        </li>
    </ul>
</div>

<br />
<br />
<br />

<div>
    <h1>Detail Area</h1>
    @{
        if (_isInEditMode)
        {
            // In the real project a <EmployeeEditComponent></EmployeeEditComponent> is being used here instead of the h2
            <h2>Edit view for item no. @Id</h2>
            <h3>Imagine lots of editable fields here e.g. TextBoxes, DatePickers and so on...</h3>
            <button @onclick=@SaveChanges> save...</button>
        }
        else
        {
            // In the real project a <EmployeeDetailComponent></EmployeeDetailComponent> is being used here instead of the h2
            <h2>ReadOnly view for item no. @Id</h2>
            <h3>Imagine lots of NON editable fields here. Probably only labels...</h3>
            <button @onclick=@SwitchToEditMode> edit...</button>
        }
    }
</div>

@code {
    private bool _isInEditMode;

    [Parameter]
    public int Id { get; set; }

    protected override void OnParametersSet()
    {
        // This lifecycle method is not called if the [Parameter] has already been set as Blazor seems to reuse the instance if the [Parameter] Id has not changed it's value.
        // For example this method is not being called when navigating from /masterdetail/1 to /masterdetail/1/edit

        Console.WriteLine($"Navigation parameters have been set for URI: {NavigationManager.Uri}");
        _isInEditMode = NavigationManager.Uri.EndsWith("edit");
        base.OnParametersSet();
    }

    private void ShowListItemDetails(int id)
    {
        Console.WriteLine($"Showing readonly details of item no. {id}");
        NavigationManager.NavigateTo($"/masterdetail/{id}");
    }

    private void SwitchToEditMode()
    {
        Console.WriteLine("Switching to edit mode...");
        NavigationManager.NavigateTo($"/masterdetail/{Id}/edit");

        // Setting _isInEditMode = true here would work and update the UI correctly.
        // In the real project this method is part of the <EmployeeEditComponent></EmployeeEditComponent> and therefore has no access to _isInEditMode as it belongs to the <MyMasterDetailPage> component.
        // I know that I could create a public EventCallback<MouseEventArgs> OnClick { get; set; } in the <EmployeeEditComponent> and react to that event here in the <MyMasterDetailPage> component but is that really the right way to do this?

        //_isInEditMode = true;
    }

    private void SaveChanges()
    {
        Console.WriteLine("Saving changes made in edit mode and switching back to readonly mode...");
        NavigationManager.NavigateTo($"/masterdetail/{Id}");

        // Setting _isInEditMode = false here would work and update the UI correctly.
        // In the real project this method is part of the <EmployeeDetailComponent></EmployeeDetailComponent> and therefore has no access to _isInEditMode as it belongs to the <MyMasterDetailPage> component
        // I know that I could create a public EventCallback<MouseEventArgs> OnClick { get; set; } in the <EmployeeDetailComponent> and react to that event here in the <MyMasterDetailPage> component but is that really the right way to do this?

        //_isInEditMode = false;
    }
}

我的设置:

关于如何在 blazor 页面内切换子内容的最佳实践/建议是什么?

我在 AspNetCore Github repo 上问了这个问题并得到了答案。

https://github.com/aspnet/AspNetCore/issues/16653

正如“mrpmorris”所说,我更改了以下几行

之前 @page "/masterdetail/{id:int}/edit"

之后 @page "/masterdetail/{id:int}/{displayMode}"


之前 -

之后 [Parameter]<br> public string DisplayMode { get; set; }


之前 _isInEditMode = NavigationManager.Uri.EndsWith("edit");

之后 string.Equals(DisplayMode, "edit", StringComparison.InvariantCultureIgnoreCase);


并且该网站按预期运行并且解决了我的问题:)