Blazor學習之旅 (9) 用MudBlazor重構Todo

cbb6d44539aec5ac2d49889e472fc844.jpeg

【Blazor】|?總結/Edison Zhou


大家好,我是Edison。

在之前的學習之旅(3)開發一個Todo應用中,我們開發了一個簡單版的Todo,這次我們基于MudBlazor來重構這個Todo應用。

Todo V1回顧

在Blazor入門學習(3)文章中,我們基于Blazor實現了一個簡單版的Todo應用,它的效果如下:

(1)加載Todo列表

ec0baa70a7581368d154c4a1c606db58.png

(2)添加新的Todo事項

b65e44bb5bbf4d9e6a24ceefcd0c1b18.png

可以看到,它僅僅實現了最基本的效果,但是如果涉及到分頁、修改等操作,現有的界面就無法滿足了。

因此,我們基于對MudBlazor組件庫的了解,使用MudBlazor來重構一下這個Todo應用。

Todo V2規劃

我們首先來做一個規劃,期望效果是:

(1)能夠有一個分頁列表,能夠將MongoDB中的數據讀取出來并展示;

(2)能夠針對Todo Name進行篩選查詢;

(3)能夠有一個彈出框進行新增Todo Item;

(4)能夠有一個彈出框對選中的Todo Item進行修改;

(5)能夠對選中的Todo Item進行刪除,但要先給出確認刪除的提示;

最后,新增、修改和刪除操作成功都需要給出提示信息;

這里,我們以終為始,先來看看重構后的效果:

(1)分頁列表展示

f5053fbb5a7eaae4ceaf6489e6498d1d.png

(2)根據Todo Item Name進行搜索

3e02449bff72b78604d90c312468cd92.png

(3)新增TodoItem

3b17e3e73a9fff51cbc5d6c3fa595167.png

2cd34166372a3897b54e180bce3fe22a.png

(4)修改TodoItem

f6156af0421ed23ed31ecefc8dc8d6d0.png

(5)刪除TodoItem

83e0aabc3f9765545efea4da1dcaa5a1.png

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文檔

7cf9260f4c34312745a1e6c8eacee0e8.gif

年終總結:Edison的2021年終總結

數字化轉型:我在傳統企業做數字化轉型

C#刷題:C#刷劍指Offer算法題系列文章目錄

.NET面試:.NET開發面試知識體系

.NET大會:2020年中國.NET開發者大會PDF資料

41b3e6008c6cf0ed635a84694b464edb.png

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/280329.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/280329.shtml
英文地址,請注明出處:http://en.pswp.cn/news/280329.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

50多種在Photoshop中刪除圖像背景的工具和技術,第3頁

We’re completing the 50 Tools and Techniques today with this final installment. Read about advanced selection and masking tools, as well as some stupid graphics geek tricks, and ways to fake removing a background in seconds. 我們今天最后一部分將完成50多種…

socket跟TCP/IP 的關系,單臺服務器上的并發TCP連接數可以有多少

常識一&#xff1a;文件句柄限制 在linux下編寫網絡服務器程序的朋友肯定都知道每一個tcp連接都要占一個文件描述符&#xff0c;一旦這個文件描述符使用完了&#xff0c;新的連接到來返回給我們的錯誤是“Socket/File:Cantopen so many files”。 這時你需要明白操作系統對可以…

SSPL的MongoDB再被拋棄,GUN Health也合流PostgreSQL

2019 年 2 月 12 日&#xff0c;紅帽官方發博稱&#xff0c;Red Hat Satellite 將擁抱PostgreSQL&#xff0c;并且不會支持 SSPL 許可的 MongoDB 新版本。無獨有偶&#xff0c;同一天GNU Health也發博稱GNU Health Federation Information System 將從MongoDB遷移到PostgreSQL&…

開源的 .NET 數據庫遷移框架

你好&#xff0c;這里是 Dotnet 工具箱&#xff0c;定期分享 Dotnet 有趣&#xff0c;實用的工具和組件&#xff0c;希望對您有用&#xff01;簡介FluentMigrator 是一個開源的數據庫遷移框架&#xff0c;可以幫助用戶在開發過程中保持數據庫的一致性。它提供了一個簡潔的 Flue…

在deepin上安裝YouCompleteMe

詳細安裝步驟在github上有&#xff0c;https://github.com/Valloric/YouCompleteMe&#xff0c;我這里是自己總結的簡化版安裝步驟。 步驟1.安裝Vundle 首先&#xff0c;clone到本地 git clone https://github.com/VundleVim/Vundle.vim.git ~/.vim/bundle/Vundle.vim把以下內容…

2015年IT領域里Docker和其它顛覆性的趨勢

本文講的是2015年IT領域里Docker和其它顛覆性的趨勢&#xff0c;【編者的話】文中作者介紹了2015年IT領域的一些顛覆性的趨勢&#xff0c;比如Docker將如何革新PaaS、IaaS等&#xff0c;Docker將如何顛覆虛擬化、私有云、配置管理。 2014年真是令人興奮的一年&#xff0c;這一年…

進化:從孤膽極客到高效團隊_極客狂:為什么這么多的網站無法使用打印樣式表?...

進化:從孤膽極客到高效團隊It never ceases to amaze me that people have to look for a link or a button that says “Print” on a web page, especially considering there’s a miracle technology that makes that step unnecessary. Sadly almost nobody uses it, even…

Iterator 和 for...of 循環

Iterator 和 for...of 循環 Iterator&#xff08;遍歷器&#xff09;意義 為Array、Object、Map、Set四種數據集合&#xff0c;提供統一的接口機制來處理所有不同的數據結構 。 任何數據結構&#xff0c;只要部署 Iterator 接口&#xff0c;就可以完成遍歷操作&#xff08;即依…

python簡單開發接口

1、首先需要安裝flask這個模塊&#xff1a;pip install flask。flask是個輕量級的接口開發框架2、開發接口有什么作用  1、mock接口&#xff0c;模擬一些接口&#xff0c;在別的接口沒有開發好的時候&#xff0c;需要用mock去模擬一些接口。  2、知道接口是怎么開發的&…

九哥聊Kestrel網絡編程第二章:開發一個Fiddler

推薦序之前在.NET 性能優化群內交流時&#xff0c;我們發現很多朋友對于高性能網絡框架有需求&#xff0c;需要創建自己的消息服務器、游戲服務器或者物聯網網關。但是大多數小伙伴只知道 DotNetty&#xff0c;雖然 DotNetty 是一個非常優秀的網絡框架&#xff0c;廣泛應用于各…

apple tv 開發_如何跨多臺Apple TV同步Apple TV的主屏幕

apple tv 開發If you have more than one Apple TV in your household, you probably know how annoying it is when you have to install Apple TV apps multiple times on each device. However, with the release of tvOS 11, that’s no longer the case. 如果您的家庭中有…

這些故事說的都是你——譯者帶你讀《硅谷革命》

作者 | 薛命燈 作為《硅谷革命》的譯者之一&#xff0c;同時也是一個擁有十余年軟件開發和架構經驗的工程師&#xff0c;當時我在決定是否接受重譯這本書的時候&#xff0c;幾乎是不假思索地答應了郭蕾&#xff08;本書重啟版發起人之一&#xff09;的提議&#xff0c;只因他的…

runc容器逃逸漏洞最強后續:應對之策匯總與熱點疑問解答

美國時間2019年2月11日晚&#xff0c;runc通過oss-security郵件列表披露了runc容器逃逸漏洞CVE-2019-5736的詳情。runc是Docker、CRI-O、Containerd、Kubernetes等底層的容器運行時&#xff0c;此次安全漏洞無可避免地會影響大多數Docker與Kubernetes用戶&#xff0c;也因此為整…

OOD之問題空間到解空間—附FP的建模

通常會被問到&#xff0c;什么事OOD&#xff0c;然后大部分人期待的答案比較死板&#xff0c;繼承、封裝、多態&#xff01;懂這個的人多的去了&#xff0c;有什么好問&#xff1f;回答出來的人是否拿著Java又去做一些面向過程的勾當&#xff1f; 計算機革命起源于機器&#xf…

com surrogate_什么是“ COM Surrogate”(dllhost.exe),為什么它在我的PC上運行?

com surrogateIf you poke around in your Task Manager, there’s a good chance you’ll see one or more “COM Surrogate” processes running on a Windows PC. These processes have the file name “dllhost.exe”, and are part of the Windows operating system. You’…

云計算時代,互聯網金融背后的想象空間

本文講的是云計算時代&#xff0c;互聯網金融背后的想象空間&#xff0c;【IT168評論】阿里巴巴在紐交所的開市鐘史無前例的由八位合作伙伴敲響&#xff0c;可見阿里對互聯網時代構筑起生態系統的堅持。這其中&#xff0c;由余額寶所敲開的互聯網金融熱潮的熱度持續不減&#x…

JavaScript數據結構與算法——集合

1.集合數據結構 集合是一組無序且唯一&#xff08;不能重復&#xff09;的項組成的。這個數據結構使用了和有限集合相同的數學概念。 2.創建集合 function Set() {// 這里使用對象而不是數組來表示集合 // js對象中不允許一個鍵值指向兩個不同屬性&#xff0c;也保證了集合中的…

php用兩個棧來實現隊列

php用兩個棧來實現隊列 一、總結 我主要的問題是不知道的是題目描述&#xff0c;題目和貴的代碼之間的關系&#xff0c;以及返回值 思路&#xff1a;A棧做入隊操作&#xff0c;B棧做出隊操作&#xff0c;入隊的時候元素直接入A&#xff0c;出隊的時候判斷B棧是否為空&#xff0…

facebook 邀請好友_如何查看緊急情況下您的Facebook朋友是否安全

facebook 邀請好友Facebook’s Safety Check feature lets you check in during an emergency to confirm you’re safe. If you have friends or family in an area that you haven’t heard from, though, you may want to ask them directly. Here’s how to ask someone to…

【您有一封來自阿里云的邀請函】阿里云成都客戶服務中心20+職位虛席以待,來吧,成就最好的自己!...

如果你不想辜負這個科技的時代&#xff0c;相信它會因你而不同。如果你不想僅做年度大戲的觀眾&#xff0c;相信自己會成為主角。如果你不想淹沒在枯燥與茍且中&#xff0c;相信工作有詩和遠方。那么&#xff0c;不要猶豫&#xff0c;加入我們&#xff01;在這&#xff0c;你已…