You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.12 23:54:57