Blazor学习之旅(4)数据共享
本篇,我们来了解下在Blazor中数据是如何共享的,组件之间又该如何传递参数。
关于Blazor组件
在 Blazor 中,从名为“组件”的自包含代码部分生成 UI。每个组件都可以包含 HTML 和 C# 代码的混合。组件是通过使用 Razor 语法编写的,其中的代码是用 @code 指令标记的。其他指令可用于访问变量、绑定到值以及实现其他呈现任务。
编译应用时,HTML 和代码将编译为组件类。组件一般被编写为扩展名为 .razor 的文件。
关于数据共享
Blazor 包含多种在组件之间共享信息的方法。
(1)可使用组件参数或级联参数将值从父组件发送到子组件。
(2)AppState 模式是另一种可用于存储值并从应用程序中的任何组件访问这些值的方法。
使用组件参数共享
在父组件和子组件的此层次结构中,可以使用组件参数在它们之间共享信息。在子组件上定义这些参数,然后在父组件中设置其值。
例如,在Counter组件中,定义了一个IncrementAmount的组件参数:
@page "/counter" <PageTitle>Counter</PageTitle> <h1>Counter</h1> <p role="status">Current count: @currentCount</p> <button class="btn btn-primary" @onclick="IncrementCount">Click me</button> @code { private int currentCount = 0; [Parameter] public int IncrementAmount { get; set; } = 1; private void IncrementCount() { currentCount += IncrementAmount; } }
在调用端只需要下面这样既可,由于Counter组件中还设置了默认值为1,因此,如果调用端不传递,则默认为1。
<Counter IncrementAmount="10" />
此外,还可以使用自定义类用作组件参数,例如:
@using EDT.BlazorServer.App.Models <p>New Topping: @Topping.Name</p> <p>Ingredients: @Topping.Ingredients</p> @code { [Parameter] public PizzaTopping Topping { get; set; } }
PizzaTopping的定义如下:
public class PizzaTopping { public string Name { get; set; } public string Ingredients { get; set; } }
调用方的定义如下:
@page "/pizzas-toppings" <h1>Our Latest Pizzas and Topping</h1> <PizzaTopping Topping="@(new PizzaTopping() { Name = "Chilli Sauce", Ingredients = "Three kinds of chilli." })" />
使用级联参数共享
当具有包含子组件的子组件的较深层次结构时,事情便会变得难以应付。组件参数不会从上级组件或沿着层次结构向下自动传递到下级组件。为了完美处理此问题,Blazor 包含了级联参数。在组件中设置级联参数的值时,其值将自动提供给所有子组件。
在父组件中,使用 标记指定将级联到所有子组件的信息。此标记作为内置的 Blazor 组件实现。在该标记内呈现的任何组件都将能够访问该值。
例如,我们有三个组件,其中,CascComp2组件被嵌套在CascComp1组件中,而CascComp1组件又被嵌套在CascCompSamle组件中。他们的调用关系如下:
使用CascadingParameter级联传递参数NickName:
(1)CascCompSample.razor
<!--this is CascCompSample.razor--> @page "/cascparamsample" <h3>This is the sample page</h3> <CascadingValue Value="NickName"> <CascComp1></CascComp1> </CascadingValue> @code { private string NickName = "Edison Zhou"; }
(2)CascComp1.razor
<!--this is CascComp1.razor--> <h3>Comp1: @NickName</h3> <CascComp2></CascComp2> @code { [CascadingParameter] public string NickName { get; set; } }
(3)CascComp2.razor
<!--this is CascComp2.razor--> <h3>Comp2: @NickName</h3> @code { [CascadingParameter] public string NickName { get; set; } }
要点:
(1)首先在CascCompSample.razor页面,我们通过把CascComp1嵌套到CascadingValue里面来传递参数。
(2)其次在CascComp1和 CascComp2,不再需要显式传递参数,只需要声明CascadingParameter即可拿到值。
效果:
如果需要级联传递多个参数,可以使用CascadingValue的嵌套,这里我们修改一下CascCompSample.razor组件,让它可以共享两个参数:
<!--this is CascCompSample.razor--> @page "/cascparamsample" <h3>This is the sample page</h3> <CascadingValue Value="currentNickName" Name="NickName"> <CascadingValue Value="currentAge" Name="Age"> <CascComp1></CascComp1> </CascadingValue> </CascadingValue> @code { private string currentNickName = "Edison Zhou"; private int currentAge = 34; }
修改CascComp1.razor,增加一个CascadingParameter属性Age:
<!--this is CascComp1.razor--> <h3>Comp1: @NickName - @Age</h3> <CascComp2></CascComp2> @code { [CascadingParameter(Name="NickName")] public string NickName { get; set; } [CascadingParameter(Name="Age")] public int Age { get; set; } }
CascComp2.razor修改同上,不再赘述。
这里需要注意的是:级联值由父级中的 Name 属性标识,与 [CascadingParameter] 属性中的 Name 值匹配。对于只有一个该类型的参数而言,在子组件中CascadingParameter特性中无需指定Name。但对于如果有多个相同类型的级联参数而言,最好加上Name名称进行指定,以避免找不到。因此,这里也推荐在CascadingValue中指定Name,然后再CascadingParameter特性中指定Name。
最终效果:
使用AppState模式共享
在不同组件之间共享信息的另一种方法是使用 AppState 模式。
即创建一个定义要存储的属性的类,并将其注册为作用域服务。在要设置或使用 AppState 值的任何组件中,注入该服务,然后可以访问其属性。不同于组件参数和级联参数,AppState 中的值可用于应用程序中的所有组件,即使这些组件不是存储该值的组件的子组件也是如此。
例如,创建一个包含销售状态的类:
public class PizzaSalesState { public int PizzasSoldToday { get; set; } }
然后,将该类注入到DI容器中:
builder.Services.AddScoped<PizzaSalesState>();
最后,在任意组件中通过依赖注入来访问该属性:
@page "/" @using EDT.BlazorServer.App.Models @inject PizzaSalesState salesState; <PageTitle>Index</PageTitle> <h1>Hello, world!</h1> Welcome to your new app. <SurveyPrompt Title="Your Pizza Shop" /> @*<Counter IncrementAmount="10" />*@ <p>Today, we've sold this many pizzas: @salesState.PizzasSoldToday</p> <button @onclick="IncrementSales">Buy a Pizza</button> @code { private void IncrementSales() { salesState.PizzasSoldToday++; } }
在本例中,由于我们已将计数器的值存储在 AppState 范围内服务中,因此计数会在页面加载期间一直存在,并且对其他用户可见。
最终效果:
小结
本篇,我们了解了数据如何在Blazor中共享。
下一篇,我们学习一下在Blazor中数据绑定的各种花样。
参考资料
Microsoft Docs,《在Blazor应用程序中共享数据》
65号腕,《Blazor中的参数和级联参数》