我想在 Blazor WebAssembly 中创建一个简单的可扩展表。我添加了一些 HTML 代码,如下所示:
<table class="table table-hover">
<thead>
<tr>
<th>#</th>
<th>User</th>
<th>Date</th>
<th>Status</th>
<th>Reason</th>
</tr>
</thead>
<tbody>
<tr data-widget="expandable-table" aria-expanded="false">
<td>183</td>
<td>John Doe</td>
<td>11-7-2014</td>
<td>Approved</td>
<td>Lorem Ipsum is simply dummy text</td>
</tr>
<tr class="expandable-body">
<td colspan="5">
<p>
Lorem Ipsum is simply dummy text
</p>
</td>
</tr>
</tbody>
</table>
如果我点击包含详细信息的行,它也总是会折叠。有没有一种不使用外部组件的简单方法来实现它?如果没有,您推荐什么组件?
我所说的“可扩展表”的一个例子是这里。
我想出了一个基于来自 Blazor 服务器模板的默认“WeatherForecast”的解决方案。 基本思想是在模型中放置一个用于表示表数据的标志。
WeatherForecast.cs
public class WeatherForecast
{
public DateTime Date { get; set; }
public int TemperatureC { get; set; }
public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
public string Summary { get; set; }
//new fields added below
public bool IsRowExpanded { get; set; } = false;
public string ExpandableContent { get; set; } = "Lorem Ipsum";
}
FetchData.razor
<table class="table">
<thead>
<tr>
<th>Date</th>
<th>Temp. (C)</th>
<th>Temp. (F)</th>
<th>Summary</th>
</tr>
</thead>
<tbody>
@foreach (var forecast in forecasts)
{
<tr @onclick="() => forecast.IsRowExpanded = !forecast.IsRowExpanded">
<td>@forecast.Date.ToShortDateString()</td>
<td>@forecast.TemperatureC</td>
<td>@forecast.TemperatureF</td>
<td>@forecast.Summary</td>
</tr>
if (forecast.IsRowExpanded)
{
<tr>@forecast.ExpandableContent</tr>
}
}
</tbody>
</table>
当您单击一行(例如第二行)时,它将如下所示:
使用 Bootstrap,内置的 jQuery/JavaScript 代码不会像传统 MVC 页面那样开箱即用。
如果您想展开/折叠行,Blazor 有两个选项。
通过将 IJSRuntime 注入页面并调用
InvokeAsync()
来调用 Bootstrap jQuery/JavaScript。
编写您自己的展开/折叠组件并将每一行包含在其中 - 就像
// CollapsibleTableRow.
<tr @onclick=Toggle>
@if(_show)
{
@ChildContent
}
</tr>
@code
{
[Parameter] public RenderFragment ChildContent { get; set; }
private bool _show { get; set; } = false;
private void Toggle()
{
_show = !_show;
}
}
使用
<table>
<tbody>
@foreach(var item in items)
{
<CollapsibleTableRow>
<td>@item.Thing</td>
<td>@item.Thing2</td>
</CollapsibleTableRow>
}
</tbody>
</table>
更新了答案。 我添加了一个小类以使其更容易。 神奇之处在于我所说的“状态载体变量”,它允许您将打开状态设为
Person
类的属性,而不是添加单独的列表来跟踪哪些行已打开或未打开。 打字花了一些时间,所以如果您喜欢的话请记得标记为“已回答”。
@page "/collapse"
<table class="table">
<thead>
<tr style="cursor:pointer;">
<th>#</th>
<th>User</th>
<th>Date</th>
<th>Status</th>
<th>Reason</th>
</tr>
</thead>
<tbody>
@foreach (Person person in People)
{
<tr style="cursor:pointer" @onclick="()=>person.opened=!person.opened">
<td>@person.id</td>
<td>@person.name</td>
<td>@person.birthday.ToString("dddd MMMM d, yyyy")</td>
<td>@(person.approved ? "Approved" : "Denied")</td>
<td>@person.reason</td>
</tr>
@if (person.opened)
{
<tr>
<td colspan="5">
<p>
Lorem Ipsum is simply dummy text
</p>
</td>
</tr>
}
}
</tbody>
</table>
@code {
class Person
{
public int id;
public string name;
public DateTime birthday;
public bool approved;
public string info;
public string reason;
public bool opened; // I call this a state carrier, because it's not really data.
public Person(int ID, string Name, DateTime Birthday, bool Approved, string Reason) {
id = ID; name = Name; birthday = Birthday; approved = Approved; reason = Reason;
}
}
List<Person> People = new List<Person>();
protected override void OnInitialized()
{
People.Add(new Person(183, "John Doe", new DateTime(2014, 7, 11), true, "Good worker."));
People.Add(new Person(184, "Benjamin", new DateTime(1999, 9, 9), false, "Types too much."));
People.Add(new Person(1, "Jesus", new DateTime(1, 1, 1), true, "Really nice guy."));
}
}
我在需要执行此操作的地方选择的模式是使用泛型并包装底层数据类型以添加 UI 特定功能。
泛型类的定义如下所示。
public class ExpandableItem<T>
{
private readonly T _item;
private bool _expanded;
public ExpandableItem(T item)
{
_item = item;
}
public T Item { get => _item; }
public bool IsExpanded{ get; set; }
}
初始化看起来像这样:
List<ExpandableItem<Person>> People = new();
protected override void OnInitialized()
{
People.Add(ExpandableItem<Person>(new Person(183, "John Doe", new DateTime(2014, 7, 11), true, "Good worker.")));
}
这导致绑定看起来有点像这样:
@foreach (ExpandableItem<Person> item in People)
{
var person = item.Item;
<tr style="cursor:pointer" @onclick="()=>item.IsExpanded=!item.IsExpanded">
<td>@person.id</td>
<td>@person.name</td>
</tr>
@if (item.IsExpanded)
{
<tr>
<td colspan="5">
<p>Lorem Ipsum is simply dummy text</p>
</td>
</tr>
}
}
这样做是我能想到的最好方法,可以确保来自 API 端点的 DTO 对象保持干净的 UI 状态属性。我发现它还确保了页面之间的一致性,因为具有可选择/可扩展行的要求在许多基础类型中很常见。
<tr>
<td class="oi oi-elevator" @onclick="@(() => ToggleDetails(@report.EventId))"/>
</tr>
<tr style="@(_eventIds.Contains(report.EventId) ? "display:table-row;" : "display:none;")">
<td colspan="6">
<EventGridDetail _status=report.Status/>
</td>
</tr>
private void ToggleDetails(string eventId)
{
if (_eventIds.Contains(eventId))
_eventIds.Remove(eventId);
else
_eventIds.Add(eventId);
}