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.mzph.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;你已…