【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了:
添加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 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 循環類似。
到此,最終的項目結構如下圖所示:
運行效果
運行起來的效果如下圖所示:
(1)加載Todo列表
(2)添加新的Todo事項
小結
本篇,我們實現了一個Todo應用。
下一篇,我們學習一下在Blazor中數據是如何被共享的。
參考資料
Microsoft Docs,《使用Blazor生成Web應用》
年終總結:Edison的2021年終總結
數字化轉型:我在傳統企業做數字化轉型
C#刷題:C#刷劍指Offer算法題系列文章目錄
.NET面試:.NET開發面試知識體系
.NET大會:2020年中國.NET開發者大會PDF資料