首页 > 解决方案 > Blazor 组件的模板页面

问题描述

基本上我正在尝试创建模板化组件布局的混合。我想要一个可以重用的页面布局,并且仍然可以传递所需的参数。我认为这样做的一种方法可能是利用抽象基类。

如果我想通过渲染树构建来完成这一切,这可以通过我的第三个选项来解决,但我不想这样做。我可以接受由渲染树构建的基础(即选项 3),但不是从它继承的组件(即MyGrid.razor

我想做的事:

MyGrid.razor

@page "/mygrid"
@inherits GridPage @*or "layout" or whatever works*@

<Grid>
    <Column>
    <Column>
</Grid>

@code {
    protected overrides string Title => "My Grid"
}

GridPage.cs

<header>@Title</header>
<div>@ChildContent</div>

@code {
    [Parameter] public string Title { get; set; }
    [Parameter] public RenderFragment ChildContent { get; set; }
}

我尝试过的解决方案

  1. 布局

GridLayout

<header>@Title</header>
<div>@Body</div>

@code {
    [CascadingParameter]
    [Parameter] public string Title { get; set; }
}

问题是你必须记住这个参数是可用的,不需要隐含

  1. 模板
<GridPageTemplate Title="My Title">
    <Grid>
         <Column>
         <Column>
    </Grid>
</GridPageTemplate> 

这里的问题是我必须用模板包装我的组件(没什么大不了的,但宁愿继承),我仍然不知道需要哪些参数来实现。

  1. 抽象基类
public abstract class GridPage : ComponentBase
{
    protected abstract string Title { get; }
  
    protected override void BuildRenderTree(RenderTreeBuilder builder)
    {
        builder.OpenRegion(0);
        builder.OpenElement(1, "header");
        builder.AddContent(2, Title);
        builder.CloseElement();
        builder.CloseRegion();

        builder.OpenElement(3, "div")
        builder.OpenRegion(10);
        this.BuildRenderTree(builder);
        builder.CloseRegion();
        builder.Closelement()
    }
}

问题是这个似乎不起作用。我实际上认为这会导致它只接受组件并继续基本渲染(this.BuildRenderTree(builder);)。

期望的结果

假设你有两个网格......一个Students网格和一个网Classes格。我希望它们每个都有不同的网格(和标题)。我想基本上为每个自定义标题和列。它们会有一个共同的布局标记(我称之为GridPage),但MyGrid(要么 要么StudentsClasses会有所不同。

Students.razor

@page "/students"
@inherits GridPage

<Grid RowType=Student>
    <Column For="StudentName">
    <Column For="StudentAge">
    <Column For="StudentHomeRoom">
</Grid>

@code {
    protected overrides string Title => "Students"
}

Classes.razor

@page "/classes"
@inherits GridPage

<Grid RowType=Class>
    <Column For="ClassName">
    <Column For="ClassTime">
</Grid>

@code {
    protected overrides string Title => "Classes"
}

他们都会输出

<header>Title_here</header>
<div>Grid_markup_here</div>

标签: c#layoutblazor

解决方案


创建具有逻辑所需的公共属性的抽象基类,然后将其扩展至您实际必须呈现的位置。

AbstractBaseClass.razor.cs

public class AbstractBaseClass<T> : ComponentBase
{
    [Parameter]
    public string Title {get;set;}

    [Parameter]
    public T TypedObject {get;set;}

    [Parameter]
    public RenderFragment<T> TypedComponent {get;set;}
    
    [Parameter]
    public RenderFragment CommonUi {get;set;}

}

在一种实现中:

IntType.razor

@inherits AbstractBaseClass<int>
@CommonUi
 // or any other markup logic if any    
@TypedComponent(TypedObject)

字符串类型.razor

@inherits AbstractBaseClass<string>
@CommonUi
 // or any other markup logic if any    
@TypedComponent(TypedObject)

然后将其与硬实现一起使用

<StringType TypedObject="StackOverflow" >
  <CommonUi> /* Component common to both*/ </CommonUi>
</StringType>

<IntType TypedObject="2" >
   <CommonUi> /* Component common to both*/ </CommonUi>
</IntType>

更新:如果你想完全避开模板部分,很简单:

CommonLayout.razor

<div>
 <header>@Title</header>
 @ChildContent
</div>

@code
{
  [Parameter]
  public string Title {get;set;}
  
  [Parameter]
  public RenderFragment ChildContent {get;set;}
}

学生剃须刀

<CommonLayout Title="Student Title">
  <Grid RowType=Student>
     <Column For="StudentName">
     <Column For="StudentAge">
     <Column For="StudentHomeRoom">
  </Grid>
</CommonLayout>

ClassRender.razor

<CommonLayout Title="Class Title">
  <Grid RowType=Class>
     <Column For="ClassName">
     <Column For="ClassTime">
 </Grid>
</CommonLayout>

推荐阅读