在 Blazor 应用程序中共享数据

已完成

Blazor 包含多种在组件之间共享信息的方法。 你可使用组件参数或级联参数将值从父组件发送到子组件。 AppState 模式是另一种可用于存储值并从应用程序中的任何组件访问这些值的方法。

假设你负责处理新的披萨派送网站。 应以相同方式在主页上显示多种披萨。 你希望通过为每个披萨呈现一个子组件来显示这些披萨。 现在,你想要将一个 ID 传递到该子组件,以确定它将显示的披萨。 你还想在多个组件上存储和显示值,该值显示今天销售的披萨总数。

在此单元中,你将了解可用于在两个或多个 Blazor 组件之间共享值的三种不同方法。

使用组件参数与其他组件共享信息

在 Blazor Web 应用中,每个组件都呈现 HTML 的一部分。 一些组件会呈现完整的页面,而另一些组件则会呈现较小的标记片段,例如表、窗体或单个控件。 如果组件仅呈现标记的一部分,则必须将其用作父组件中的子组件。 子组件也可以是在其中呈现的其他较小组件的父组件。 子组件也称为嵌套组件。

在父组件和子组件的此层次结构中,可以使用组件参数在它们之间共享信息。 在子组件上定义这些参数,然后在父组件中设置其值。 例如,如果具有显示披萨照片的子组件,则可以使用组件参数传递披萨 ID。 子组件从 ID 查找披萨,并获取图片和其他数据。 如果要显示多个不同的披萨,可在同一父页面多次使用此子组件,并将不同 ID 传递给每个子组件。

首先,在子组件中定义组件参数。 将其定义为 C# 公共属性,并使用 [Parameter] 特性对其进行修饰:

<h2>New Pizza: @PizzaName</h2>

<p>@PizzaDescription</p>

@code {
    [Parameter]
    public string PizzaName { get; set; }
    
    [Parameter]
    public string PizzaDescription { get; set; } = "The best pizza you've ever tasted."
}

由于组件参数是子组件的成员,因此可以使用 Blazor 的预留 @ 符号(后跟其名称)在 HTML 中呈现它们。 此外,上面的代码还指定了 PizzaDescription 参数的默认值。 如果父组件不传递值,将呈现此值。 否则,它将被从父组件传递的值替代。

还可以将项目中的自定义类用作组件参数。 请考虑描述配料的以下类:

public class PizzaTopping
{
    public string Name { get; set; }
    public string Ingredients { get; set; }
}

可以使用与参数值相同的方式将该类用作组件参数,使用点语法访问该类的各个属性:

<h2>New Topping: @Topping.Name</h2>

<p>Ingredients: @Topping.Ingredients</p>

@code {
    [Parameter]
    public PizzaTopping Topping { get; set; }
}

在父组件中,使用子组件标记的属性设置参数值。 直接设置简单组件。 借助基于自定义类的参数,使用内联 C# 代码创建该类的新实例并设置其值:

@page "/pizzas-toppings"

<h1>Our Latest Pizzas and Topping</h1>

<Pizza PizzaName="Hawaiian" PizzaDescription="The one with pineapple" />

<PizzaTopping Topping="@(new PizzaTopping() { Name = "Chilli Sauce", Ingredients = "Three kinds of chilli." })" />

使用级联参数共享信息

如果要将值传递给组件的直接子组件,可以使用组件参数。 当具有包含子组件的子组件的较深层次结构时,事情便会变得难以应付。 组件参数不会从上级组件或沿着层次结构向下自动传递到下级组件。 为了完美处理此问题,Blazor 包含了级联参数。 在组件中设置级联参数的值时,其值将自动提供给所有子组件。

在父组件中,使用 <CascadingValue> 标记指定将级联到所有子组件的信息。 此标记作为内置的 Blazor 组件实现。 在该标记内呈现的任何组件都能够访问该值。

@page "/specialoffers"

<h1>Special Offers</h1>

<CascadingValue Name="DealName" Value="Throwback Thursday">
    <!-- Any descendant component rendered here will be able to access the cascading value. -->
</CascadingValue>

在子组件中,可以通过使用组件成员并使用 [CascadingParameter] 特性对其进行修饰来访问级联值。

<h2>Deal: @DealName</h2>

@code {
    [CascadingParameter(Name="DealName")]
    private string DealName { get; set; }
}

因此,在本例中,<h2> 标记将具有内容 Deal: Throwback Thursday,因为该级联值是由父组件设置的。

注意

至于组件参数,如果有更复杂的需求,可以将对象作为级联参数传递。

在上面的示例中,级联值由父级中的 Name 属性标识,与 [CascadingParameter] 属性中的 Name 值匹配。 可以选择省略这些名称,在这种情况下,属性将按类型匹配。 只有一个该类型的参数时,可省略名称。 如果要实现两个不同字符串值的级联,则必须使用参数名称以避免任何歧义。

使用 AppState 共享信息

在不同组件之间共享信息的另一种方法是使用 AppState 模式。 创建一个定义要存储的属性的类,并将其注册为作用域服务。 在要设置或使用 AppState 值的任何组件中,注入该服务,然后可以访问其属性。 不同于组件参数和级联参数,AppState 中的值可用于应用程序中的所有组件,即使这些组件不是存储该值的组件的子组件也是如此。

例如,考虑这个类,它存储了一个关于销售的值:

public class PizzaSalesState
{
    public int PizzasSoldToday { get; set; }
}

将该类作为作用域服务添加到 Program.cs 文件中:

...
// Add services to the container
builder.Services.AddRazorPages();
builder.Services.AddServerSideBlazor();

// Add the AppState class
builder.Services.AddScoped<PizzaSalesState>();
...

现在,在要设置或检索 AppState 值的任何组件中,可以注入该类,然后访问属性:

@page "/"
@inject PizzaSalesState SalesState

<h1>Welcome to Blazing Pizzas</h1>

<p>Today, we've sold this many pizzas: @SalesState.PizzasSoldToday</p>

<button @onclick="IncrementSales">Buy a Pizza</button>

@code {
    private void IncrementSales()
    {
        SalesState.PizzasSoldToday++;
    }
}

注意

此代码实现了一个计数器,该计数器会在用户选择按钮时递增值,这非常类似于 Blazor 教程 - 生成第一个 Blazor 应用中的示例。 不同之处在于,在本例中,由于我们已将计数器的值存储在 AppState 范围内服务中,因此计数会在页面加载期间一直存在,并且对其他用户可见。

在下一个单元中,你将亲自试一试!

知识检查

1.

Blazor 组件可以使用两种类型的参数来接收输入。 分别是哪两个参数?

2.

可以使用以下哪个语句将 AppState 注册到服务定位器?