Blazor 模板化组件开发指南
翻譯自 Waqas Anwar 2021年4月15日的文章?《A Developer’s Guide To Blazor Templated Components》?[1]
在我之前的一篇文章?Blazor 組件入門指南中,我介紹了組件參數(shù),并向您展示了如何將數(shù)據(jù)作為參數(shù)傳遞給 Blazor 組件以定制化其功能。在這篇文章中,我將更進一步向您展示,如何將一個或多個 UI 模板作為參數(shù)傳遞給一個稱之為模板化組件的不同類型的 Blazor 組件。
下載源碼[2]
Blazor 模板化組件概述
Blazor 模板化組件是一種接受將一個或多個 UI 模板作為參數(shù)的組件。這有助于組件的可重用性,因為您只需要創(chuàng)建一次模板化組件,然后使用該組件的每個頁面都可以提供其 UI 模板,模板化組件可以根據(jù)頁面需求渲染此 UI 模板。
本文中的模板化組件示例包括:
一個允許用戶指定表格表頭、行和頁腳模板的表格組件。
一個允許用戶呈現(xiàn)具有相同外觀和體驗而具有不同內(nèi)容的小部件組件。
一個允許用戶指定一個模板來呈現(xiàn)項目符號或編號等列表項的列表組件。
一個允許用戶以列表、網(wǎng)格或卡片視圖來顯示數(shù)據(jù)的列表組件。
當我們創(chuàng)建 Blazor 組件的一個參數(shù)時,我們通常將其類型指定為?string、int?或者其他內(nèi)置 .NET 數(shù)據(jù)類型。為了創(chuàng)建一個模板化組件,我們需要創(chuàng)建類型為?RenderFragment?或?RenderFragment<T>?的組件參數(shù)。RenderFragment?允許我們提供一個可以由模板化組件渲染的 UI 內(nèi)容片段(作為一個委托實現(xiàn),將其內(nèi)容寫入到 RenderTreeBuilder)。
[Parameter] public RenderFragment HeaderTemplate { get; set; }RenderFragment<T>?更進一步,允許我們傳入?yún)?shù)的類型?T,可以用它來自定義模板化組件的輸出。
[Parameter] public RenderFragment<T> RowTemplate { get; set; }從一個實例開始
為了詳細了解模板化組件,我決定構(gòu)建一個?TableWidget?模板化組件,它允許我們自定義不同格式的表頭、行和頁腳。在創(chuàng)建第一個模板化組件之前,我們先來創(chuàng)建一個新的 Blazor Server 應(yīng)用程序并添加其基本功能,以表格格式呈現(xiàn)一些數(shù)據(jù)。
在 Blazor Server 應(yīng)用程序中創(chuàng)建一個?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; } }在項目中創(chuàng)建一個?Services?文件夾,并在?Services?文件夾中添加如下的?IProductService?和?ProductService。在本教程中,我僅返回一些用于生成表格的模擬數(shù)據(jù)。
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?文件夾中創(chuàng)建?IOrderService?和?OrderService?并添加一些用于生成表格的模擬訂單數(shù)據(jù)。
IOrderService.cs
public 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}};} }我們需要使用依賴注入將上述服務(wù)注入到 Blazor 組件中,為此,我們需要在?Startup.cs?文件中注冊上述服務(wù)。如果您想了解關(guān)于依賴注入的更多知識,可以閱讀我的文章?A Step by Step Guide to ASP.NET Core Dependency Injection[3]。
Startup.cs
public void ConfigureServices(IServiceCollection services) {services.AddRazorPages();services.AddServerSideBlazor();services.AddSingleton<WeatherForecastService>();services.AddScoped<IOrderService, OrderService>();services.AddScoped<IProductService, ProductService>(); }接下來,在項目?Pages?文件夾中創(chuàng)建 Blazor 組件?Dashboard.razor?及其對應(yīng)的代碼隱藏文件?Dashboard.razor.cs。如果您不熟悉 Blazor 組件及代碼隱藏文件,請閱讀我的文章?Blazor 組件入門指南。
組件的代碼隱藏文件?Dashboard.razor.cs?中同時注入了?IOrderService?和?IProductService,然后我們將使用?GetLatestOrders?和?GetTopSellingProducts?方法來填充我們的本地?Orders?和?Products?列表。
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 組件視圖文件將簡單地在?Orders?和?Products?上運行?foreach?循環(huán),并生成 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>此時如果您運行項目,將在頁面上看到以下兩個表格。
截至目前,我們尚沒有創(chuàng)建任何模板化組件,但您會感覺到我們很快將需要一個,因為上面顯示的訂單和產(chǎn)品表格幾乎都具有相同的外觀和體驗,并且我們在上面的?foreach?循環(huán)中復(fù)制了大量的 HTML 來生成這兩張表格。一個好注意是,創(chuàng)建一個模板化組件,然后重用該組件來生成上述兩張表格,并且仍然能夠自定義它們顯示的表頭和數(shù)據(jù)行。讓我們來創(chuàng)建我們的第一個模板化組件,命名為?TableWidget?組件。
創(chuàng)建 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?允許用戶在表格的表頭中呈現(xiàn)任意 UI 模板。此模板用于在?thead?元素內(nèi)渲染表格表頭的單元格。
<thead class="thead-dark"><tr>@HeaderTemplate</tr> </thead>FooterTemplate?與?HeaderTemplate?類似,它允許用戶在表格的頁腳中呈現(xiàn)任意 UI 模板。此模板用于在?tfoot?元素內(nèi)渲染表格頁腳的單元格。
<tfoot><tr>@FooterTemplate</tr> </tfoot>RowTemplate?的類型為?RanderFragment<TItem>,它允許用戶使用任意的 .NET 類型渲染 UI 模板。該類型不是固定的,而是使用組件頂部的?@typeparam?指令聲明為一個泛型類型。
@typeparam TItem我們還在組件中創(chuàng)建了一個?TItem?對象的集合,以便我們可以迭代該集合生成表格的行。
[Parameter] public IReadOnlyList<TItem> Items { get; set; }我們將要傳入 UI 模板中的?TItem?類型的對象會使用以下?foreach?循環(huán)進行渲染。您很快就會看到這將如何幫助我們使用相同的 TableWidget 組件同時渲染產(chǎn)品和訂單表格。
<tbody>@foreach (var item in Items){<tr>@RowTemplate(item)</tr>} </tbody>使用 Blazor 模板化組件的不同方式
現(xiàn)在是時候來實踐一下我們的 TableWidget 組件了,我們可以通過不同的方式使用這個組件。用下面的 TableWidget 組件替換我們前面生成的?Recent Orders?表格。
<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?屬性是使用我們的從服務(wù)獲取的?Orders?列表進行初始化的。然后我們選擇使用?HeaderTemplate?和?RowTemplate?來生成表格的表頭和數(shù)據(jù)行。您可能在想?context?是從哪里來的?context?是一個隱式參數(shù),所有類型為?RenderFragment<T>?的組件參數(shù)都可以使用。我們可以使用?context?訪問我們正在處理對象的屬性。在上面的示例中,context?將向模板提供訂單信息。
如果此時您運行項目,會在頁面上看到以下兩個表格?,F(xiàn)在,最近的訂單(Recent Orders)表格是使用我們的 TableWidget 組件生成的了。
讓我們重用?TableWidget?組件來生成熱賣產(chǎn)品(Top Selling Products)表格。這一次,我們傳遞了 Products 列表給它,還指定了我們自己的?Context="product",這意味著現(xiàn)在我們可以使用?product?取代隱式參數(shù)?context?來訪問產(chǎn)品的屬性。
<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>現(xiàn)在如果您運行該項目,您將看到頁面上顯示了以下兩個表格,但是我們知道這次這兩個表格是使用我們的模板化組件 TableWidget 渲染的。該示例清楚地演示了,同一個模板化組件可用于生成不同類型的 UI,并且可以根據(jù)我們的應(yīng)用程序需求渲染不同類型的對象。
下面讓我們通過另外兩個例子重用一下我們的 TableWidget 組件,它們將顯示同樣的最近訂單(Recent Orders)和熱銷產(chǎn)品(Top Selling Products),但布局略有改變。
<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>在使用泛型類型組件時,會盡可能推斷類型參數(shù)。不過,我們可以選擇使用一個特性來顯式指定類型,該特性的名稱與類型參數(shù)相同,在上面的示例中是?TItem。
此時如果您運行該項目,您將在頁面上看到使用同一個 TableWidget 模板化組件渲染的全部四個表格。
創(chuàng)建通用模板化組件
我們的?TableWidget?組件很好,我們已見識了重用它的多個示例,但該組件的問題是它只生成了 HTML 表格。如果我們想要創(chuàng)建一個更通用的組件,可以重用它來生成任何類型的 UI(比如:表格、卡片、項目符號等)。我們可以通過從模板化組件中刪除所有的標簽來輕松地創(chuàng)建這樣一個組件。讓我們來創(chuàng)建一個通用的?ListWidget?組件,來實戰(zhàn)練習一下這種組件。
在?Shared?文件夾中創(chuàng)建一個新的?ListWidget.razor?組件,并在其中添加以下代碼。這次在組件中沒有 HTML 標簽,在?foreach?循環(huán)中僅有一個?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 組件生成 bootstrap 列表,那么我們可以使用下面的代碼段來實現(xiàn)這一操作。
<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>運行該項目,您將看到以 bootstrap 列表方式生成的相同產(chǎn)品的列表。
現(xiàn)在,假設(shè)您有另一個頁面,其中需要使用?div?和?a?標簽以不同形式展示產(chǎn)品列表,那么您可以再次重用相同的 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>運行該項目,您將看到類似以下內(nèi)容的輸出。
總結(jié)
在本教程中,我概述了 Blazor 模板化組件,并創(chuàng)建了兩種類型的模板化組件。然后,我們實踐了幾個重用 TableWidget 和 ListWidget 組件來生成不同類型標記的例子。我不得不承認,模板化組件是 Blazor 開發(fā)者工具箱中的一個很好的補充,我們可以使用這些組件創(chuàng)建一些令人驚嘆的可重用組件。
相關(guān)閱讀:
Blazor Server 和 WebAssembly 應(yīng)用程序入門指南
Blazor 組件入門指南
Blazor 數(shù)據(jù)綁定開發(fā)指南
Blazor 事件處理開發(fā)指南
Blazor 組件之間使用 EventCallback 進行通信
Blazor 路由及導(dǎo)航開發(fā)指南
Blazor 模板化組件開發(fā)指南(本文)
相關(guān)鏈接:
https://www.ezzylearning.net/tutorial/a-developers-guide-to-blazor-templated-components?A Developer’s Guide To Blazor Templated Components???
https://github.com/ezzylearning/BlazorTemplatedComponentDemo?下載源碼???
https://www.ezzylearning.net/tutorial/a-step-by-step-guide-to-asp-net-core-dependency-injection?A Step by Step Guide to ASP.NET Core Dependency Injection???
作者 :Waqas Anwar
譯者 :技術(shù)譯民
出品 :技術(shù)譯站(https://ITTranslator.cn/)
總結(jié)
以上是生活随笔為你收集整理的Blazor 模板化组件开发指南的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: FastGithub让Github畅通无
- 下一篇: WPF等待动画