前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Blazor学习之旅(9)用MudBlazor重构Todo

Blazor学习之旅(9)用MudBlazor重构Todo

作者头像
Edison Zhou
发布2023-08-02 08:41:11
2370
发布2023-08-02 08:41:11
举报
文章被收录于专栏:EdisonTalkEdisonTalk

大家好,我是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

代码语言:javascript
复制
<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,代码如下:

代码语言:javascript
复制
@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

代码语言:javascript
复制
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 TodoItem
                await TodoItemService.DeleteTodoItem(id);
                // Refresh Todos
                todos = await TodoItemService.GetTodoItems(ReadPreference.PrimaryPreferred);
                // Sucess Tip
                Snackbar.Add("Delete todoitem success!", Severity.Success);
            }
            catch (Exception ex)
            {
                Logger.LogError(ex, $"An exception happened during DeleteTodoItem {id}");
                // Failed Tip
                Snackbar.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 Todos
            todos = await TodoItemService.GetTodoItems(ReadPreference.PrimaryPreferred);
            // Sucess Tip
            Snackbar.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 Todos
            todos = await TodoItemService.GetTodoItems(ReadPreference.PrimaryPreferred);
            // Sucess Tip
            Snackbar.Add("Update todoitem success!", Severity.Success);
        }
    }
}

在Todo列表页中,可以看到在Create和Update以及Delete时都进行了弹框操作,因此我们还需要实现几个Dialog。

(3)开发CreateTodoDialog

在CreateTodoDialog中,使用到了DialogContext 和 MudForm两个重要的标签,以很少的代码实现了一个原本需要用JS实现的对话框。

代码语言:javascript
复制
@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

代码语言:javascript
复制
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中进行。

代码语言:javascript
复制
@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

代码语言:javascript
复制
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:

代码语言:javascript
复制
<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如何实现本地化及多语言支持。

本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文链接。

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2023-07-31,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • Todo V1回顾
  • Todo V2规划
  • Todo V2重构
    • (1)准备工作
      • (2)重构Todo列表页
        • (3)开发CreateTodoDialog
          • (4)开发UpdateTodoDialog
            • (5)开发通用ConfirmDialog
            • 小结
            相关产品与服务
            云数据库 MongoDB
            腾讯云数据库 MongoDB(TencentDB for MongoDB)是腾讯云基于全球广受欢迎的 MongoDB 打造的高性能 NoSQL 数据库,100%完全兼容 MongoDB 协议,支持跨文档事务,提供稳定丰富的监控管理,弹性可扩展、自动容灾,适用于文档型数据库场景,您无需自建灾备体系及控制管理系统。
            领券
            问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档