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

能否从抽象Razor基类继承实现Blazor组件复用?

Blazor组件的基类继承复用方案

Blazor完全支持通过C#基类实现组件逻辑与UI的复用,和普通C#类继承逻辑一致,下面结合你的代码场景给出具体实现步骤:

1. 创建通用逻辑基类

定义一个继承自ComponentBase的泛型基类,封装列表页通用的搜索、数据绑定逻辑:

public class OrganizationListBase<T> : ComponentBase where T : IOrganization
{
    [Parameter]
    public string UrlFileName { get; set; } = string.Empty;

    // 搜索参数,供Filter组件绑定
    protected string SearchName { get; set; } = string.Empty;
    protected string SearchUniqueId { get; set; } = string.Empty;

    // 列表数据,供Grid组件绑定
    protected List<T> Values { get; set; } = new();

    // 子类需实现的具体搜索方法
    [Parameter]
    public Func<string, string, Task<List<T>>> SearchAsync { get; set; } = default!;

    // 数据变更回调
    [Parameter]
    public EventCallback<List<T>> ValuesChanged { get; set; }

    // 搜索执行逻辑
    protected async Task SearchOrganizations()
    {
        Values = await SearchAsync(SearchName, SearchUniqueId);
        await ValuesChanged.InvokeAsync(Values);
        StateHasChanged();
    }

    // 接收数据变更
    protected void OnListChanged(List<T> newValues)
    {
        Values = newValues;
        StateHasChanged();
    }

    // 获取当前页面URL作为返回地址
    protected string ReturnUrl => NavigationManager.Uri;

    [Inject]
    protected NavigationManager NavigationManager { get; set; } = default!;
}

2. 调整顶层组件继承基类

修改ListCampaigns组件,继承上述基类,仅需实现具体的搜索逻辑:

@page "/Manager/ListCampaigns"
@inherits OrganizationListBase<Campaign>

<OrganizationFilter @bind-SearchName="@SearchName"
                    @bind-SearchUniqueId="@SearchUniqueId"
                    SearchAsync="@SearchCampaigns"
                    ValuesChanged="@OnListChanged" />

<OrganizationGrid @bind-Data="@Values"
                  UrlFileName="Campaign"
                  ReturnUrl="@ReturnUrl" />

@code {
    // 实现Campaign的具体搜索逻辑
    private async Task<List<Campaign>> SearchCampaigns(string name, string uniqueId)
    {
        // 替换为实际的API调用或数据查询逻辑
        return new List<Campaign>();
    }
}

3. 调整Filter组件适配基类绑定

修改OrganizationFilter,将搜索参数改为可绑定的组件参数,对接基类的属性:

<DxFormLayout>
    <DxFormLayoutGroup Caption="Filter (search)" ColSpanMd="10">
        <DxFormLayoutItem Caption="Name:" ColSpanMd="4">
            <DxTextBox @bind-Text="@SearchName"></DxTextBox>
        </DxFormLayoutItem>
        <DxFormLayoutItem Caption="Unique Id:" ColSpanMd="4">
            <DxTextBox @bind-Text="@SearchUniqueId"></DxTextBox>
        </DxFormLayoutItem>
        <DxFormLayoutItem ColSpanMd="2">
            <DxButton Text="Search" Click="@SearchOrganizations"></DxButton>
        </DxFormLayoutItem>
    </DxFormLayoutGroup>
</DxFormLayout>

@code {
    [Parameter]
    public string SearchName { get; set; } = string.Empty;

    [Parameter]
    public EventCallback<string> SearchNameChanged { get; set; }

    [Parameter]
    public string SearchUniqueId { get; set; } = string.Empty;

    [Parameter]
    public EventCallback<string> SearchUniqueIdChanged { get; set; }

    [Parameter]
    public Func<string, string, Task<List<IOrganization>>> SearchAsync { get; set; } = default!;

    [Parameter]
    public EventCallback<List<IOrganization>> ValuesChanged { get; set; }

    private async Task SearchOrganizations()
    {
        var results = await SearchAsync(SearchName, SearchUniqueId);
        await ValuesChanged.InvokeAsync(results);
    }
}

4. 调整Grid组件适配基类参数

修改OrganizationGrid,新增ReturnUrl参数,对接基类的返回地址逻辑:

<DxGrid @bind-Data="@Data"
        PageSize="5"
        CssClass="dx-datagrid"
        PagerNavigationMode="PagerNavigationMode.InputBox"
        PageSizeSelectorVisible="true"
        PageSizeSelectorAllRowsItemVisible="true"
        PageSizeSelectorItems="@(new[] { 5, 10, 20, 50, 100 })">
    <Columns>
        <DxGridDataColumn >
            <HeaderCaptionTemplate>
                @{
                    var urlCreate = $"/Manager/{@UrlFileName}?mode=create&returnUrl={@ReturnUrl}";
                }
                <NavLink href="@urlCreate">create</NavLink>
            </HeaderCaptionTemplate>
            <CellDisplayTemplate>
                @context.DisplayText &nbsp;
                @{
                    var dataItem = context.DataItem as IOrganization;
                    var uniqueId = dataItem?.UniqueId;
                    var urlView = $"/Manager/{@UrlFileName}?mode=read&uniqueId={@uniqueId}&returnUrl={@ReturnUrl}";
                    var urlEdit = $"/Manager/{@UrlFileName}?mode=update&uniqueId={@uniqueId}&returnUrl={@ReturnUrl}";
                }
                <NavLink class="btn btn-link" href="@urlView">view</NavLink>
                &nbsp;|&nbsp;
                <NavLink class="btn btn-link" href="@urlEdit">edit</NavLink>
            </CellDisplayTemplate>
        </DxGridDataColumn>
        <DxGridDataColumn Caption="Name" FieldName="Name" />
        <DxGridDataColumn Caption="Unique Id" FieldName="UniqueId" />
        <DxGridDataColumn Caption="Region" FieldName="Parent.Name" />
    </Columns>
</DxGrid>

@code {
    [Parameter]
    public IEnumerable<IOrganization> Data { get; set; } = Enumerable.Empty<IOrganization>();

    [Parameter]
    public EventCallback<IEnumerable<IOrganization>> DataChanged { get; set; }

    [Parameter]
    public string UrlFileName { get; set; } = string.Empty;

    [Parameter]
    public string ReturnUrl { get; set; } = string.Empty;
}

扩展说明

通过这种方式,后续新增类似的列表页面(如ListRegions)时,只需继承OrganizationListBase<Region>并实现对应搜索方法,即可复用OrganizationFilter和OrganizationGrid组件,完全避免重复代码。

如果需要更进一步复用UI结构,还可以在基类中重写BuildRenderTree方法,直接渲染Filter和Grid的UI,子类组件甚至无需编写Razor代码,仅需实现业务逻辑即可,适合高度标准化的场景。

内容的提问来源于stack exchange,提问作者David Thielen

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.19 09:37:03