Blazor 模板化组件的开发使用指南

在这里插入图片描述

在我之前的文章 Blazor 组件初学者指南中,我介绍了组件参数,并向您展示了如何将数据作为参数传递给 Blazor 组件以自定义其功能。在这篇文章中,我将更进一步,向您展示如何将一个或多个 UI 模板作为参数传递到称为模板化组件的不同类型的 Blazor 组件中。

下载:Download Source Code

Blazor 模板化组件概述

Blazor 模板化组件是一种接受一个或多个 UI 模板作为参数的组件。这有助于组件的可重用性,因为您只需要创建一次模板化组件,然后使用该组件的每个页面都可以提供其 UI 模板,该模板可以根据页面要求由模板化组件呈现。在这里插入图片描述
模板化组件的示例包括:

  1. 一个表格组件,它允许用户为表格标题、行和页脚指定模板。
  2. 一个小部件组件,允许用户呈现具有相同外观和感觉但内容不同的不同小部件。
  3. 一个列表组件,允许用户指定一个模板来呈现项目符号或数字等列表项。
  4. 允许用户在列表、网格或卡片视图中显示数据的列表组件

当我们创建任何 Blazor 组件的参数时,我们通常将其类型指定为 string、int 或任何其他内置 .NET 数据类型。要创建模板化组件,我们创建 RenderFragmentRenderFragment 类型的组件参数。 RenderFragment 允许我们提供可以由模板化组件呈现的一段 UI。

[Parameter]
public RenderFragment HeaderTemplate { get; set; }

RenderFragment 更进一步,允许我们传递 T 类型的参数,该参数可用于自定义模板化组件的输出。

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

开始使用真实示例

为了详细了解模板化组件,我决定构建一个 TableWidget 模板化组件,它允许我们自定义不同格式的表头、行和页脚。在创建第一个模板化组件之前,让我们创建一个新的 Blazor 服务器应用程序并添加基本功能以呈现表格格式的一些数据。
在 Blazor Server App 中创建一个 Data 文件夹,并在 Data 文件夹中添加以下两个模型类。

Product.cs

public class Product
{
    public int Id { get; set; }
    public string Title { get; set; }
    public decimal Price { get; set; }
    public int Quantity { get; set; }
}

Order.cs

public class Order
{
    public int Id { get; set; }
    public string OrderNo { get; set; }
    public DateTime OrderDate { get; set; }
    public string Status { get; set; }
    public decimal OrderTotal { get; set; }
}

在项目中创建一个Services文件夹,在Services文件夹中添加如下IProductService和ProductService。对于本教程,我只是返回一些假数据来生成表。

IProductService.cs

public interface IProductService
{
    List<Product> GetTopSellingProducts();
}

ProductService.cs

public class ProductService : IProductService
{
    public List<Product> GetTopSellingProducts()
    {
        return new List<Product>()
        {
            new Product()
            {
                Id = 1,
                Title = "Wireless Mouse",
                Price = 29.99m,
                Quantity = 3
            },
            new Product()
            {
                Id = 2,
                Title = "HP Headphone",
                Price = 79.99m,
                Quantity = 4
            },
            new Product()
            {
                Id = 3,
                Title = "Sony Keyboard",
                Price = 119.99m,
                Quantity = 5
            }
        };
    }
}

接下来,在同一个 Services 文件夹中创建 IOrderService 和 OrderService 并添加一些假订单数据以生成一个表。

IOrderService.cs

ublic interface IOrderService
{
    List<Order> GetLatestOrders();
}

OrderService.cs

public class OrderService : IOrderService
{
    public List<Order> GetLatestOrders()
    {
        return new List<Order>()
        {
            new Order()
            {
                Id = 1, 
                OrderNo = "12345",
                OrderDate = DateTime.Today.AddDays(-2),
                Status = "Pending",
                OrderTotal = 399.99m
            },
            new Order()
            {
                Id = 2,
                OrderNo = "67890",
                OrderDate = DateTime.Today.AddDays(-5),
                Status = "Completed",
                OrderTotal = 199.99m
            },
            new Order()
            {
                Id = 3,
                OrderNo = "13579",
                OrderDate = DateTime.Today.AddDays(-7),
                Status = "Completed",
                OrderTotal = 249.99m
            }
        };
    }
}

我们需要使用依赖注入将上述服务注入到 Blazor 组件中,为此,我们需要在 Startup.cs 文件中注册上述服务。

Startup.cs

public void ConfigureServices(IServiceCollection services)
{
    services.AddRazorPages();
    services.AddServerSideBlazor();
    services.AddSingleton<WeatherForecastService>();
 
    services.AddScoped<IOrderService, OrderService>();
    services.AddScoped<IProductService, ProductService>();
}

接下来,在项目 Pages 文件夹中创建 Blazor 组件 Dashboard.razor 及其对应的代码隐藏文件 Dashboard.razor.cs。如果您不熟悉 Blazor 组件和代码隐藏文件,请阅读我的文章 Blazor 组件初学者指南。

Dashboard.razor.cs 文件将在组件的代码隐藏文件中注入 IOrderService 和 IProductService,然后我们将使用 GetLatestOrders 和 GetTopSellingProducts 方法来填充我们的本地订单和产品列表。

Dashboard.razor.cs

public partial class Dashboard
{
    [Inject]
    private IOrderService OrderService { get; set; }
 
    [Inject]
    private IProductService ProductService { get; set; }
 
    private List<Order> Orders { get; set; }
    private List<Product> Products { get; set; }
 
    protected override void OnInitialized()
    {
        Orders = OrderService.GetLatestOrders();
        Products = ProductService.GetTopSellingProducts();
    }
}

razor 组件视图文件将简单地在 OrdersProducts 上运行 foreach 循环,并将生成 HTML 表。

@page "/dashboard"
<h1>Dashboard</h1>
<br />
<div class="row">
   <div class="col">
      @if (Orders != null)
      {
          <table class="table table-striped table-bordered">
             <thead class="thead-dark">
                <tr>
                   <th scope="col">Order</th>
                   <th scope="col">Date</th>
                   <th scope="col">Status</th>
                   <th scope="col">Total</th>
                </tr>
             </thead>
             <tbody>
                @foreach (var order in Orders)
                {
                    <tr>
                       <td>@order.OrderNo</td>
                       <td>@order.OrderDate.ToShortDateString()</td>
                       <td>@order.Status</td>
                       <td>@order.OrderTotal</td>
                    </tr>
                }
             </tbody>
          </table>
      }
   </div>
   <div class="col">
      @if (Products != null)
      {
          <h3>Top Selling Products</h3>
          <table class="table table-striped table-bordered">
             <thead class="thead-dark">
                <tr>
                   <th scope="col">Title</th>
                   <th scope="col">Price</th>
                   <th scope="col">Quantity</th>
                </tr>
             </thead>
             <tbody>
                @foreach (var product in Products)
                {
                    <tr>
                       <td>@product.Title</td>
                       <td>@product.Price</td>
                       <td>@product.Quantity</td>
                    </tr>
                }
             </tbody>
          </table>
      }
   </div>
</div>

如果您将运行该项目,您将在页面上看到以下两个表格。在这里插入图片描述
到目前为止,我们还没有创建任何模板化组件,但你会觉得我们很快就需要一个,因为上面显示的订单和产品表具有几乎相同的外观和感觉,我们正在上面的 foreach 循环中复制大量 HTML 以生成这两个表。创建一个模板化组件,然后重用该组件来生成上述两个表,并且仍然能够自定义由这些表呈现的标题和数据行,这是一个好主意。让我们创建我们的第一个模板化组件,称为 TableWidget 组件。

创建 Blazor 模板化组件

在 Shared 文件夹中添加一个新的 Razor 组件 TableWidget.razor 并在其中添加以下代码。

TableWidget.razor

@typeparam TItem
<br />
<h3>@Title</h3>
<table class="table table-striped table-bordered">
   <thead class="thead-dark">
      <tr>
         @HeaderTemplate
      </tr>
   </thead>
   <tbody>
      @foreach (var item in Items)
      {
      <tr>
         @RowTemplate(item)
      </tr>
      }
   </tbody>
   <tfoot>
      <tr>
         @FooterTemplate
      </tr>
   </tfoot>
</table>
@code {
    [Parameter]
    public string Title { get; set; }
 
    [Parameter]
    public RenderFragment HeaderTemplate { get; set; }
 
    [Parameter]
    public RenderFragment<TItem> RowTemplate { get; set; }
 
    [Parameter]
    public RenderFragment FooterTemplate { get; set; }
 
    [Parameter]
    public IReadOnlyList<TItem> Items { get; set; }
}

我们的 TableWidget 组件具有以下三个模板。

[Parameter]
public RenderFragment HeaderTemplate { get; set; }
 
[Parameter]
public RenderFragment<TItem> RowTemplate { get; set; }
 
[Parameter]
public RenderFragment FooterTemplate { get; set; }

HeaderTemplate 将允许用户在表的标题中呈现任何 UI 模板。此模板用于呈现 thead 元素中的表格标题单元格。

<thead class="thead-dark">
   <tr>
      @HeaderTemplate
   </tr>
</thead>

FooterTemplate 类似于 HeaderTemplate,它允许用户在表格的页脚中呈现任何 UI 模板。此模板用于呈现 tfoot 元素中的表格页脚单元格。

<tfoot>
   <tr>
      @FooterTemplate
   </tr>
</tfoot>

RowTemplate 属于 RanderFragment 类型,它将允许用户使用任何 .NET 类型呈现 UI 模板。该类型不是固定的,而是使用组件顶部的 @typeparam 指令声明为泛型类型。

@typeparam TItem

我们还在组件中创建了一个 TItem 对象的集合,以便我们可以迭代该集合并生成我们的表行

[Parameter]
public IReadOnlyList<TItem> Items { get; set; }

我们将在 UI 模板中传递的对象类型将使用以下 foreach 循环呈现。您很快就会看到这将如何帮助我们使用相同的 TableWidget 组件呈现 Products 和 Order 表。

<tbody>
   @foreach (var item in Items)
   {
       <tr>
          @RowTemplate(item)
       </tr>
   }
</tbody>

使用 Blazor 模板化组件的不同方式

现在是时候看看我们的 TableWidget 组件的运行情况了,我们可以通过不同的方式使用这个组件。用下面的 TableWidget 组件替换我们上面生成的最近订单表。

<div class="col">
   @if (Orders != null)
   {
       <TableWidget Title="Recent Orders" Items="Orders">
          <HeaderTemplate>
             <th scope="col">Order</th>
             <th scope="col">Date</th>
             <th scope="col">Status</th>
             <th scope="col">Total</th>
          </HeaderTemplate>
          <RowTemplate>
             <td>@context.OrderNo</td>
             <td>@context.OrderDate.ToShortDateString()</td>
             <td>@context.Status</td>
             <td>@context.OrderTotal</td>
          </RowTemplate>
       </TableWidget>
   }
</div>

在上面的代码片段中,Items 属性是用我们从我们的服务收到的 Orders 列表初始化的。然后我们决定使用 HeaderTemplateRowTemplate 来生成表格的页眉和页脚。您可能正在思考上下文的来源。上下文是一个隐式参数,可用于类型 RenderFragment 的所有组件参数。我们可以使用上下文来访问我们正在处理的对象的属性。在上面的示例中,上下文将向模板提供订单信息。

如果您将运行该项目,您将在页面上看到以下两个表格。现在使用我们的 TableWidget 组件生成最近的订单表。在这里插入图片描述
让我们重用我们的 TableWidget 组件,这次生成 Top Selling Products 表。这一次,我们传递给它 Products 列表,并且我们还指定了我们自己的 Context=“product” 这意味着我们现在可以使用 product 而不是隐式参数 context 来访问产品属性。

<div class="col">
   @if (Products != null)
   {
       <TableWidget Title="Top Selling Products" Items="Products" Context="product">
          <HeaderTemplate>
             <th scope="col">Title</th>
             <th scope="col">Price</th>
             <th scope="col">Quantity</th>
          </HeaderTemplate>
          <RowTemplate>
             <td>@product.Title</td>
             <td>@product.Price</td>
             <td>@product.Quantity</td>
          </RowTemplate>
       </TableWidget>
   }
</div>

您可以在模板级别指定 Context,如示例中所示,其中 Context=”product” 添加到 RowTemplate

<TableWidget Title="Top Selling Products" Items="Products">
   <HeaderTemplate>
      <th scope="col">Title</th>
      <th scope="col">Price</th>
      <th scope="col">Quantity</th>
   </HeaderTemplate>
   <RowTemplate Context="product">
      <td>@product.Title</td>
      <td>@product.Price</td>
      <td>@product.Quantity</td>
   </RowTemplate>
</TableWidget>

如果您将运行该项目,您将看到页面上呈现了以下两个表,但这次我们知道这两个表是使用我们的 TableWidget 模板化组件呈现的。此示例清楚地表明,相同的模板化组件可用于生成不同类型的 UI,并且它可以根据我们的应用程序需求呈现不同类型的对象。在这里插入图片描述
让我们用另外两个示例重用我们的 TableWidget 组件,这些示例将显示相同的最近订单和热销产品,但布局略有不同

<div class="row">
   <div class="col">
      @if (Orders != null)
      {
          <TableWidget Title="Recent Orders" Items="Orders">
             <HeaderTemplate>
                <th scope="col" colspan="2">Order Details</th>
                <th scope="col">Status</th>
                <th scope="col">Total</th>
             </HeaderTemplate>
             <RowTemplate Context="order">
                <td colspan="2">
                   <b>Order No: </b>@order.OrderNo
                   <br />
                   <b>Order Date: </b>@order.OrderDate.ToShortDateString()
                </td>
                <td>@order.Status</td>
                <td>@order.OrderTotal</td>
             </RowTemplate>
          </TableWidget>
      }
   </div>
   <div class="col">
      @if (Products != null)
      {
          <TableWidget Title="Top Selling Products" Items="Products" TItem=”Product”>
             <RowTemplate Context="product">
                <td>
                   <h2>@product.Title</h2>
                   <h4><b>@product.Price.ToString("C")</b></h4>
                </td>
             </RowTemplate>
             <FooterTemplate>
                <td class="text-right"><b>Last 30 Days</b></td>
             </FooterTemplate>
          </TableWidget>
      }
   </div>
</div>

使用泛型类型组件时,如果可能,将推断类型参数。但是,我们可以选择使用名称与类型参数匹配的属性显式指定类型,在上面的示例中为 TItem。

如果您将运行该项目,您将看到使用相同的 TableWidget 模板化组件在页面上呈现的所有四个表。在这里插入图片描述

创建通用模板组件

我们的 TableWidget 组件很好,我们已经看到了可以重用它的不同示例,但该组件的问题在于它只生成 HTML 表格。如果我们想创建一个更通用的组件,可以重用它来生成任何类型的 UI,例如表格、卡片、项目符号等。我们可以通过从模板化组件中删除所有标记来轻松创建这样的组件。

让我们创建一个通用的 ListWidget 组件来查看一个这样的组件的运行情况。 在 Shared 文件夹中创建一个新的 ListWidget.razor 组件,并在其中添加以下代码。这一次,我们在组件中没有标记,在 foreach 循环中只有一个 ItemTemplate。这意味着我们可以使用这个 ListWidget 组件自由生成任何类型的列表。

ListWidget.razor

@typeparam TItem
 
@foreach (var item in Items)
{
    @ItemTemplate(item)
}
 
@code {
    [Parameter]
    public RenderFragment<TItem> ItemTemplate { get; set; }
 
    [Parameter]
    public IReadOnlyList<TItem> Items { get; set; }
}

假设我们想使用这个 ListWidget 组件生成引导程序列表,所以我们可以使用以下代码片段来做到这一点。

<ul class="list-group">
   <li class="list-group-item d-flex justify-content-between align-items-center active">
      Latest Products
   </li>
   <ListWidget Items="Products" Context="product">
      <ItemTemplate>
         <li class="list-group-item d-flex justify-content-between align-items-center">
            @product.Title
            <b>@product.Price.ToString("C")</b>
            <span class="badge badge-primary badge-pill">
            @product.Quantity
            </span>
         </li>
      </ItemTemplate>
   </ListWidget>
</ul>

运行该项目,您将看到现在生成的相同产品列表作为引导列表组件。在这里插入图片描述
现在假设您有另一个页面,其中需要使用 div 和 a 标签以不同方式显示产品列表,因此您可以再次重用相同的 ListWidget 组件,这次生成如下标记:

<div class="list-group">
   <a class="list-group-item d-flex justify-content-between align-items-center active">
   Latest Products
   </a>
   <ListWidget Items="Products" Context="product" TItem="Product">
      <ItemTemplate>
         <a href="#" class="list-group-item list-group-item-action flex-column align-items-start">
            <div class="d-flex w-100 justify-content-between">
               <h5 class="mb-1"><b>@product.Title</b></h5>
               <small class="text-muted">@product.Quantity units left</small>
            </div>
            <p class="mb-1">@product.Price.ToString("C")</p>
         </a>
      </ItemTemplate>
   </ListWidget>
</div>

运行该项目,您将看到类似于以下内容的输出。在这里插入图片描述

总结概括

在本教程中,我概述了 Blazor 模板化组件,我们创建了两种类型的模板化组件。接下来,我们看到了几个重用 TableWidget 和 ListWidget 组件来生成不同类型标记的示例。我不得不承认模板化组件是 Blazor 开发人员工具箱的一个很好的补充,使用这些组件我们可以创建一些惊人的可重用组件。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值