Blazor项目:如何创建可动态生成列表项的有序列表组件
问题
我正在开发Blazor项目,想要创建一个可动态生成<li>并展示数据的OrderedList组件。具体需求:
- 获取子组件
ListItem的参数,循环提取属性值在<ul>中展示内容 - 为每个
ListItem生成带属性名的列表头部 - 支持两种
ListItem:一种绑定数据属性展示值,一种自定义内容(比如操作按钮)
已尝试实现但无法正确获取ListItem的参数,请求指导和代码示例。
当前尝试的代码:
<!-- 使用示例 --> <OrderedList Items="pagedResult.Items"> <ListItem Header="Name" Property="Name" /> <ListItem Header="Actions"> <button type="button"> Edit </button> </ListItem> </OrderedList> <!-- OrderedList.razor 当前实现 --> <ul> @foreach (var item in Items) { <li> @foreach (var listItem in ListItems) { <span> @item.GetType().GetProperty(listItem.Property).GetValue(item) </span> } </li> } </ul> @code { [Parameter] public List<OrganizationResponse> Items { get; set; } [Parameter] public RenderFragment ListItems { get; set; } } <!-- ListItem.razor 当前实现 --> <div> @ChildContent </div> @code { [Parameter] public string Header { get; set; } [Parameter] public string Property { get; set; } [Parameter] public bool Sortable { get; set; } [Parameter] public RenderFragment ChildContent { get; set; } }
解决方案
核心问题是无法通过RenderFragment直接获取ListItem的参数,需要通过级联参数让子组件主动注册到父组件,同时将OrderedList改为泛型组件提升通用性。
1. 修改ListItem.razor,添加级联参数注册到父组件
@if (ChildContent != null) { @ChildContent } @code { // 级联获取父组件引用 [CascadingParameter] private OrderedList<TItem> ParentList { get; set; } [Parameter] public string Header { get; set; } [Parameter] public string Property { get; set; } [Parameter] public bool Sortable { get; set; } [Parameter] public RenderFragment<TItem> ChildContent { get; set; } // 改为带数据上下文的RenderFragment protected override void OnInitialized() { ParentList?.AddListItem(this); } }
2. 重写OrderedList.razor,实现表头渲染和动态行内容
<div class="ordered-list-container"> <!-- 列表头部 --> <div class="list-header"> @foreach (var listItem in _registeredItems) { <span class="header-cell"> @listItem.Header @if (listItem.Sortable) { <span> ↑</span> <!-- 可添加排序图标 --> } </span> } </div> <!-- 列表主体 --> <ul class="list-body"> @foreach (var item in Items) { <li class="list-row"> @foreach (var listItem in _registeredItems) { <span class="row-cell"> @if (!string.IsNullOrEmpty(listItem.Property)) { <!-- 反射获取属性值,处理空值 --> @GetPropertyValue(item, listItem.Property) } else if (listItem.ChildContent != null) { <!-- 渲染自定义内容,传递当前数据项作为上下文 --> @listItem.ChildContent(item) } </span> } </li> } </ul> </div> @code { private List<ListItem<TItem>> _registeredItems = new(); // 泛型参数支持任意数据类型 [Parameter] public IEnumerable<TItem> Items { get; set; } = Enumerable.Empty<TItem>(); [Parameter] public RenderFragment ChildContent { get; set; } // 供子组件注册的方法 public void AddListItem(ListItem<TItem> listItem) { _registeredItems.Add(listItem); StateHasChanged(); } // 反射获取属性值,封装成方法避免重复代码 private object GetPropertyValue(TItem item, string propertyName) { if (item == null) return string.Empty; var propertyInfo = typeof(TItem).GetProperty(propertyName); return propertyInfo?.GetValue(item) ?? string.Empty; } }
3. 组件使用示例
<OrderedList Items="pagedResult.Items"> <ListItem Header="Name" Property="Name" /> <ListItem Header="Actions"> @(item => { <button type="button" @onclick="() => HandleEdit(item)"> Edit </button> <button type="button" @onclick="() => HandleDelete(item)" class="ml-2"> Delete </button> }) </ListItem> </OrderedList> @code { private void HandleEdit(OrganizationResponse organization) { // 编辑逻辑 } private void HandleDelete(OrganizationResponse organization) { // 删除逻辑 } }
额外建议
- 样式定制:给组件添加CSS类(如示例中的
ordered-list-container、list-header等),方便自定义布局和样式 - 性能优化:如果数据量较大,可缓存反射的
PropertyInfo,避免每次循环都调用GetType().GetProperty() - 排序功能:基于
Sortable参数,在表头点击时触发排序逻辑,修改Items的排序状态 - 空状态处理:添加
@if (!Items.Any())分支,展示空数据提示
内容的提问来源于stack exchange,提问作者user3903484
相关产品推荐
相关产品推荐

