如何动态切换 blazor 页面中的子内容
How to dynamically switch child content in blazor pages
因为我是 Web 开发的绝对初学者,所以我开始研究 Blazor 并学习如何使用它来轻松开始 Web 开发,但现在遇到了一个问题。
我构建了一个主/详细信息页面,该页面使用一个主组件(员工列表)和 2 个不同的详细信息组件(员工只读详细信息视图和员工编辑视图)。
主详情页面使用以下路由:
https://localhost:44344/masterdetail
https://localhost:44344/masterdetail/{id:int}
https://localhost:44344/masterdetail/{id:int}/edit
我努力实现了这些目标:
当用户单击主组件中的列表条目时,这应该像 https://localhost:44344/masterdetail/2
一样显示在 URL 中,然后将员工只读详细信息视图加载到详细信息中面积
当用户单击位于员工只读详细信息视图上的编辑按钮时,主详细信息页面应切换到详细信息区域内的员工编辑视图,并在 URL 中显示喜欢 https://localhost:44344/masterdetail/2/edit
当用户单击位于员工编辑视图上的保存按钮时,主详细信息页面应切换到详细信息区域内的员工只读详细信息视图,并在 URL 中显示喜欢 https://localhost:44344/masterdetail/2
我遇到的问题:
当用户处于只读视图并单击编辑按钮时,我的代码调用 NavigationManager.NavigateTo($"/masterdetail/{Id}/edit");
切换浏览器地址栏中的 URL 但不调用 OnParametersSet()
生命周期主从页面的方法。
如果 [Parameter] Id 没有更改它的值,Blazor 似乎会重用该实例。
当用户在 /masterdetail/{Id}/edit route(通过浏览器地址栏输入)然后点击保存按钮时,也会发生同样的情况。
我在研究问题时学到的东西:
- 我知道我可以使用
NavigationManager.NavigateTo($"/masterdetail/{Id}/edit", true);
像这样调用,但这会导致整个页面刷新
我不确定这是否有必要。
- 我知道我可以在我的子组件中使用
EventCallback<T>
并且
在父主详细信息页面中对这些事件做出反应,但这似乎
就像一个解决方法。
- 我在寻找一种方法来“在 blazor 页面中路由”,然后偶然发现了
跨越诸如“Areas”和“Partial Views”之类的主题,但它看起来
这些都是 MVC 概念。
- 我还发现了一个名为“RouteView”的东西
(https://github.com/aspnet/AspNetCore/blob/2e4274cb67c049055e321c18cc9e64562da52dcf/src/Components/Components/src/RouteView.cs)
这是一个 Blazor 组件,但我没有运气将它用于我的
目的。
这是一个显示问题的简化示例:
- 在 Visual Studio
中创建一个新的 "Blazor App" 项目
- 选择"Blazor Server App"
- 添加一个新的 .razor 文件并将代码片段粘贴到
- 看看评论和代码
- 导航至
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;
}
}
我的设置:
- Visual Studio 2019 16.3.1
- .NET Core 3.0 SDK - Windows x64 安装程序 (v3.0.100)
关于如何在 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);
并且该网站按预期运行并且解决了我的问题:)
因为我是 Web 开发的绝对初学者,所以我开始研究 Blazor 并学习如何使用它来轻松开始 Web 开发,但现在遇到了一个问题。
我构建了一个主/详细信息页面,该页面使用一个主组件(员工列表)和 2 个不同的详细信息组件(员工只读详细信息视图和员工编辑视图)。
主详情页面使用以下路由:
https://localhost:44344/masterdetail
https://localhost:44344/masterdetail/{id:int}
https://localhost:44344/masterdetail/{id:int}/edit
我努力实现了这些目标:
当用户单击主组件中的列表条目时,这应该像
https://localhost:44344/masterdetail/2
一样显示在 URL 中,然后将员工只读详细信息视图加载到详细信息中面积当用户单击位于员工只读详细信息视图上的编辑按钮时,主详细信息页面应切换到详细信息区域内的员工编辑视图,并在 URL 中显示喜欢
https://localhost:44344/masterdetail/2/edit
当用户单击位于员工编辑视图上的保存按钮时,主详细信息页面应切换到详细信息区域内的员工只读详细信息视图,并在 URL 中显示喜欢
https://localhost:44344/masterdetail/2
我遇到的问题:
当用户处于只读视图并单击编辑按钮时,我的代码调用 NavigationManager.NavigateTo($"/masterdetail/{Id}/edit");
切换浏览器地址栏中的 URL 但不调用 OnParametersSet()
生命周期主从页面的方法。
如果 [Parameter] Id 没有更改它的值,Blazor 似乎会重用该实例。
当用户在 /masterdetail/{Id}/edit route(通过浏览器地址栏输入)然后点击保存按钮时,也会发生同样的情况。
我在研究问题时学到的东西:
- 我知道我可以使用
NavigationManager.NavigateTo($"/masterdetail/{Id}/edit", true);
像这样调用,但这会导致整个页面刷新 我不确定这是否有必要。 - 我知道我可以在我的子组件中使用
EventCallback<T>
并且 在父主详细信息页面中对这些事件做出反应,但这似乎 就像一个解决方法。 - 我在寻找一种方法来“在 blazor 页面中路由”,然后偶然发现了 跨越诸如“Areas”和“Partial Views”之类的主题,但它看起来 这些都是 MVC 概念。
- 我还发现了一个名为“RouteView”的东西 (https://github.com/aspnet/AspNetCore/blob/2e4274cb67c049055e321c18cc9e64562da52dcf/src/Components/Components/src/RouteView.cs) 这是一个 Blazor 组件,但我没有运气将它用于我的 目的。
这是一个显示问题的简化示例:
- 在 Visual Studio 中创建一个新的 "Blazor App" 项目
- 选择"Blazor Server App"
- 添加一个新的 .razor 文件并将代码片段粘贴到
- 看看评论和代码
- 导航至
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;
}
}
我的设置:
- Visual Studio 2019 16.3.1
- .NET Core 3.0 SDK - Windows x64 安装程序 (v3.0.100)
关于如何在 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);
并且该网站按预期运行并且解决了我的问题:)