Templating components with RenderFragments
使用RenderFragment传HTML片段。
直接如下使用component会出错:
<Collapsible>Hello world!</Collapsible>
抛出的异常可能是:WASM: System.InvalidOperationException: Object of type ‘TemplatedComponents.Components.Collapsible’ does not have a property matching the name ‘ChildContent’.
需要在Collapsible组件中指定一个额外的参数ChildContent,用于接收片段:
@code {
[Parameter]
public RenderFragment ChildContent { get; set; }
}
此处的ChildContent用于接收传入的渲染片段。可以是纯文本,可以是HTML片段,可以是razor片段。
可以传多个RenderFragment:
<MyComponent>
<Header>
<h1>The header</h1>
</Header>
<Footer>
This is the footer
</Footer>
<ChildContent>
The ChildContent render fragment must now be explicitly named because we have
more than one render fragment parameter in MyComponent.
It doesn't have to be named ChildContent.
</ChildContent>
</MyComponent>
Creating a TabControl
- 通过RenderFragment传数据
- 通过CacadingParameter来将上级TabControl传入到下级Tabpage组件中
顶层代码是:
<TabControl>
<TabPage Text="Tab 1">
<h1>The first tab</h1>
</TabPage>
<TabPage Text="Tab 2">
<h1>The second tab</h1>
</TabPage>
<TabPage Text="Tab 3">
<h1>The third tab</h1>
</TabPage>
</TabControl>
内容是这么传递的,首先TabControl标签中的所有内容作为RenderFragment都传给TabControl组件的ChildContent辖属。TabControl只需在页面上输入ChildContent,然后Blazor会自动初始化三个tabPage,每个TabPage又被传入相应的RenderFragment(例如<h1>The first tab</ha>
)。当然,每个TabPage也要有ChildContent辖属来接收其RenderFragment。
Tab标签栏由上级的TabControl控制,所以TabControl需要知道有几个下级的TabPage,以及每个TabPage的标签名。前者是让TabPage初始化时告诉TabControl的,后者是使用TabPage上设置的Text属性。也就是说,每个TabPage需要知道上级是谁,这是TabControl使用CacadingParameter,把自己(this)传递给下级TabPage,然后每个下级TabPage又将自己注册给TabControl。
TabControl记录当前那个标签页是活跃的,并将其保存在一个辖属中。下级TabPage只需要判断自己是否是上级的活跃标签页那个辖属,就知道自己的内容该不该显示了。
Passing data to a RenderFragment
对上面做一些修改:
<TabControl>
<TabTextTemplate>
@context.Text
</TabTextTemplate>
<ChildContent>
<TabPage Text="Tab 1">
<h1>The first tab</h1>
</TabPage>
<TabPage Text="Tab 2">
<h1>The second tab</h1>
</TabPage>
<TabPage Text="Tab 3">
<h1>The third tab</h1>
</TabPage>
</ChildContent>
</TabControl>
传入两个RenderFragment
- 一个是类型为RenderFragment的childContent
- 另一个是RenderFragment的TabTextTemplate
- TabTextTemplate 对应上述的
<TabTextTemplate>
标签 - 此处的TabPage既是
@context.Text
中的context。如果不想用context,也可以改成其他名字:<TabTextTemplate Context="TheTab">
。 - 无他,维函数传递而已
- TabTextTemplate 对应上述的
Using @typeparam to create generic components
将组件定义成泛型的需要特定的语法。
假设有一个DataList.razor,需要定义成泛型的,那么需要使用@typeparam
指示,以及定义一个IEnumerable类型的辖属:
@typeparam TItem
@code
{
[Parameter]
public IEnumerable<TItem> Data { get; set; }
}
下面定义一个Person类,作为其TItem的类型:
public class Person
{
public string Salutation { get; set; }
public string GivenName { get; set; }
public string FamilyName { get; set; }
}
DataList的组件是这样使用的:
@page "/"
<h1>A generic list of Person</h1>
<DataList Data=@People>
@context.Salutation @context.FamilyName, @context.GivenName
</DataList>
@code
{
private IEnumerable<Person> People;
protected override void OnInitialized()
{
base.OnInitialized();
People = new Person[]
{
new Person { Salutation = "Mr", GivenName = "Bob", FamilyName = "Geldof" },
new Person { Salutation = "Mrs", GivenName = "Angela", FamilyName = "Rippon" },
new Person { Salutation = "Mr", GivenName = "Freddie", FamilyName = "Mercury" }
};
}
}
上述代码片段中,<DataList Data=@People>
中的People符合IEnumerable<TItem>
类型。然后Data是组件中用于接收People的辖属。此处TItem是从People的类型推断出来的,也可以显示指定:<SomeGenericComponent TParam1=Person TParam2=Supplier TItem=etc/>
。
DataList对应的ChildContent是@context.Salutation @context.FamilyName, @context.GivenName
,可以把这个看成一个函数,输入时IEnumerable<TItem>
中的一项,然后对其处理后。
可以在DataList.razor显示调用ChildContent:
@typeparam TItem
<ul>
@foreach(TItem item in Data ?? Array.Empty<TItem>())
{
<li>@ChildContent(item)</li>
}
</ul>
@code
{
[Parameter]
public IEnumerable<TItem> Data { get; set; }
[Parameter]
public RenderFragment<TItem> ChildContent { get; set; }
}
Passing placeholders to RenderFragments
假设有这样一个参数,其类型为RenderFragment<RenderFragment>
:
[Parameter]
public RenderFragment<RenderFragment> ChildContent { get; set; }
RenderFragment<T>
中的<T>
是按用户自定义的标签传入@context
变量的。但是在layout中,其对应的是@Body
,如果将@Body.GetType().Name
打印出来,那么可以看到输出的是RenderFragment
。
@:@{
在C#代码中插入Razor代码。
假设将RenderFragment看出一个可执行函数,如果RenderFragment<T>
中的<T>
部分缺失则代表此RenderFragment执行的时候不需要额外的参数;如果指定了<T>
,那么必须传入类型为T的参数。
这些参数应该不是在构造组件的时候传入的,而是在组件构造完毕时传递给组件的指定辖属。
(本篇完)