翻譯自 Waqas Anwar 2021年3月28日的文章 《Communication between Blazor Components using EventCallback》 [1]
Blazor 應用程式是相互互動的多個 Blazor 元件的集合,我們可以在其他父元件中使用子元件。在實際的應用程式中,將資料或事件資訊從一個元件傳遞到另一元件是一種十分常見的場景。您可能會有一個頁面,其中一個元件中發生的使用者操作需要更新其他元件中的某些 UI。通常使用 EventCallback 委託來處理這種型別的通訊。在本教程中,我將介紹如何使用 EventCallback 在父元件和子元件之間進行通訊。
下面是使用 EventCallback 從子元件到父元件進行通訊所涉及的通用步驟。
- 在子元件中宣告一個
EventCallback
或EventCallback<T>
委託 - 在父元件中附加一個到子元件的
EventCallback
或EventCallback<T>
的回撥方法 - 當子元件想要與父元件通訊時,可以使用以下方法之一呼叫父元件的回撥方法。
- InvokeAsync(Object) – 如果使用的是
EventCallback
- InvokeAsync(T) – 如果使用的是
EventCallback<T>
- InvokeAsync(Object) – 如果使用的是
為了理解上述步驟,讓我們建立一個簡單的待辦事項列表(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 迴圈中設定了屬性)。該子元件簡單地使用 <tr>
元素生成一個表格行,並在表格單元格中顯示 Title 和 Minutes 屬性。
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">
+ 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<T>
委託。我們宣告一個委託 OnMinutesAdded,並使用 MouseEventArgs 作為 T
,因為它可以為我們提供有關按鈕點選事件的額外資訊。
[Parameter]
public EventCallback<MouseEventArgs> OnMinutesAdded { get; set; }
步驟2:在父元件中附加一個到子元件的 EventCallback
或 EventCallback<T>
的回撥方法
在這一步中,我們需要向在前面的步驟 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();
}
}
在瀏覽器中執行應用程式,並嘗試增加任一待辦事項的分鐘數,您會注意到父元件將自動地實時更新總分鐘數。
相關閱讀:
- Blazor Server 和 WebAssembly 應用程式入門指南
- Blazor 元件入門指南
- Blazor 資料繫結開發指南
- Blazor 事件處理開發指南
- Blazor 元件之間使用 EventCallback 進行通訊
https://www.ezzylearning.net/tutorial/communication-between-blazor-components-using-eventcallback Communication between Blazor Components using EventCallback ︎