【Blazor】|?總結/Edison Zhou
大家好,我是Edison。
在之前的學習之旅(3)開發一個Todo應用中,我們開發了一個簡單版的Todo,這次我們基于MudBlazor來重構這個Todo應用。
Todo V1回顧
在Blazor入門學習(3)文章中,我們基于Blazor實現了一個簡單版的Todo應用,它的效果如下:
(1)加載Todo列表
(2)添加新的Todo事項
可以看到,它僅僅實現了最基本的效果,但是如果涉及到分頁、修改等操作,現有的界面就無法滿足了。
因此,我們基于對MudBlazor組件庫的了解,使用MudBlazor來重構一下這個Todo應用。
Todo V2規劃
我們首先來做一個規劃,期望效果是:
(1)能夠有一個分頁列表,能夠將MongoDB中的數據讀取出來并展示;
(2)能夠針對Todo Name進行篩選查詢;
(3)能夠有一個彈出框進行新增Todo Item;
(4)能夠有一個彈出框對選中的Todo Item進行修改;
(5)能夠對選中的Todo Item進行刪除,但要先給出確認刪除的提示;
最后,新增、修改和刪除操作成功都需要給出提示信息;
這里,我們以終為始,先來看看重構后的效果:
(1)分頁列表展示
(2)根據Todo Item Name進行搜索
(3)新增TodoItem
(4)修改TodoItem
(5)刪除TodoItem
Todo V2重構
(1)準備工作
在NavMenu.razor中新增一個導航菜單,名為TodoV2
<div class="@NavMenuCssClass" @onclick="ToggleNavMenu"><nav class="flex-column">.....<div class="nav-item px-3"><NavLink class="nav-link" href="todo"><span class="oi oi-list-rich" aria-hidden="true"></span> Todo</NavLink></div><div class="nav-item px-3"><NavLink class="nav-link" href="todov2"><span class="oi oi-list-rich" aria-hidden="true"></span>Todo v2</NavLink></div></nav>
</div>
(2)重構Todo列表頁
在Pages目錄下新增一個razor組件:TodoV2.razor,代碼如下:
@page "/todov2"
@using EDT.Todo.Application.Models.VO<PageTitle>Todo v2</PageTitle><MudTable Items="@todos" Dense="true" Bordered="true" Striped="true" Hover="true" Breakpoint="Breakpoint.Sm" Filter="new Func<TodoItemVO, bool>(FilterTodoItems)" @bind-SelectedItem="selectedTodoItem"><ToolBarContent><MudText Typo="Typo.h6">My TodoItems (Total: @todos.Count(), NotFinished: @todos.Count(todo => !todo.IsComplete))</MudText><MudSpacer /><MudTextField @bind-Value="searchKeyword" Placeholder="Search" Adornment="Adornment.Start"AdornmentIcon="@Icons.Material.Filled.Search" IconSize="Size.Medium" Class="mt-0" /><MudSpacer /><MudButton OnClick="@OpenCreateTodoDialog" Variant="Variant.Filled" StartIcon="@Icons.Material.Filled.Create" Color="Color.Primary">Create New</MudButton></ToolBarContent><HeaderContent><MudTh>Id</MudTh><MudTh>Name</MudTh><MudTh>Category</MudTh><MudTh>IsComplete</MudTh><MudTh>CheckItems</MudTh><MudTh>Action</MudTh></HeaderContent><RowTemplate><MudTd DataLabel="Id">@context.Id</MudTd><MudTd DataLabel="Name">@context.Name</MudTd><MudTd DataLabel="Category">@context.Category</MudTd><MudTd DataLabel="IsComplete"><MudSwitch @bind-Checked="@context.IsComplete" Color="Color.Primary" ReadOnly="true" /></MudTd><MudTd DataLabel="CheckItems">@String.Join(";", context.CheckItems)</MudTd><MudTd DataLabel="Action"><MudButton Variant="Variant.Filled" StartIcon="@Icons.Material.Filled.Update" Color="Color.Primary" OnClick="(async () => await OpenUpdateTodoDialog(context.Id))">Update</MudButton><MudButton Variant="Variant.Filled" StartIcon="@Icons.Material.Filled.Delete" Color="Color.Secondary" OnClick="(async () => await DeleteTodoItem(context.Id))">Delete</MudButton></MudTd></RowTemplate><PagerContent><MudTablePager /></PagerContent>
</MudTable>
新增對應的C#代碼類:TodoV2.razor.cs
using Microsoft.AspNetCore.Components;
using MongoDB.Driver;
using MudBlazor;
using EDT.Todo.Application.Contracts.Business;
using EDT.Todo.Application.Models.VO;
using EDT.Todo.Portal.Shared;namespace EDT.Todo.Portal.Pages
{public partial class TodoV2{[Inject]public ISnackbar Snackbar { get; set; }[Inject]public IDialogService DialogService { get; set; }[Inject]public ITodoItemService TodoItemService { get; set; }[Inject]public ILogger<TodoV2> Logger { get; set; }private IList<TodoItemVO> todos = Array.Empty<TodoItemVO>();private string searchKeyword = string.Empty;private TodoItemVO selectedTodoItem = null;protected override async Task OnInitializedAsync(){todos = await TodoItemService.GetTodoItems(ReadPreference.SecondaryPreferred);}private bool FilterTodoItems(TodoItemVO todoItem) => FilterTodoItemDetail(todoItem, searchKeyword);private bool FilterTodoItemDetail(TodoItemVO todoItem, string searchString){if (string.IsNullOrWhiteSpace(searchString))return true;if (todoItem.Name.Contains(searchString))return true;if (todoItem.CheckItems.Contains(searchString))return true;return false;}private async Task DeleteTodoItem(string id){var parameters = new DialogParameters();parameters.Add("ContentText", "Do you really want to delete this todoitem?");parameters.Add("ButtonText", "Delete");parameters.Add("Color", Color.Error);var options = new DialogOptions() { CloseButton = true, MaxWidth = MaxWidth.ExtraSmall };var dialog = DialogService.Show<ConfirmDialog>("Delete", parameters, options);var result = await dialog.Result;if (result.Cancelled || result.Data == null)return;try{// Delete TodoItemawait TodoItemService.DeleteTodoItem(id);// Refresh Todostodos = await TodoItemService.GetTodoItems(ReadPreference.PrimaryPreferred);// Sucess TipSnackbar.Add("Delete todoitem success!", Severity.Success);}catch (Exception ex){Logger.LogError(ex, $"An exception happened during DeleteTodoItem {id}");// Failed TipSnackbar.Add("Delete todoitem failed!", Severity.Error);}}private async Task OpenCreateTodoDialog(){DialogOptions closeOnEscapeKey = new DialogOptions() { CloseOnEscapeKey = true };var dialog = DialogService.Show<CreateTodoDialog>("Create Todo", closeOnEscapeKey);var result = await dialog.Result;if (result.Cancelled || result.Data == null)return;// Refresh Todostodos = await TodoItemService.GetTodoItems(ReadPreference.PrimaryPreferred);// Sucess TipSnackbar.Add("Create todoitem success!", Severity.Success);}private async Task OpenUpdateTodoDialog(string todoItemId){DialogOptions closeOnEscapeKey = new DialogOptions() { CloseOnEscapeKey = true };var parameters = new DialogParameters { ["TodoItemId"] = todoItemId };var dialog = DialogService.Show<UpdateTodoDialog>("Update Todo", parameters, closeOnEscapeKey);var result = await dialog.Result;if (result.Cancelled || result.Data == null)return;// Refresh Todostodos = await TodoItemService.GetTodoItems(ReadPreference.PrimaryPreferred);// Sucess TipSnackbar.Add("Update todoitem success!", Severity.Success);}}
}
在Todo列表頁中,可以看到在Create和Update以及Delete時都進行了彈框操作,因此我們還需要實現幾個Dialog。
(3)開發CreateTodoDialog
在CreateTodoDialog中,使用到了DialogContext 和 MudForm兩個重要的標簽,以很少的代碼實現了一個原本需要用JS實現的對話框。
@using EDT.Todo.Domain.Enums<MudDialog><DialogContent><MudForm @ref="form" @bind-IsValid="@success"><MudTextField T="string" Label="Name" @bind-Value="_todoItemDTO.Name"Required="true" RequiredError="Name is required!" Immediate="true"></MudTextField><MudSelect T="Category" Label="Category" AnchorOrigin="Origin.BottomCenter"@bind-Value="_todoItemDTO.Category">@foreach (var category in Enum.GetValues<Category>()){<MudSelectItem T="Category" Value="category">@category.ToString()</MudSelectItem>}</MudSelect><MudSwitch @bind-Checked="@_todoItemDTO.IsComplete" Color="Color.Primary" Label="Is Completed" /></MudForm></DialogContent><DialogActions><MudButton OnClick="Cancel">Cancel</MudButton><MudButton Color="Color.Primary" Disabled="@(!success)" OnClick="@CreateTodoItem">Create</MudButton></DialogActions>
</MudDialog>
對應的C#代碼類如下:CreateTodoDialog.razor.cs
public partial class CreateTodoDialog
{[Inject]public ITodoItemService TodoItemService { get; set; }[CascadingParameter] MudDialogInstance MudDialog { get; set; }private MudForm form;private bool success = false;private TodoItemDTO _todoItemDTO = new();private void Cancel() => MudDialog.Cancel();private async Task CreateTodoItem(){var result = await TodoItemService.CreateTodoItem(_todoItemDTO);MudDialog.Close(result);}
對于Dialog組件,默認需要一個級聯參數MudDialogInstance,因此需要將其放在代碼中。
此外,在此Dialog中還實現了調用Service類進行具體Create的操作。
(4)開發UpdateTodoDialog
開發完CreateTodoDialog后,UpdateTodoDialog就很簡單了,復制過來改一下就OK。當然,你也可以將這兩個操作放在同一個Dialog中進行。
@using EDT.Todo.Domain.Enums<MudDialog><DialogContent><MudForm @ref="form" @bind-IsValid="@success"><MudTextField T="string" Label="Name" @bind-Value="_todoItemVO.Name"Required="true" RequiredError="Name is required!" Immediate="true"></MudTextField><MudSelect T="Category" Label="Category" AnchorOrigin="Origin.BottomCenter"@bind-Value="_todoItemVO.Category">@foreach (var category in Enum.GetValues<Category>()){<MudSelectItem T="Category" Value="category">@category.ToString()</MudSelectItem>}</MudSelect><MudSwitch @bind-Checked="@_todoItemVO.IsComplete" Color="Color.Primary" Label="Is Completed" /></MudForm></DialogContent><DialogActions><MudButton OnClick="Cancel">Cancel</MudButton><MudButton Color="Color.Primary" Disabled="@(!success)" OnClick="@UpdateTodoItem">Update</MudButton></DialogActions>
</MudDialog>
對應的C#代碼類如下:UpdateTodoDialog.razor.cs
public partial class UpdateTodoDialog
{[Inject]public ITodoItemService TodoItemService { get; set; }[CascadingParameter] MudDialogInstance MudDialog { get; set; }[Parameter]public string TodoItemId { get; set; }private MudForm form;private bool success = false;private TodoItemVO _todoItemVO = new();private void Cancel() => MudDialog.Cancel();private async Task UpdateTodoItem(){var todoItemDTO = new TodoItemDTO{Id = TodoItemId,Name = _todoItemVO.Name,Category = _todoItemVO.Category,IsComplete = _todoItemVO.IsComplete};await TodoItemService.UpdateTodoItem(todoItemDTO);MudDialog.Close(todoItemDTO);}protected override async Task OnInitializedAsync(){if (string.IsNullOrWhiteSpace(TodoItemId))return;_todoItemVO = await TodoItemService.GetTodoItem(TodoItemId, ReadPreference.PrimaryPreferred);}
}
對于Update操作,需要Todo列表頁將需要修改的TodoItem的Id作為參數傳遞過來,并在初始化的時候調用Service進行數據的獲取 和 綁定。
(5)開發通用ConfirmDialog
對于ConfirmDialog而言,它本身并沒有任何邏輯,而且可以被任意頁面進行復用,只是提示的消息內容不同而已。
因此,我們在Shared目錄下創建一個ConfirmDialog.razor:
<MudDialog><DialogContent><MudText>@ContentText</MudText></DialogContent><DialogActions><MudButton OnClick="Cancel">Cancel</MudButton><MudButton Color="@Color" Variant="Variant.Filled" OnClick="Submit">@ButtonText</MudButton></DialogActions>
</MudDialog>
@code {[CascadingParameter] MudDialogInstance MudDialog { get; set; }[Parameter] public string ContentText { get; set; }[Parameter] public string ButtonText { get; set; }[Parameter] public Color Color { get; set; }void Submit() => MudDialog.Close(DialogResult.Ok(true));void Cancel() => MudDialog.Cancel();
}
由于該頁面代碼很簡單,我們就直接將其放在同一個razor中,不區分前后端的部分類。
小結
本篇,我們試著將之前的Todo應用使用MudBlazor來重構一下,相比之前會有一些互動了,但也僅僅是展示了最基本的界面。實際上,我們可以基于MudBlazor開發更加好看一點的界面和互動效果,這就等待你自己去探索了。
下一篇,我們學習在Blazor如何實現本地化及多語言支持。
參考資料
MudBlazor官網Doc文檔
年終總結:Edison的2021年終總結
數字化轉型:我在傳統企業做數字化轉型
C#刷題:C#刷劍指Offer算法題系列文章目錄
.NET面試:.NET開發面試知識體系
.NET大會:2020年中國.NET開發者大會PDF資料