能否从抽象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 @{ 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> | <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
相关产品推荐
相关产品推荐

