Blazor 組件之間使用 EventCallback 進行通信
本文轉載自微信公眾號「技術譯站」,作者技術譯民。轉載本文請聯系技術譯站公眾號。
Blazor 應用程序是相互交互的多個 Blazor 組件的集合,我們可以在其他父組件中使用子組件。在實際的應用程序中,將數據或事件信息從一個組件傳遞到另一組件是一種十分常見的場景。您可能會有一個頁面,其中一個組件中發生的用戶操作需要更新其他組件中的某些 UI。通常使用 EventCallback 委托來處理這種類型的通信。在本教程中,我將介紹如何使用 EventCallback 在父組件和子組件之間進行通信。
下面是使用 EventCallback 從子組件到父組件進行通信所涉及的通用步驟。
- 在子組件中聲明一個 EventCallback 或 EventCallback
委托 - 在父組件中附加一個到子組件的 EventCallback 或 EventCallback
的回調方法 - 當子組件想要與父組件通信時,可以使用以下方法之一調用父組件的回調方法。
- InvokeAsync(Object) – 如果使用的是 EventCallback
- InvokeAsync(T) – 如果使用的是 EventCallback
為了理解上述步驟,讓我們創建一個簡單的待辦事項列表(To Do List)示例。首先,在 Data 文件夾中創建以下 ToDo.cs 類。這是一個簡單類,用于存儲每個待辦事項的 Title 和 Minutes 屬性。Minutes 屬性指定完成特定待辦事項所需的時間。
ToDo.cs
- public class ToDo
- {
- public string Title { get; set; }
- public int Minutes { get; set; }
- }
在項目中添加以下 ToDoList.razor 組件,并在其中編寫以下代碼:
ToDoList.razor
- @page "/todos"
- @using BlazorEventHandlingDemo.Data
- <div class="row">
- <div class="col"><h3>To Do List</h3></div>
- <div class="col"><h5 class="float-right">Total Minutes: @TotalMinutes</h5></div>
- </div>
- <br />
- <table class="table">
- <tr>
- <th>Title</th>
- <th>Minutes</th>
- <th></th>
- </tr>
- @foreach (var todo in ToDos)
- {
- <ToDoItem Item="todo" />
- }
- </table>
- @code {
- public List<ToDo> ToDos { get; set; }
- public int TotalMinutes { get; set; }
- protected override void OnInitialized()
- {
- ToDos = new List<ToDo>()
- {
- new ToDo() { Title = "Analysis", Minutes = 40 },
- new ToDo() { Title = "Design", Minutes = 30 },
- new ToDo() { Title = "Implementation", Minutes = 75 },
- new ToDo() { Title = "Testing", Minutes = 40 }
- };
- UpdateTotalMinutes();
- }
- public void UpdateTotalMinutes()
- {
- TotalMinutes = ToDos.Sum(x => x.Minutes);
- }
- }
在上面的 @code 代碼塊中,我們聲明了兩個屬性 ToDos 和 TotalMinutes。其中 ToDos 屬性存儲待辦事項的列表,TotalMinutes 存儲所有待辦事項花費分鐘數的總和。
- public List<ToDo> ToDos { get; set; }
- public int TotalMinutes { get; set; }
接下來,我們在 Blazor 組件生命周期方法之一的名為 OnInitialized 的方法中使用一些待辦事項對象來初始化我們的 ToDos 列表。我們還調用了 UpdateTotalMinutes 方法,該方法簡單地計算 ToDos 列表中所有 ToDo 對象的 Minutes 屬性的總和。
- protected override void OnInitialized()
- {
- ToDos = new List<ToDo>()
- {
- new ToDo() { Title = "Analysis", Minutes = 40 },
- new ToDo() { Title = "Design", Minutes = 30 },
- new ToDo() { Title = "Implementation", Minutes = 75 },
- new ToDo() { Title = "Testing", Minutes = 40 }
- };
- UpdateTotalMinutes();
- }
HTML 代碼也非常簡單,我們將 TotalMinutes 屬性顯示在帶有頁面標題的頁面頂部。
- <h5 class="float-right">Total Minutes: @TotalMinutes</h5>
我們還在頁面上生成了一個 HTML 表格,接下來的 foreach 循環遍歷 ToDos 列表并渲染一個名為 ToDoItem 的子組件,我們還使用其 Item 屬性將每個 ToDo 對象傳入子組件中。
- @foreach (var todo in ToDos)
- {
- <ToDoItem Item="todo" />
- }
讓我們在 Shared 文件夾中創建一個子組件 ToDoItem.razor 并在其中添加以下代碼。該子組件有一個 Item 屬性(我們在父組件的 foreach 循環中設置了屬性)。該子組件簡單地使用 元素生成一個表格行,并在表格單元格中顯示 Title 和 Minutes 屬性。
- @using BlazorEventHandlingDemo.Data
- <tr>
- <td>@Item.Title</td>
- <td>@Item.Minutes</td>
- <td>
- <button type="button" class="btn btn-success btn-sm float-right">
- + Add Minutes
- </button>
- </td>
- </tr>
- @code {
- [Parameter]
- public ToDo Item { get; set; }
- }
運行該應用程序,您會看到一個類似于如下內容的頁面:
如果此時您點擊子組件中的 Add Minutes 按鈕,則不會有任何反應,因為我們還沒有將 click 事件與 Add Minutes 按鈕關聯起來。讓我們更新一下 Add Minutes 按鈕的代碼,添加調用 AddMinute 方法的 @onclick 特性。
- <button type="button" class="btn btn-success btn-sm float-right" @onclick="AddMinute">
- + Add Minutes
- </button>
當用戶每次點擊 Add Minutes 按鈕時,事件處理方法 AddMinute 簡單地將 Minutes 屬性加 1。
- public async Task AddMinute(MouseEventArgs e)
- {
- Item.Minutes += 1;
- }
再次運行應用程序并嘗試點擊每個待辦事項的 Add Minutes 按鈕。您將注意到每個待辦事項顯示的分鐘數會增加,但是頂部的總分鐘數屬性將保持不變。這是由于 TotalMinutes 屬性是在父組件中計算的,而父組件并不知道子組件中的 Minutes 屬性增加了。
讓我們使用上面提到的步驟在我們的示例中改進一下子組件到父組件的通信,以便每次增加子組件中的 Minutes 時,能夠相應地更新父組件的 UI。
步驟1:在子組件中聲明一個 EventCallback 或 EventCallback<T> 委托
第一步是在我們的子組件中聲明 EventCallback
- [Parameter]
- public EventCallback<MouseEventArgs> OnMinutesAdded { get; set; }
步驟2:在父組件中附加一個到子組件的 EventCallback 或 EventCallback
在這一步中,我們需要向在前面的步驟 1 中聲明的子組件的 EventCallback 委托 OnMinutesAdded 附加一個回調方法。
- <ToDoItem Item="todo" OnMinutesAdded="OnMinutesAddedHandler" />
在本例中我們使用的回調方法是 OnMinutesAddedHandler,該方法簡單地調用同一個 UpdateTotalMinutes 方法,更新 TotalMinutes 屬性。
- public void OnMinutesAddedHandler(MouseEventArgs e)
- {
- UpdateTotalMinutes();
- }
步驟3:當子組件需要與父組件通信時,使用 InvokeAsync(Object) 或 InvokeAsync(T) 方法調用父組件的回調方法。
在這一步中,我們需要調用父組件中的回調方法,因為我們希望每次用戶點擊 Add Minute 按鈕時都會更新父組件 UI,所以最好的調用位置是在 AddMinute 方法中。
- public async Task AddMinute(MouseEventArgs e)
- {
- Item.Minutes += 1;
- await OnMinutesAdded.InvokeAsync(e);
- }
這就是在 Blazor 中實現從子組件到父組件通信我們所要做的所有事情。以下是子組件 ToDoItem.razor 的完整代碼:
ToDoItem.razor
- @using BlazorEventHandlingDemo.Data
- <tr>
- <td>@Item.Title</td>
- <td>@Item.Minutes</td>
- <td>
- <button type="button" class="btn btn-success btn-sm float-right" @onclick="AddMinute">
- + Add Minutes
- </button>
- </td>
- </tr>
- @code {
- [Parameter]
- public ToDo Item { get; set; }
- [Parameter]
- public EventCallback<MouseEventArgs> OnMinutesAdded { get; set; }
- public async Task AddMinute(MouseEventArgs e)
- {
- Item.Minutes += 1;
- await OnMinutesAdded.InvokeAsync(e);
- }
- }
以下是父組件 ToDoList.razor 的完整代碼:
ToDoList.razor
- @page "/todos"
- @using BlazorEventHandlingDemo.Data
- <div class="row">
- <div class="col"><h3>To Do List</h3></div>
- <div class="col"><h5 class="float-right">Total Minutes: @TotalCount</h5></div>
- </div>
- <br />
- <table class="table">
- <tr>
- <th>Title</th>
- <th>Minutes</th>
- <th></th>
- </tr>
- @foreach (var todo in ToDos)
- {
- <ToDoItem Item="todo" OnMinutesAdded="OnMinutesAddedHandler" />
- }
- </table>
- @code {
- public List<ToDo> ToDos { get; set; }
- public int TotalCount { get; set; }
- protected override void OnInitialized()
- {
- ToDos = new List<ToDo>()
- {
- new ToDo() { Title = "Analysis", Minutes = 40 },
- new ToDo() { Title = "Design", Minutes = 30 },
- new ToDo() { Title = "Implementation", Minutes = 75 },
- new ToDo() { Title = "Testing", Minutes = 40 }
- };
- UpdateTotalMinutes();
- }
- public void UpdateTotalMinutes()
- {
- TotalCount = ToDos.Sum(x => x.Minutes);
- }
- public void OnMinutesAddedHandler(MouseEventArgs e)
- {
- UpdateTotalMinutes();
- }
- }
在瀏覽器中運行應用程序,并嘗試增加任一待辦事項的分鐘數,您會注意到父組件將自動地實時更新總分鐘數。
翻譯自 Waqas Anwar 2021年3月28日的文章 《Communication between Blazor Components using EventCallback》 [1]