Blazor MudTable中传递给基组件的泛型RenderFragment无法正确渲染
我封装了一个BaseMaintenance基组件,用来提供可复用的MudTable功能。在子组件中定义了三个RenderFragment:
private RenderFragment TableHeaders =@<MudTh><MudTableSortLabel SortBy="new Func<AgreementTypeDTO, object>(x => (object)(x.AgreementDescription != null ? x.AgreementDescription : string.Empty))">Description</MudTableSortLabel></MudTh>; private RenderFragment<AgreementTypeDTO> TableRowTemplate = item => { Console.WriteLine($"TableRowTemplate invoked with AgreementTypeDTO: {item}"); return @<MudTd DataLabel="Description">@item.AgreementDescription</MudTd>; }; private RenderFragment<AgreementTypeDTO> TableRowEditTemplate => item => builder => { builder.OpenComponent(0, typeof(MudTd)); builder.AddAttribute(1, "DataLabel", "Description"); builder.OpenComponent(2, typeof(MudTextField<string>)); builder.AddAttribute(3, "Value", item.AgreementDescription); builder.AddAttribute(4, "ValueChanged", EventCallback.Factory.Create(this, (string value) => { item.AgreementDescription = value; })); builder.AddAttribute(5, "Required", true); builder.CloseComponent(); // 原代码漏掉了关闭MudTd的CloseComponent调用 };
将这些RenderFragment传入BaseMaintenance组件:
<MudContainer MaxWidth="MaxWidth.Large"> <br /> <AddNew Size="Size.Medium" TItem="AgreementTypeDTO" Update="StateHasChanged" Data="@Items" /> <Delete Size="Size.Medium" TItem="AgreementTypeDTO" Update="StateHasChanged" DeleteAction="@agreementTypeService.Remove" OriginalList="@Items" SelectedItems="@selectedItems.ToList()"></Delete> <div style="height: 10px"></div> <BaseMaintenance TItem="AgreementTypeDTO" Items=@Items TableTitle="Agreement Types" TableHeaders=@TableHeaders TableRowTemplate=@TableRowTemplate TableRowEditTemplate=@TableRowEditTemplate Service="@agreementTypeService" /> <br /> <br /> <EditEventLog @ref="eventLog"></EditEventLog> </MudContainer>
在BaseMaintenance组件中,将传入的RenderFragment放入MudTable:
<MudTable MultiSelection="true" @bind-SelectedItems="selectedItems" Loading="@_loading" Items="@Items" Hover="true" CanCancelEdit="true" Filter="new Func<TItem,bool>(FilterFunc)" SortLabel="Sort By" CommitEditTooltip="Commit Edit" RowEditPreview="BackupItem" RowEditCancel="ResetItemToOriginalValues" RowEditCommit="ItemHasBeenCommitted" IsEditRowSwitchingBlocked="true" ApplyButtonPosition="TableApplyButtonPosition.Start" EditButtonPosition="TableEditButtonPosition.Start" EditTrigger="@editTrigger"> <ToolBarContent> <MudText Typo="Typo.h6">@TableTitle</MudText> <MudSpacer /> <MudTextField @bind-Value="searchString" Placeholder="Search" Adornment="Adornment.Start" AdornmentIcon="@Icons.Material.Filled.Search" IconSize="Size.Medium" Class="mt-0"></MudTextField> </ToolBarContent> <ColGroup> <col style="width:50px;" /> <col /> <col /> <col /> <col /> <col /> </ColGroup> <HeaderContent> @TableHeaders </HeaderContent> <RowTemplate> @*<MudTd DataLabel="Description">@GetDescription(context)</MudTd>*@ @TableRowTemplate </RowTemplate> <RowEditingTemplate> @* @TableRowEditTemplate *@ </RowEditingTemplate> <PagerContent> <MudTablePager /> </PagerContent> <EditButtonContent Context="button"> <MudIconButton Size="@Size.Small" Icon="@Icons.Outlined.Edit" Class="pa-0" OnClick="@button.ButtonAction" Disabled="@button.ButtonDisabled" /> </EditButtonContent> <FooterContent> <MudTd colspan="6">Select All</MudTd> </FooterContent> </MudTable> @code { private string GetDescription(object obj) { var property = obj.GetType().GetProperty("AgreementDescription"); return property != null ? property.GetValue(obj)?.ToString() : "Property not found"; } }
当前无控制台或运行时错误,但描述字段显示为Microsoft.AspNetCore.Components.RenderFragment1[PIMS.Service.AgreementTypeDTO]。注释掉@TableRowTemplate改用@GetDescription(context)`能正常渲染,但这破坏了组件抽象性——不同表格列数不同,必须用RenderFragment实现自定义行模板。表头的RenderFragment能正常工作,无法解决传递RowTemplate的异常问题。
问题核心在于泛型RenderFragment<T>的调用方式错误:RenderFragment<TItem>本质是一个接受TItem参数并返回RenderFragment的委托,直接写@TableRowTemplate会让Blazor把这个委托对象转换成字符串输出,而非执行它生成渲染内容。
1. 修改BaseMaintenance中的RowTemplate
将RowTemplate内的代码改为传递当前行的context参数:
<RowTemplate> @TableRowTemplate(context) </RowTemplate>
2. 修复RowEditingTemplate的调用
同样给TableRowEditTemplate传递context:
<RowEditingTemplate> @TableRowEditTemplate(context) </RowEditingTemplate>
3. 检查BaseMaintenance的参数定义
确保TableRowTemplate和TableRowEditTemplate的参数类型正确:
[Parameter] public RenderFragment<TItem> TableRowTemplate { get; set; } [Parameter] public RenderFragment<TItem> TableRowEditTemplate { get; set; }
额外修复:TableRowEditTemplate的代码问题
原代码中漏掉了关闭MudTd的builder.CloseComponent(),需要补上,否则会导致DOM结构异常:
private RenderFragment<AgreementTypeDTO> TableRowEditTemplate => item => builder => { builder.OpenComponent(0, typeof(MudTd)); builder.AddAttribute(1, "DataLabel", "Description"); builder.OpenComponent(2, typeof(MudTextField<string>)); builder.AddAttribute(3, "Value", item.AgreementDescription); builder.AddAttribute(4, "ValueChanged", EventCallback.Factory.Create(this, (string value) => { item.AgreementDescription = value; })); builder.AddAttribute(5, "Required", true); builder.CloseComponent(); // 关闭MudTextField builder.CloseComponent(); // 关闭MudTd,补充原代码缺失的行 };
修改后,MudTable会把当前行的context(即AgreementTypeDTO实例)传递给TableRowTemplate和TableRowEditTemplate,触发委托生成对应的<MudTd>元素,就能正常渲染字段内容了。
内容的提问来源于stack exchange,提问作者Daniel

