【问题标题】:Template Page For Blazor ComponentBlazor 组件的模板页面
【发布时间】:2023-04-09 18:57:01
【问题描述】:

基本上我正在尝试创建Templated Componentlayout 的组合。 我想要一个可以重复使用的页面布局,并且仍然将 required 参数传递给。我认为一种方法可能是利用抽象基类。

如果我想通过渲染树构建来完成这一切,这可以通过我的第三个选项来解决,但我不想这样做。我可以接受由渲染树构建的基础(即选项 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),但MyGridStudentsClasses)会有所不同。

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# layout blazor


    【解决方案1】:

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

    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)
    

    StringType.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>
    

    【讨论】:

    • 有趣的想法@MayurEkbote,但它似乎是一个类似(并且更复杂)的模板解决方案?我可以将页面作为TypedObject 传递吗?
    • 作为 RederFragment 参数?是的。但我认为您也在寻找两个组件的通用布局。即一些共同的组件和一些特定的组件。在 IntType 和 StringType 中,您可以通过在基类中包含 RenderFragment 来放置常用的 ui 元素。编辑答案以说明。
    • 你能根据我上面给出的任何通用示例来暗示吗?我想我按照你的例子,但我猜我不需要通用类/渲染片段
    • 请看,抽象类为常见的渲染元素(在您的情况下为标题)提供了一个占位符。通用成员为不常见的元素(学生和班级部分)提供了一种方式。这一切都是因为我们希望最终拥有一个组件。否则,它非常简单。更新了示例以显示更简单的方法。
    • 嗯,这是一个你已经完成的模板化组件。正如问题中提到的,我不喜欢这种方式,因为缺乏必要性,而且这样做也感觉不对
    【解决方案2】:

    组件继承适用于继承功能,但不适用于内容。这是来自 ComponentBase 的代码 sn-p,它定义了传递给渲染器的 RenderFragment 委托:

            public ComponentBase()
            {
                _renderFragment = builder =>
                {
                    _hasPendingQueuedRender = false;
                    _hasNeverRendered = false;
                    BuildRenderTree(builder);
                };
            }
    

    它调用BuildRenderTree,即子级中的那个,它(只要你有一些 Razor 内容)覆盖父级中的BuildRenderTree

    您需要考虑组件

    GridPage.razor 是一个组件,看起来像这样:

    <header>@Title</header>
    <div>@ChildContent</div>
    
    @code {
        [Parameter] public string Title { get; set; } = "Silly sod, you didn't define a header";
        [Parameter] public RenderFragment ChildContent { get; set; }
    }
    

    然后MyGrid.Razor 是一个RouteView。

    @page "/MyGrid"
    <GridPage Title="My Grid Page">
        <Column>
        <Column>
    ... more markup
    </GridPage>
    

    【讨论】:

    • 这是模板化的组件路由。我不喜欢包装以及它没有所需的参数
    • 如果你想构建一个具有你想要的功能的基础组件,那么你需要编写自己的基础组件。向您展示如何做到这一点超出了 StackOverflow 答案的范围。
    猜你喜欢
    • 2021-01-29
    • 2020-08-04
    • 1970-01-01
    • 2019-09-18
    • 1970-01-01
    • 2012-10-10
    • 2012-11-25
    • 2020-02-01
    • 1970-01-01
    相关资源
    最近更新 更多