Blazor 组件之间使用 EventCallback 进行通信

在这里插入图片描述

Blazor 应用程序是多个相互交互的 Blazor 组件的集合,我们还可以在其他父组件中使用子组件。在实际应用中,将数据或事件信息从一个组件传递到另一个组件是一种非常常见的场景。也许您有一个页面,其中一个组件中发生的用户操作需要更新其他组件中的某些 UI。这种类型的通信通常使用 EventCallback 委托进行处理。在本教程中,我们将介绍如何使用 EventCallback 在父组件和子组件之间进行通信。

以下是使用 EventCallback 从子组件到父组件进行通信所涉及的常见步骤。

  1. 在子组件中声明 EventCallback 或 EventCallback 委托
  2. 将回调方法附加到子组件的 EventCallback或父组件中的 EventCallback
  3. 每当子组件想要与父组件通信时,它会使用以下方法之一调用父组件的回调方法

InvokeAsync(Object) – 如果我们使用
EventCallback InvokeAsync(T) – 如果我们使用 EventCallback

为了理解上述步骤,让我们创建一个简单的待办事项列表示例。首先,在 Data 文件夹中创建以下 ToDo.cs 类。它是一个简单的类,用于存储每个待办事项的 Title 和 Minutes 属性。 Minutes 属性指定完成特定 ToDo 项目需要多长时间。

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 属性将存储 ToDo 项目的列表,而 TotalMinutes 将存储所有 ToDo 项目分钟的总和。

public List<ToDo> ToDos { get; set; }
public int TotalMinutes { get; set; }

接下来,我们使用称为 OnInitialized 的 Blazor 组件生命周期方法之一中的一些 ToDo 项对象初始化我们的 ToDo 列表。我们还调用了 UpdateTotalMinutes 方法,该方法仅计算 ToDos 列表中所有 ToDo 对象的 Sum of 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 属性。

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>

每次用户单击“添加分钟”按钮时,AddMinute 事件处理程序方法只会在 Minutes 属性中添加 1 分钟。

public async Task AddMinute(MouseEventArgs e)
{
    Item.Minutes += 1; 
}

再次运行应用程序并尝试为每个待办事项单击“添加分钟”按钮。 您会注意到每个待办事项显示的分钟数将开始增加,但顶部的总分钟数属性将保持不变。 这是因为 TotalMinutes 属性是在父组件中计算的,而父组件不知道 Minutes 在子组件中是递增的。 在这里插入图片描述
让我们使用我上面提到的步骤在我们的示例中促进子级与父级的通信,以便每次我们在子组件中添加分钟时,我们都能够相应地更新父级 UI。

第 1 步:在子组件中声明 EventCallback 或 EventCallback 委托

第一步是在我们的子组件中声明 EventCallback 委托。 我们声明一个委托 OnMinutesAdded 并使用 MouseEventArgs 作为 T,因为这可以为我们提供有关按钮单击事件的额外信息。

[Parameter]
public EventCallback<MouseEventArgs> OnMinutesAdded { get; set; }

第二步:在子组件的 EventCallback 或父组件中的 EventCallback 中附加一个回调方法

在这一步中,我们需要使用我们在上面的步骤 1 中声明的子组件的 OnMinutesAdded EventCallback 委托附加一个回调方法。

<ToDoItem Item="todo" OnMinutesAdded="OnMinutesAddedHandler" />

我们在此示例中使用的回调方法是 OnMinutesAddedHandler,该方法仅调用更新 TotalMinutes 属性的相同 UpdateTotalMinutes 方法。

public void OnMinutesAddedHandler(MouseEventArgs e)
{
    UpdateTotalMinutes();
}

第 3 步:每当子组件想要与父组件通信时,它都会使用 InvokeAsync(Object) 或 InvokeAsync(T) 方法调用父组件的回调方法。

在这一步中,我们需要调用父组件回调方法,最好的地方是 AddMinute 方法,因为我们希望在用户每次单击 Add Minute 按钮时更新父组件 UI。

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();
    }
}

在浏览器中运行应用程序并尝试在任何 ToDo 项目中添加分钟,您会注意到父组件会自动实时更新总分钟数。 在这里插入图片描述

posted @ 2021-07-23 09:55  cool2feel  阅读(953)  评论(0编辑  收藏  举报