我在Blazor wasm中有一个主-详细页面:一个包含项目列表的左侧面板和一个包含所选项目的右侧面板.

一个示例(ASCII生成here),其中 Select 了项目2:

       MASTER                        DETAIL
+--------------------+----------------------------------------+
| item 1             |                                        |
|                    |                                        |
|                    |               item 2                   |
+--------------------+                                        |
|░░░░░░░░░░░░░░░░░░░░|                                        |
|░item 2░░░░░░░░░░░░░|                                        |
|░░░░░░░░░░░░░░░░░░░░|                                        |
|░░░░░░░░░░░░░░░░░░░░|                                        |
+--------------------+                                        |
| item 3             |                                        |
|                    |                                        |
|                    |                                        |
+--------------------+                                        |
| item 4             |                                        |
|                    |                                        |
|                    |                                        |
+--------------------+----------------------------------------+
               https://www.example.com/items/2

URL:

  • /items表示显示不带选定内容的项目的页面
  • /items/2,用于显示选中了项目2的项目的页面

我成功地做到了这一切,使用了以下方法:

  • 该页面有一个<Master />组件(左)和一个<Detail Id=_selectedId />组件(右)
  • 当在主组件(左侧)中单击项时,它会引发一个带有Id
  • 页面处理该事件,并将id传递给详图构件(右)
  • 详图构件(右侧)显示新项目

我还希望浏览器的位置从/items更新到/items/2,所以我使用了:NavigationManager.NavigateTo("/items/" + id).But that reloads all components.

解决方法:

  • 我还没有找到一种在不重新加载页面的情况下更新浏览器位置的方法(尽管我可以用Java脚本来做到这一点).即使这是可能的,我也不确定这是一个好方法.
  • 我可以使用查询字符串参数/items?id=10,然后NavigationManager将(我认为)避免重新呈现-但我不能使用这种方法,它必须是URI的一部分.

在Blazor中处理主-细节导航的推荐方式是什么?

推荐答案

为此,您不需要使用布局.

有很多方法可以解决这个问题.解决方案中的复杂程度取决于所需的功能.

这是一个使用天气预报的普通页面版本,演示了一种方法.如果你想要一个香草色的外观,可以创建一个空白布局.

首先,我要向天气预报服务致敬:

public class WeatherForecastService
{
    public IEnumerable<WeatherForecast>? Forecasts { get; private set; }

    public WeatherForecast? Forecast { get; private set; }

    public async Task<bool> GetForecastsAsync()
    {
        if (this.Forecasts is null)
            this.Forecasts = await _getForecastAsync();

        return true;
    }

    public async Task GetForecastAsync(int id)
    {
        // mimic an async fetch
        await Task.Delay(100);
        this.Forecast = Forecasts?.FirstOrDefault(item => item.Id == id);
    }


    private static readonly string[] Summaries = new[]
    {
        "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
    };

    private async Task<IEnumerable<WeatherForecast>> _getForecastAsync()
    {
        // mimic an async fetch
        await Task.Delay(100);
        return Enumerable.Range(1, 5).Select(index => new WeatherForecast
        {
            Id = index,
            Date = DateOnly.FromDateTime(DateTime.Now.AddDays(index)),
            TemperatureC = Random.Shared.Next(-20, 55),
            Summary = Summaries[Random.Shared.Next(Summaries.Length)]
        });
    }
}

查看器组件:

@inject WeatherForecastService ForecastService

@if (ForecastService.Forecast == null)
{
    <p><em>No Record Selected</em></p>
}
else
{
    <div class="container-fluid">
        <div class="row">
            <div class="col-6">Date</div>
            <div class="col-6">@ForecastService.Forecast.Date</div>
        </div>

        <div class="row">
            <div class="col-6">Temperature &deg; C</div>
            <div class="col-6">@ForecastService.Forecast.TemperatureC</div>
        </div>

        <div class="row">
            <div class="col-6">Temperature &deg; F</div>
            <div class="col-6">@ForecastService.Forecast.TemperatureF</div>
        </div>

        <div class="row">
            <div class="col-6">Summary</div>
            <div class="col-6">@ForecastService.Forecast.Summary</div>
        </div>

    </div>
}

@code {
    [Parameter] public int? Id { get; set; }

    private int? currentId;

    protected async override Task OnParametersSetAsync()
    {
        if (this.currentId != Id)
        {
            await ForecastService.GetForecastAsync(this.Id ?? 0);
            this.currentId = this.Id;
        }
    }
}

和List组件:

@inject WeatherForecastService ForecastService

@if (ForecastService.Forecasts == null)
{
    <p><em>Loading...</em></p>
}
else
{
    <table class="table">
        <tbody>
            @foreach (var forecast in this.ForecastService.Forecasts)
            {
                <tr>
                    <td><a href="/fetchdata/@forecast.Id"> @forecast.Date.ToShortDateString()</a></td>
                </tr>
            }
        </tbody>
    </table>
}

@code {

    protected override async Task OnInitializedAsync()
        => await ForecastService.GetForecastsAsync();
}

然后是新的FetchData强:

@page "/fetchdata/{Id:int}"
@page "/fetchdata"

<div class="container-fluid">
    <div class="row">
        <div class="col-4">
            <WeatherList />
            </div>
        <div class="col-8">
            <WeatherViewer Id=this.Id />
        </div>
    </div>

</div>

@code {
    [Parameter] public int? Id { get; set; }
}

注:

  • 所有数据和数据管理都驻留在服务中.
  • 不需要重新加载页面:只需更改参数.因为导航到的是同一页,所以我们在查看器中使用OnParametersSetAsync来判断我们是否需要刷新所选项目.

enter image description here

Csharp相关问答推荐

Rx.Net -当关闭序列被触发时如何聚合消息并发出中间输出?

在WPF.NET 6中使用C++/WinRT组件(但实际上是任何WinRT组件)

为什么我不能更改尚未设置的模拟对象属性的值?

为什么这个Reflection. Emit代码会导致一个DDL ViolationException?

我需要两个属性类吗

如何使用C#Interop EXCEL创建度量衡

由于POST中的应用程序/JWT,出现不支持的内容类型异常

共享暂存/生产环境中Azure事件中心的建议配置

如何在毛伊岛应用程序中完美地同步视图模型和视图的加载?

异步实体框架核心查询引发InvalidOperation异常

将字节转换为 struct 并返回

net中从位图获取坐标和绘制折线

错误CS1061';AuthenticationBuilder';不包含AddOpenIdConnect的定义

使用可空引用类型时C#接口实现错误

如何正确地在VB中初始化类?

如何使用.NET 8.0中新的CompositeFormat类?

C#USB条形码 scanner 在第二次扫描时未写入行尾字符

WPF如何获取有关从一个视图模型更改另一个视图模型的信息

RavenDb:为什么在字符串==空的情况下过滤会过滤得太多?

在C#中通过Matheval使用自定义公式