Blazor学习之旅(3)实现一个Todo应用

5fd22d2ba9541602a01176401d69aa6b.jpeg

【Blazor】| 总结/Edison Zhou


大家好,我是Edison。

最近在学习Blazor做全栈开发,因此根据老习惯,我会将我的学习过程记录下来,一来体系化整理,二来作为笔记供将来翻看。

本篇,我们通过一个简单的Todo示例应用来介绍如何实现基础的数据绑定和事件。

添加Todo组件

在Pages目录下,新增一个Razor组件,命名:Todo.razor

@page "/todo"<h3>Todo</h3>@code {}

将Todo组件添加到导航栏

我们知道,在Shared目录下的NavMenu组件用于应用的导航,因此我们需要将Todo组件加进去以便可以访问到:

<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></nav>
</div>

这时导航栏中也就有Todo了:

e38116db97239154aa57a86847a02b91.png

添加Model

添加一个Models目录,在此目录下新建一个TodoItem类:

namespace EDT.BlazorServer.App.Models
{public class TodoItem{public string Id { get; set; }public string? Name { get; set; }public bool IsComplete { get; set; }public string? Remark { get; set; }}
}

为了模拟实现数据库访问的效果,这里我们使用EF Core的内存数据库来模拟。

首先,添加对Microsoft.EntityFrameworkCore.InMemory的应用。

其次,在Models目录下创建一个TodoContext类:

using Microsoft.EntityFrameworkCore;namespace EDT.BlazorServer.App.Models
{public class TodoContext : DbContext{public TodoContext(DbContextOptions<TodoContext> options): base(options){}public DbSet<TodoItem> TodoItems { get; set; }}
}

然后,在Program.cs中注入这个DbContext:

// Add database context
builder.Services.AddDbContext<TodoContext>(opt =>opt.UseInMemoryDatabase("TodoList"));

添加种子数据

为了方便演示,我们提前准备一些SeedData,创建一个SeedData的静态类:

namespace EDT.BlazorServer.App.Models
{public static class SeedData{public static void Initialize(TodoContext db){var todos = new TodoItem[]{new TodoItem { Id = Guid.NewGuid().ToString(), Name = "Study Computer Network", IsComplete=false, Remark = "Take a Test" },new TodoItem { Id = Guid.NewGuid().ToString(), Name = "Study Operation System", IsComplete=false, Remark = "Take a Test" },new TodoItem { Id = Guid.NewGuid().ToString(), Name = "Study Data Structure", IsComplete=false, Remark = "Take a Test" },new TodoItem { Id = Guid.NewGuid().ToString(), Name = "Walk the dog", IsComplete=true, Remark = string.Empty },new TodoItem { Id = Guid.NewGuid().ToString(), Name = "Run 5km in 40mins", IsComplete=true, Remark = string.Empty },};db.TodoItems.AddRange(todos);db.SaveChanges();}}
}

然后,在Program.cs中确保运行这个初始化操作:

添加Service

假设我们所有的TodoItem都是通过Service来完成的,不直接在Pages下的组件中来操作。

首先,创建一个接口ITodoItemService:

using EDT.BlazorServer.App.Models;namespace EDT.BlazorServer.App.Service.Contracts
{public interface ITodoItemService{Task<IList<TodoItem>> GetTodoItemsAsync();Task<TodoItem> GetTodoItemAsync(string id);Task<TodoItem> AddTodoItemAsync(TodoItem todoItem);Task<TodoItem> UpdateTodoItemAsync(TodoItem todoItem);Task<TodoItem> DeleteTodoItemAsync(TodoItem todoItem);}
}

这时,我们重新启动应用就可以看到Counter组件显示在主页上面了:

0513d4e89c47327a8f8850df7a5a5601.png

其次,实现TodoItemService:

using EDT.BlazorServer.App.Models;
using EDT.BlazorServer.App.Service.Contracts;
using Microsoft.EntityFrameworkCore;namespace EDT.BlazorServer.App.Service
{public class TodoItemService : ITodoItemService{private readonly TodoContext _todoContext;public TodoItemService(TodoContext todoContext){_todoContext = todoContext;}public async Task<TodoItem> AddTodoItemAsync(TodoItem todoItem){todoItem.Id = Guid.NewGuid().ToString();todoItem.IsComplete = false;_todoContext.TodoItems.Add(todoItem);await _todoContext.SaveChangesAsync();return todoItem;}public async Task<TodoItem> DeleteTodoItemAsync(TodoItem todoItem){_todoContext.TodoItems.Remove(todoItem);await _todoContext.SaveChangesAsync();return todoItem;}public async Task<TodoItem> GetTodoItemAsync(string id){return await _todoContext.TodoItems.FirstOrDefaultAsync(t => t.Id == id);}public async Task<IList<TodoItem>> GetTodoItemsAsync(){return await _todoContext.TodoItems.ToListAsync();}public async Task<TodoItem> UpdateTodoItemAsync(TodoItem todoItem){_todoContext.TodoItems.Update(todoItem);await _todoContext.SaveChangesAsync();return todoItem;}}
}

完善Todo组件

这里,我们仿照FetchData组件添加一个表格 并 实现TodoItem的添加:

@page "/todo"
@using EDT.BlazorServer.App.Models
@using EDT.BlazorServer.App.Service.Contracts
@inject ITodoItemService todoItemService;<h3>Todo (@todos.Count(todo => !todo.IsComplete))</h3>@if (todos == null)
{<p><em>Loading...</em></p>
}
else
{<table class="table"><thead><tr><th>Id</th><th>Name</th><th>IsComplete</th><th>Remark</th></tr></thead><tbody>@foreach (var todo in todos){<tr><td>@todo.Id.ToString()</td><td>@todo.Name</td><td><input type="checkbox" @bind="todo.IsComplete" /></td><td>@todo.Remark</td></tr>}</tbody></table>
}<input placeholder="Todo Item Name (Necessary)" @bind="newTodoItemName" />
<input placeholder="Todo Item Remark (Optioinal)" @bind="newTodoItemRemark" />
<button @onclick="AddTodo">Add todo</button>@code {private IList<TodoItem> todos;private string? newTodoItemName;private string? newTodoItemRemark;protected override async Task OnInitializedAsync(){todos = await todoItemService.GetTodoItemsAsync();}private async void AddTodo(){if (string.IsNullOrWhiteSpace(newTodoItemName))return;var todoItem = new TodoItem { Name = newTodoItemName, Remark = newTodoItemRemark };await todoItemService.AddTodoItemAsync(todoItem);// Clear TextboxesnewTodoItemName = newTodoItemRemark = string.Empty;// Refresh Todostodos = await todoItemService.GetTodoItemsAsync();}
}

需要注意的是:

(1)通过@injec指令进行Service的注入,和常见的构造函数注入不同。

(2)通过重写OnInitializeAsync事件,进行数据的初始化,即从数据库中读取TodoItem的列表。这部分属于Blazor组件的生命周期范畴,这里不过多纠结即可。唯一需要了解的是,OnInitialized 和 OnInitializeAsync 事件是在做组件的初始化,它发生在参数注入完成之后(这里的ITodoItemService就是注入的参数)。

(3)除了foreach,Blazor还包含其他循环指令,例如 @for、@while 和 @do while。这些指令返回重复的标记块。它们的工作方式与等效的 C# for、while 和 do...while 循环类似。

到此,最终的项目结构如下图所示:

d85ba7c9284b810cf35ef456a7343c03.png

运行效果

运行起来的效果如下图所示:

(1)加载Todo列表

b232301479d76e96ad549b16c57b9bb6.png

(2)添加新的Todo事项

6f5362a864214cd26c07e4f85233b060.gif

小结

本篇,我们实现了一个Todo应用。

下一篇,我们学习一下在Blazor中数据是如何被共享的。

参考资料

Microsoft Docs,《使用Blazor生成Web应用》

d0f30ce176cf8bd88ab000ab66968694.gif

年终总结:Edison的2021年终总结

数字化转型:我在传统企业做数字化转型

C#刷题:C#刷剑指Offer算法题系列文章目录

.NET面试:.NET开发面试知识体系

.NET大会:2020年中国.NET开发者大会PDF资料

d917b6c01d8261171e19820beaf33d5c.png

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/282065.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

JavaScript 省市级联效果

为什么80%的码农都做不了架构师&#xff1f;>>> JavaScript 省市级联效果 1 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">2 <html xmlns"http…

20分钟完成Mac上的 LNMP 环境部署,优雅·高效开发(Docker方式)

一、前言 对于很多开发者来说&#xff0c;突然转到在 mac 系统开发&#xff0c;可能会非常难受&#xff0c;主要有以下几个原因 1、mac 上安装软件程序坑多&#xff0c;比如安装 PHP、Nginx&#xff0c;会存在很多使用 linux 或 windows 时没有遇到过的坑 2、对 mac 系统不熟…

可编程交换时代就在这里

在第一批网络处理器推出20年后&#xff0c;我们正在向完全可编程数据平面迈进&#xff0c;网络运营商在虚拟化方面的努力推动了这一趋势的发展。 大多数物理网络系统正在被运行在网络核心和边缘的服务器上的虚拟网络功能&#xff08;VNF&#xff09;取代。这些服务器一般采用通…

【智力大冲浪】

【智力大冲浪】riddle内存限制&#xff1a; 128M【题目描述】例 1 智力大冲浪&#xff08;riddle.pas&#xff09;。【题目描述】小伟报名参加中央电视台的智力大冲浪节目。本次挑战赛吸引了众多参赛者&#xff0c;主持人为了表彰大家的勇气&#xff0c;先奖励每个参赛者 m 元。…

调试 不弹出 小米_时隔六年,小米NFC碰碰贴复活,碰一下自动亮灯、联网、投屏...

有朋友问我&#xff0c;NFC除了刷门禁、刷公交、离线支付外&#xff0c;还能干什么&#xff1f;实际上NFC的应用场景远不止于此&#xff0c;今天就带大家开开眼界。今年是小米十周年&#xff0c;各种酷玩新品不断&#xff0c;其中“小米碰碰贴2”就是一款便宜又好玩的产品。小米…

微信小程序把玩(三)tabBar底部导航

为什么80%的码农都做不了架构师&#xff1f;>>> tabBar相对而言用的还是比较多的&#xff0c;但是用起来并没有难&#xff0c;在app.json中配置下tabBar即可&#xff0c;注意tabBar至少需要两个最多五个Item选项 主要属性&#xff1a; 对于tabBar整体属性设置&…

WPF 之列表分页控件

WPF 之列表分页控件控件名&#xff1a;WindowAcrylicBlur作者&#xff1a; WPFDevelopersOrg - 黄佳 | 驚鏵原文链接&#xff1a; https://github.com/WPFDevelopersOrg/WPFDevelopers框架使用大于等于.NET40。Visual Studio 2022。项目使用 MIT 开源许可协议。新建Paginat…

高并发下防止库存超卖解决方案

一、概述 目前网上关于防止库存超卖&#xff0c;我没找到可以支持一次购买多件的&#xff0c;都是基于一次只能购买一件做的秒杀方案&#xff0c;但是实际场景中&#xff0c;一般秒杀活动都是支持&#xff11;&#xff5e;&#xff15;件的&#xff0c;因此为了补缺&#xff0…

【几何/分治】【最短路】【数学期望】Day 10.24

1、斜率 可以证明如果两点之间还有一点的话那么原来的两个点连线一定不会是最大斜率 然后我就写了个沙茶分治………… 其实根据上面的推论只用枚举相邻的两个点&#xff0c;扫一遍就可以了 1 #include <cstdio>2 #include <algorithm>3 #include <iostream>4…

K8s 介绍

过去一段时间&#xff0c;公司事情比较多&#xff0c;现在稍微能好点&#xff0c;今天进一步验证自己K8S 集群环境&#xff0c;遇到不少问题&#xff0c; 发现从自己的master 上无法访问node 的pod&#xff0c; 然后一堆search 。 config 。。 [rootk8s-master ~]# systemctl s…

easypoi needmerge失效_EasyPOI简单用例,简单有效

用poi导出Excel表格&#xff0c;需要配置很多东西&#xff0c;也比较麻烦&#xff0c;这里使用poi的封装easypoi&#xff0c;可以快速配置&#xff0c;实现Excel或者word文件的导出。这里我们结合SpringMVC开发easypoi。1&#xff0c;导入以下3个.jar包:这里是springMVC和easyp…

禁止sethc.exe运行 防止3389的sethc后门

废话&#xff1a;在土司看到的一篇文章,发私信给那个哥们儿说让不让转载,结果还没回复我就在百度看到相同的文章。他自己也是转载的。这哥们儿ID迟早被ban 文章转载自:http://www.jb51.net/hack/64484.html 点“开始”&#xff0c;在“运行”中敲入gpedit.msc依次展开“用户配置…

Mac 与虚拟机中的linux集群共享文件目录设置

一、环境介绍 本机&#xff1a;Macos Big Sur系统 虚拟机软件&#xff1a;vmware-fusion 虚拟机上虚拟的linux - centos7 系统 二、实现的效果 在mac上创建一个/Users/SH-Server/vm-vagrant目录&#xff0c;作为之后和虚拟机linux系统 /data 文件夹的共享目录。 我们最终想…

jsp编程技术徐天凤课后答案_jsp编程技术教材课后习题.doc

jsp编程技术教材课后习题JSP编程技术习题集1.6 本 章 习 题思考题(1)为什么要为JDK设置环境变量&#xff1f;(2)Tomcat和JDK是什么关系&#xff1f;(3)什么是Web服务根目录、子目录、相对目录&#xff1f;如何配置虚拟目录&#xff1f;(4)什么是B/S模式&#xff1f;(5)JSP、Jav…

JVM知识(一)

java三大流&#xff1a;数据流、控制流、指令流 线程是执行程序的最小单元&#xff0c;一个线程中也有这些东西。 java 运行时数据区&#xff1a; 1.程序计数器 指向当前线程正在执行的字节码指令地址。如果此时从一个线程转为执行另一个线程&#xff0c;此时就会中断&#xff…

AWD-LSTM为什么这么棒?

摘要&#xff1a; AWD-LSTM为什么这么棒&#xff0c;看完你就明白啦&#xff01;AWD-LSTM是目前最优秀的语言模型之一。在众多的顶会论文中&#xff0c;对字级模型的研究都采用了AWD-LSTMs&#xff0c;并且它在字符级模型中的表现也同样出色。 本文回顾了论文——Regularizing …

Spread / Rest 操作符

Spread / Rest 操作符指的是 ...&#xff0c;具体是 Spread 还是 Rest 需要看上下文语境。 当被用于迭代器中时&#xff0c;它是一个 Spread 操作符&#xff1a;&#xff08;参数为数组&#xff09; function foo(x,y,z) {console.log(x,y,z); }let arr [1,2,3]; foo(...arr);…

python postman脚本自动化_如何用Postman做接口自动化测试

什么是自动化测试把人对软件的测试行为转化为由机器执行测试行为的一种实践。例如GUI自动化测试&#xff0c;模拟人去操作软件界面&#xff0c;把人从简单重复的劳动中解放出来本质是用代码去测试另一段代码&#xff0c;属于一种软件开发工作&#xff0c;已经开发完成的用例还必…

Mac上,为虚拟机集群上的每台虚拟机设置固定IP

一、环境介绍 本机&#xff1a;macOS系统 虚拟机软件&#xff1a;VMware Fusion 虚拟机上&#xff1a;centos7内核的Linux系统集群 二、为什么要为每台虚拟机设置固定ip 由于每次启动虚拟机&#xff0c;得到的ip可能不一样&#xff0c;这样对远程连接非常不友好&#xff0c…

朱晔的互联网架构实践心得S1E7:三十种架构设计模式(上)

设计模式是前人通过大量的实践总结出来的一些经验总结和最佳实践。在经过多年的软件开发实践之后&#xff0c;回过头来去看23种设计模式你会发现很多平时写代码的套路和OO的套路和设计模式里总结的类似&#xff0c;这也说明了你悟到的东西和别人悟到的一样&#xff0c;经过大量…