最近在学习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了:
添加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组件显示在主页上面了:
其次,实现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 Textboxes newTodoItemName = newTodoItemRemark = string.Empty; // Refresh Todos todos = await todoItemService.GetTodoItemsAsync(); } }
需要注意的是:
(1)通过@inject指令进行Service的注入,和常见的构造函数注入不同。
(2)通过重写OnInitializeAsync事件,进行数据的初始化,即从数据库中读取TodoItem的列表。这部分属于Blazor组件的生命周期范畴,这里不过多纠结即可。唯一需要了解的是,OnInitialized 和 OnInitializeAsync 事件是在做组件的初始化,它发生在参数注入完成之后(这里的ITodoItemService就是注入的参数)。
(3)除了foreach,Blazor还包含其他循环指令,例如 @for、@while 和 @do while。这些指令返回重复的标记块。它们的工作方式与等效的 C# for、while 和 do...while 循环类似。
到此,最终的项目结构如下图所示:
运行效果
运行起来的效果如下图所示:
(1)加载Todo列表
(2)添加新的Todo事项
小结
本篇,我们实现了一个Todo应用。
下一篇,我们学习一下在Blazor中数据是如何被共享的。
参考资料
Microsoft Learning,《使用Blazor生成Web应用》
作者:周旭龙
出处:https://edisonchou.cnblogs.com
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文链接。
标签:todoContext,Task,todoItem,之旅,Blazor,Todo,public,TodoItem From: https://www.cnblogs.com/edisonchou/p/edc_aspnet_blazor_learning_chap3.html