实际项目当中经常会有很多重复使用的组件,它们具有相同的布局,只是填充的内容不一样,如果不停的复制粘贴也是件费劲的事情,而且也不好维护和更新,这时候就可以考虑使用模板化组件。
比如以下 Bootstrap 自带的 Card 组件:
红色框的部分就是公共的部分,我们可以把她们封装到模板化组件里。
# RENDERFRAGMENT
模板化组件需要用到 RenderFragment 类型的参数。对于上面的例子,我们可以分成 Header 和 Body 两个部分,如下:
<!--Card 组件 --> | |
<div class="card" style="width: 18rem;"> | |
<div class="card-header"> | |
@Header | |
</div> | |
<ul class="list-group list-group-flush"> | |
@Body | |
</ul> | |
</div> | |
@code { | |
[Parameter] | |
public RenderFragment Header { get; set; } | |
[Parameter] | |
public RenderFragment Body { get; set; } | |
} |
调用的时候可以这样写:
<Card> | |
<Header> | |
Featured | |
</Header> | |
<Body> | |
<li class="list-group-item">Cras justo odio</li> | |
<li class="list-group-item">Dapibus ac facilisis in</li> | |
<li class="list-group-item">Vestibulum at eros</li> | |
</Body> | |
</Card> |
RenderFragment 不仅可以使用静态内容,还可以使用 Razor 语法,如下:
<Card> | |
<Header> | |
Featured | |
</Header> | |
<Body> | |
@foreach (var item in this.Items) | |
{ | |
<li class="list-group-item">@item</li> | |
} | |
</Body> | |
</Card> | |
@code { | |
string[] Items = new string[] | |
{ | |
"Cras justo odio","Dapibus ac facilisis in","Vestibulum at eros" | |
}; | |
} |
你还可以把 Items 封装到 Card 组件里面:
<!--Card 组件 --> | |
<div class="card" style="width: 18rem;"> | |
<div class="card-header"> | |
@Header | |
</div> | |
<ul class="list-group list-group-flush"> | |
@foreach (var item in this.Items) | |
{ | |
<li class="list-group-item">@item</li> | |
} | |
</ul> | |
</div> | |
@code { | |
[Parameter] | |
public RenderFragment Header { get; set; } | |
[Parameter] | |
public IEnumerable<string> Items { get; set; } | |
} |
调用如下:
<Card Items="this.Items"> | |
<Header> | |
Featured | |
</Header> | |
</Card> |
# RENDERFRAGMENT<T>
RenderFragment 还可以带一个类型参数,这个参数有什么用呢? 看下面的例子:
<!--Card 组件 --> | |
<div class="card" style="width: 18rem;"> | |
<div class="card-header"> | |
@Header | |
</div> | |
<ul class="list-group list-group-flush"> | |
@foreach (var item in this.Items) | |
{ | |
@ItemTemplate(item) | |
} | |
</ul> | |
</div> | |
@code { | |
[Parameter] | |
public RenderFragment Header { get; set; } | |
[Parameter] | |
public RenderFragment<string> ItemTemplate { get; set; } | |
[Parameter] | |
public IEnumerable<string> Items { get; set; } | |
} |
这里的 ItemTemplate 定义成了 string 类型的 RenderFragment,并通过 @ItemTemplate (item) 来呈现内容,似乎有点不太好理解,先来看看调用方式:
<Card Items="this.Items"> | |
<Header> | |
Featured | |
</Header> | |
<ItemTemplate> | |
<li class="list-group-item">@context</li> | |
</ItemTemplate> | |
</Card> |
与前面的例子一样,ItemTemplate 可以包含静态内容,也可以使用 Razor 语法,但是她多了一个 @context,这个 @context 的类型其实就是参数声明里的 string,她的值就是 foreach 里面当前 item 的值。
# TYPEPARAM
RenderFragment 不仅可以使用固定类型,还可以通过 typeparam 来声明为泛型,如下:
<!--Card 组件 --> | |
@typeparam TItem | |
<div class="card" style="width: 18rem;"> | |
<div class="card-header"> | |
@Header | |
</div> | |
<ul class="list-group list-group-flush"> | |
@foreach (var item in this.Items) | |
{ | |
@ItemTemplate(item) | |
} | |
</ul> | |
</div> | |
@code { | |
[Parameter] | |
public RenderFragment Header { get; set; } | |
[Parameter] | |
public RenderFragment<TItem> ItemTemplate { get; set; } | |
[Parameter] | |
public IEnumerable<TItem> Items { get; set; } | |
} |
与前面的例子的区别就是 string 换成了 TItem, 同时声明了 @typeparam,这样我们就可以使用任意类型的列表了,如下:
<Card Items="this.Users"> | |
<Header> | |
Featured | |
</Header> | |
<ItemTemplate> | |
<li class="list-group-item" value="@context.Key">@context.Value</li> | |
</ItemTemplate> | |
</Card> | |
@code { | |
Dictionary<int, string> Users = new Dictionary<int, string>() | |
{ | |
{ 1, "Cras justo odio" }, | |
{ 2, "Dapibus ac facilisis in" }, | |
{ 3, "Vestibulum at eros" } | |
}; | |
} |
由于这里传递的是 Dictionary, 所以 @context 就是 KeyValuePair (因为 Dictionary 就是 IEnumerable
# CONTEXT
通过前面的例子我们已经可以知道 @context 其实就是 RenderFragment<T> 中使用的类型,Blazor 通过隐式的方式把当前的值传给了她。当然你也可以换成其他的名字,比如:
<Card Items="this.Users" > | |
<ItemTemplate Context="user"> | |
<li class="list-group-item" value="@user.Key">@user.Value</li> | |
</ItemTemplate> | |
</Card> |
通过 Context 属性,就可以将 context 修改成你想要的名字。
虽然这里的 Context 属性你可以放在 ItemTemplate 标签中,也可以放在 Card 标签中,但是笔者建议只放在 ItemTemplate,以将修改控制在最小范围。