实际项目当中经常会有很多重复使用的组件,它们具有相同的布局,只是填充的内容不一样,如果不停的复制粘贴也是件费劲的事情,而且也不好维护和更新,这时候就可以考虑使用模板化组件。

比如以下 Bootstrap 自带的 Card 组件:

红色框的部分就是公共的部分,我们可以把她们封装到模板化组件里。

# RENDERFRAGMENT

模板化组件需要用到 RenderFragment 类型的参数。对于上面的例子,我们可以分成 Header 和 Body 两个部分,如下:

r
<!--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; }
}

调用的时候可以这样写:

r
<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 语法,如下:

r
<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 组件里面:

r
<!--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; }
}

调用如下:

r
<Card Items="this.Items">
    <Header>
        Featured
    </Header>
</Card>

# RENDERFRAGMENT<T>

RenderFragment 还可以带一个类型参数,这个参数有什么用呢? 看下面的例子:

r
<!--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) 来呈现内容,似乎有点不太好理解,先来看看调用方式:

r
<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 来声明为泛型,如下:

r
<!--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,这样我们就可以使用任意类型的列表了,如下:

r
<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 通过隐式的方式把当前的值传给了她。当然你也可以换成其他的名字,比如:

r
<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,以将修改控制在最小范围。

更新于 阅读次数

请我喝[茶]~( ̄▽ ̄)~*

Roc 微信支付

微信支付

Roc 支付宝

支付宝