Blazor 数据绑定开发指南

翻译自 Waqas Anwar 2021年3月21日的文章 《A Developer’s Guide to Blazor Data Binding》 [1]

Blazor 数据绑定开发指南

现如今,大多数 Web 应用程序要么是在页面上显示某种数据,要么是使用表单从用户那里收集数据。这意味着每个 SPA 框架都必须支持数据绑定,以便开发者可以将数据与 labelform 控件等元素进行绑定。Blazor 组件内置了对数据绑定的支持,还使用了多种技术来同时支持单向和双向数据绑定。在本教程中,我将通过一个简单的卡片设计器示例来介绍 Blazor 数据绑定功能的基础知识,在该示例中,用户能够查看其卡片设计的实时更新情况。

下载源码[2]

单向绑定

在单向数据绑定中,数据沿一个方向传递。应用程序代码更新值以响应某些事件或用户操作,当值更新时,相应的 UI 也会动态更新。单向数据绑定中,不允许用户直接更改值。在 Blazor 中,我们通常使用 @ 符号后跟属性、字段,甚至是一个方法来实现单向数据绑定。例如,如果您的代码中有一个 Title 属性,并且您想将它与一个 h1 元素绑定,那么您可以编写类似以下代码段的代码。

<h1>@TItle</h1>

现在,要更新上述代码中的 Title,您可以使用一个简单的按钮 onclick 事件,调用代码中的 UpdateTitle 方法来更新 Title 属性的值。因为一旦用户点击按钮,更新的值便自动从代码传递到用户界面,所以 h1 元素的文本将会自动更新。

<button @onclick="UpdateTitle">Update Title</button>
 
@code{
    public string Title { get; set; } = "Hello";
 
    private void UpdateTitle()
    {
        Title = "Hello, Blazor!";
    }
}

双向绑定

在双向数据绑定中,数据是双向流动的。通常,用户在前端以某种形式更新某个值,该值在后端代码中自动更新,然后这个新值再传递到 UI 并更新绑定到该值的所有元素。在 Blazor 中,可以使用 @bind 特性实现双向数据绑定,该特性能够以多种方式使用。下面的简单示例演示了 @bind 特性的基本用法,在这个示例中我们使用 @bind=Property 语法将 Title 属性与 input 元素进行绑定。

<h1>@Title</h1>
 
<input @bind="Title" />
 
@code {
    public string Title { get; set; } = "Blazor";
}

我们还可以将某一属性绑定到特定的事件,以便仅在特定的事件触发时才更新该属性的值。绑定到特定事件的语法是 @bind:event="EVENT NAME"。例如,在下面的代码段中,我希望仅在用户将焦点从输入框移开时才改变 Title 属性。

<h1>@Title</h1>
 
<input @bind="Title" @bind:event="onchange" />
 
@code {
    public string Title { get; set; } = "Blazor";
}

现在我们已经掌握了 Blazor 数据绑定的基础知识,那么,在接下来的教程中,我将向您展示一些使用数据绑定的真实示例。在我们开始之前,请确保您已熟悉了创建和使用 Blazor 组件。如果您不了解如何创建 Blazor 应用程序或组件,我建议您阅读我之前的文章 Blazor 组件入门指南

入门实例

在 Visual Studio 2019 中创建一个 Blazor Server 应用,并在 Pages 文件夹中添加一个新的 Blazor 组件 CardDesigner.razor。 我们想要构建一个简单的卡片设计器页面,允许用户同时设计和预览卡片。 让我们在新建组件的 razor 视图中添加以下 HTML 标记。

CardDesigner.razor

@page "/"
 
<h1>Card Designer (Blazor Data Binding)</h1> 
 
<div class="container">
    <div class="row">
        <div class="col-8">
            <h3>Design</h3>
            <form>
                <div class="form-group">
                    <label for="Heading">Heading</label>
                    <input type="text" class="form-control" id="Heading">
                </div>
                <div class="form-group">
                    <label for="Description">Description</label>
                    <textarea class="form-control" id="Description" rows="4"></textarea>
                </div>              
                <button class="btn btn-secondary mb-2">Reset</button>
            </form>
        </div>
        <div class="col-4">
            <h3>Preview</h3>
            <div class="card bg-light w-100">
                <div class="card-header">
                    Heading
                </div>
                <div class="card-body">
                    <p class="card-text">
                        Description
                    </p>
                </div> 
            </div>
        </div>
    </div>
</div>

为了确保我们的卡片设计器看起来赏心悦目,您还需要在 wwwroot/css/site.css 文件中添加一些基本的 CSS 样式。

site.css

.container {
    margin: 15px;
    padding: 0px;
}
.col-8, .col-4 {
    border: 1px solid #dadada;
    padding: 10px;
}
h1 {
    font-size: 22px;
    font-weight: bold;
    margin-bottom: 30px;
    margin-top: 20px;
}
h3 {
    font-size: 18px;
    font-weight: bold;
    margin-bottom: 25px;
}

运行该应用程序,您应该能看到类似以下内容的页面。左手边是设计部分,允许用户设置卡片的 HeadingDescription,右手边显示卡片预览。还有一个 Reset 按钮,可以将表单重置为默认值。

Blazor 数据绑定开发指南

让我们为卡片设计器的实现逻辑创建一个单独的代码隐藏部分类(partial class)。该类有两个简单的带有默认值的属性 HeadingDescription。还有一个 ResetCard 方法,当用户点击 Reset 按钮时会调用此方法,将两个属性重置为其默认值。

CardDesigner.razor.cs

public partial class CardDesigner
{
    public string Heading { get; set; } = "Heading";
    public string Description { get; set; } = "Description";
 
    public void ResetCard(MouseEventArgs e)
    {
        Heading = "Heading";
        Description = "Description";
    }
}

单向绑定实例

现在,我们已为查看 Blazor 数据绑定功能的实际应用做好了准备。让我们先从单向数据绑定开始。稍微更新一下上面的 <form> 代码,并使用 @ 符号和属性名添加单向绑定。我将 inputtextarea 元素的 value 特性(attribute)绑定到 HeadingDescription 属性(property),然后添加 @onchange 事件,使用 Lambda 表达式语法更改 Heading 属性。还将 ResetCard 方法附加到了 Reset 按钮的 onclick 事件。

CardDesigner.razor

<form>
   <div class="form-group">
      <label for="Heading">Heading</label>
      <input type="text" class="form-control" id="Heading" value="@Heading"
         @onchange="@(e => { Heading = e.Value.ToString(); })">
   </div>
   <div class="form-group">
      <label for="Description">Description</label>
      <textarea class="form-control" id="Description" rows="4"
         @onchange="@(e => { Description = e.Value.ToString(); })" value="@Description"></textarea>
   </div>
   <button type="button" class="btn btn-secondary mb-2" @onclick="ResetCard">Reset</button>
</form>

我们还需要使用单向数据绑定更新预览部分的卡片,以便每次代码中的 HeadingDescription 更新时,预览部分都会自动在卡片上渲染更新后的值。

CardDesigner.razor

<div class="card text-white w-100 @SelectedStyleCssClass">
   <div class="card-header">
      @Heading
   </div>
   <div class="card-body">
      <p class="card-text">
         @Description
      </p>
   </div>
</div>

现在,如果您运行应用程序,您将会看到类似于下面的输出。尝试在设计区输入标题和描述,您会注意到,当您将焦点从输入框移开时,预览区会立即自动更新。

Blazor 数据绑定开发指南

如果您不喜欢在 HTML 中使用 Lambda 表达式,您还可以在代码中定义 UpdateHeadingUpdateDescription 方法,然后将这些方法与 @onchange 事件关联起来。

CardDesigner.razor

<form>
   <div class="form-group">
      <label for="Heading">Heading</label>
      <input type="text" class="form-control" id="Heading" value="@Heading"
         @onchange="UpdateHeading">
   </div>
   <div class="form-group">
      <label for="Description">Description</label>
      <textarea class="form-control" id="Description" rows="4"
         value="@Description"
         @onchange="UpdateDescription"></textarea>
   </div>
   <button type="button" class="btn btn-secondary mb-2" @onclick="ResetCard">Reset</button>
</form>

CardDesigner.razor.cs

public partial class CardDesigner
{
    public string Heading { get; set; } = "Heading";
    public string Description { get; set; } = "Description";
 
    public void ResetCard(MouseEventArgs args)
    {
        Heading = "Heading";
        Description = "Description";
    }
 
    public void UpdateHeading(ChangeEventArgs e)
    {
        Heading = e.Value.ToString();
    }
 
    public void UpdateDescription(ChangeEventArgs e)
    {
        Description = e.Value.ToString();
    }
}

双向绑定实例

截至目前,我们仅在应用程序中使用了单向绑定,因为 HeadingDescription 属性的值是在我们的代码中更新的,而且我们的代码只在用户将焦点从表单控件移开时才执行。让我们更新一下示例的代码,看看如何在该示例中使用双向数据绑定。使用 @bind 特性将 HeadingDescription 属性与表单控件绑定。我还希望当用户开始在表单控件中打字时立即更新卡片预览。为此,请将 @bind:event="oninput" 添加到了 inputtextarea 控件。

CardDesigner.razor

<form>
   <div class="form-group">
      <label for="Heading">Heading</label>
      <input type="text" class="form-control" id="Heading" @bind="Heading" @bind:event="oninput">
   </div>
   <div class="form-group">
      <label for="Description">Description</label>
      <textarea class="form-control" id="Description" rows="4" @bind="Description" @bind:event="oninput"></textarea>
   </div>
   <button type="button" class="btn btn-secondary mb-2" @onclick="ResetCard">Reset</button>
</form>

现在,双向绑定已经设置好了,因此我们不再需要手动更新属性,这样我们就可以将 UpdateHeadingUpdateDescription 方法从代码中删除。

CardDesigner.razor.cs

public partial class CardDesigner
{
    public string Heading { get; set; } = "Heading";
    public string Description { get; set; } = "Description";
 
    public void ResetCard(MouseEventArgs args)
    {
        Heading = "Heading";
        Description = "Description";
    }
}

再次运行应用程序并在设计表单中输入标题和描述,看下卡片预览是如何自动更新的。

Blazor 数据绑定开发指南

让我们进一步扩展我们的示例,在设计区引入一个下拉列表控件。该下拉列表将显示不同的卡片样式,用户能够使用双向数据绑定即时地选择和应用任一卡片样式。让我们在 Data 文件夹中添加以下 StyleInfo 类。

StyleInfo.cs

public class StyleInfo
{
    public string Name { get; set; }
    public string CssClass { get; set; }
}

让我们在 CardDesigner.razor.cs 文件中再添加两个属性来存储可用卡片样式的列表,并存储所选样式的引用。我们在名为 OnInitialized 的组件初始化方法中初始化 Styles 属性。在从父组件接收到初始化参数后,组件会进行初始化,初始化完成时将调用 OnInitialized 方法。

CardDesigner.razor.cs

public partial class CardDesigner
{
    public string Heading { get; set; } = "Heading";
    public string Description { get; set; } = "Description";
    public List<StyleInfo> Styles { get; set; }
    public string SelectedStyleCssClass { get; set; }
 
    protected override void OnInitialized()
    {
        Styles = new List<StyleInfo>()
        {
            new StyleInfo() { Name = "Primary", CssClass = "bg-primary" },
            new StyleInfo() { Name = "Secondary", CssClass = "bg-secondary" },
            new StyleInfo() { Name = "Success", CssClass = "bg-success" } 
        };
 
        SelectedStyleCssClass = "bg-primary";
    }
 
    public void ResetCard(MouseEventArgs args)
    {
        Heading = "Heading";
        Description = "Description";
    }
}

最后,我们需要在 CardDesigner.razor 文件中添加一个 HTML select 元素。我们运行一个简单的 @foreach 循环来迭代 Styles 属性,并在循环中创建 <option> 元素。每个 <option> 元素的 value 特性使用 CssClass 属性值呈现,每个 <option> 元素的文本使用 Name 属性值呈现。

CardDesigner.razor

<div class="form-group">
   <label for="Style">Style</label>
   <select class="form-control" id="Style" @bind="SelectedStyleCssClass" @bind:event="onchange">
      @foreach (var style in Styles)
      {
      <option value="@style.CssClass">@style.Name</option>
      }
   </select>
</div>

在上面的代码片段中,我们使用 @bind 特性将 SelectedStyleCssClass 属性与 select 元素绑定,并指定使用 selectonchange 事件,以便每次用户从下拉列表中选择一个选项时,卡片样式自动更新。

现在,如果您运行项目,将会看到下拉列表中填充的样式,并且选中的样式会应用到预览部分的卡片。

Blazor 数据绑定开发指南

右键点击 select 元素并选择 检查(Inspect) 菜单选项,可以查看 option 是如何渲染在 HTML 中的,以及每个 optionvalue 是如何包含我们在代码中初始化的 CssClass 属性的。

Blazor 数据绑定开发指南

试试从下拉列表中选择不同的样式,卡片预览会立即更新。

Blazor 数据绑定开发指南

总结

在本教程中,我介绍了 Blazor 数据绑定的基础知识。我们学习了如何使用单向和双向数据绑定功能,以及如何使用数据绑定更新值。我们还学习了如何利用不同的事件来指定何时更新值。在 Blazor 中还有一些更高级的数据绑定概念,我将尽最大的努力就这个主题再写几篇文章。


相关阅读:

作者 : Waqas Anwar
翻译 : 技术译站
链接 : 英文原文


  1. https://www.ezzylearning.net/tutorial/a-developers-guide-to-blazor-data-binding A Developer’s Guide to Blazor Data Binding ↩︎

  2. https://github.com/ezzylearning/BlazorDataBindingDemo 下载源码 ↩︎

上一篇:助力碳中和,EMQ 与 SAP 共同构建绿色 IoT 解决方案


下一篇:EMQ安装与部属