Blazor如何处理多个组件和嵌套组件之间的共享数据



我有一个Blazor应用程序,它有很多组件和嵌套组件,我想在它们之间共享数据,但我不知道如何有效地做到这一点。这是我所拥有的:

MyProject.Pages.Index.razor:

@page "/"
<div>
@if(some_state) {
<ComponentAlpha @bind-something=Something />
else if(some_other_state){
<ComponentBeta Something="Something" />
} else {
<ComponentGamma Something="Something" />
}
</div>
@code {
String Something { get; set; }
}

MyProject.Shared.ComponentAlpha.rarzor:

<div>
Stuff here ...
</div>
@code {
[Parameter]
public String something { get; set; }
[Parameter]
public EventCallback<String> somethingChanged { get; set; }
private async Task MyTask() {
await somethingChanged.InvokeAsync(something);
}
}

这一切对于从ComponentAlpha.razorIndex.razor、从Index.razorComponentBeta.razorComponentGamma.razor的数据获取都非常有效。我的问题适用于ComponentBeta.razorComponentGamma.razor之外的

MyProject.Shared.ComponentBeta.razor:

<div>
Stuff here ...
<ComponentDelta />
<ComponentEpsilon />
</div>
@code {
[Parameter]
public String Something { get; set; }
}

MyProject.Shared.ComponentGamma.rarzor:

<div>
Stuff here ...
<ComponentZeta />
<ComponentEta />
</div>
@code {
[Parameter]
public String Something { get; set; }
}

MyProject.Shared.ComponentDelta.rarzor:

MyProject.Shared.ComponentEpsilon.razor:

MyProject.Shared.ComponentZeta.rarzor:

MyProject.Shared.ComponentEta.razor:

<div>
Stuff here ...
<MoreAndMoreComponents />
</div>
@code {
// I want to use variable "something" here as well.
}

为了能够在我的所有组件和嵌入式组件之间共享字符串something,我是否需要跳过我为Index.razorComponentAlpha.razorComponentBeta.razor所做的所有精心设计的关卡,或者有更好的方法吗?

我看到了THIS,并想到了选项3。State Container将是我的最佳选择,然而,当我以他们为例时,我总是会出现以下异常:

Error   CS0119  'AppState' is a type, which is not valid in the given context

那么,我们应该用什么方式在所有组件和嵌套组件之间有效地共享数据呢?

您应该考虑使用服务,可能是Scoped。然后将服务注入到每个组件中,可以使用Interface和/或抽象基类来对代码进行样板。您还可以对事件使用相同的服务,以发出数据已更改的信号。

请参阅此处的MS文档,了解服务以及如何使用/提供服务。

您可以考虑的一个选项是使用级联参数,这将允许顶级组件将自己传递给任何子组件,而不管组件树的深度如何。它很容易设置。

在您的顶级组件中:

<CascadingValue Value="this">
@*Note Component Alpha placement*@
<ComponentAlpha />
</CascadingValue>
@code {
private string something = "initial value";
// this is a getter only for the property, but you can use
// a setter for 2 way binding also. Just make sure
// to modify the setter to run 'StateHasChanged()'
// after updating the value, and then 
// you can then set the property directly
public string Something => something;
// This will allow child components to set the new value
public Task SetNewValue(string value)
{
something = value;
StateHasChanged();
return Task.CompletedTask;
}
}

请注意,CascadingValue正在将this从树向下传递给它的子级,因此您可以在需要的地方捕获它。

在中级(alpha(组件中

<div>
<div @onclick="SetNewValue">@ValueFromTopLevel</div>
@*Note Component Beta placement*@
<ComponentBeta /> 
</div>
@code {
// Capture the top level component here
[CascadingParameter]
public Component TopLevelComponent { get; set; }
// pull out the value you need here
public string ValueFromTopLevel => TopLevelComponent.Something;
// use this to set a new value
void SetNewValue()
{
TopLevelComponent.SetNewValue("Hello from Alpha component!");
}
}

请注意,贝塔组件嵌套在阿尔法组件中。

在低级别(以测试版为例(组件中

<div>
<div @onclick="SetNewValue">@ValueFromTopLevel</div>
</div>
@code {
// still capture the top level component
[CascadingParameter]
public Component TopLevelComponent { get; set; }
// use the value the same as Alpha
public string ValueFromTopLevel => TopLevelComponent.Something;
// set the value the same as Alpha
void SetNewValue()
{
TopLevelComponent.SetNewValue("Hello from Beta component!");
}
}

使用此设置,通过单击Alpha或Beta组件的文本,something的值将在顶层更新,然后新值将再次级联到新的渲染中。所有的状态管理都在顶部,子组件只是在需要的地方挂接。此设置将允许您根据需要使用顶级组件中的方法和属性。例如,我创建了一个顶级Toast Notification组件,它封装了整个应用程序,可以在任何地方捕获和使用。只需向它传递一条消息和一种类型(信息、错误、警告(,它就会显示闪烁消息几秒钟,然后再次隐藏。

不过也有一些缺点,特别是您的顶级组件需要负责所有的状态管理。这适用于更简单的场景,但随着应用程序和交互变得更加复杂,这可能会失控。然而,如果与Enet的答案结合起来,并在顶部进行状态管理,那么您就可以在组件树的需要位置进行挂钩。

还要注意,任何子组件都会耦合到顶级父组件,因为它们需要位于树的某个位置才能正常工作。也许对你来说可以,也许不行。不管怎样,它都是一个很好的工具。

级联参数的官方文档可以在这里找到

这取决于您需要信息的持久性,以及您愿意在多大程度上保留父级上的全局变量。

1.一个技巧是将父控件传递给子控件--让它们完全访问其变量:

ParentControl

<CascadingValue Value="this">
<ChildControl />
</CascadingValue>

儿童控制

[Parameter]
ParentControl Parent;
@code {
Parent.SomeVariable = Something;
}

2.如果您不想这样做,那么您可以使用EventCallback<T>将数据传递给其父级

儿童控制

@code
{
[Parameter]
EventCallBack<MyCustomClass> OnDataReady { get; set; }
MyCustomClass ActiveObject { get; set; }
void DoSomething()
{
OnDataReady.InvokeAsync(ActiveObject);
}
}

ParentControl上:

<ChildControl OnDataReady=HandleData />
@code {
async Task HandleData (MyCustomClass data){
// Do stuff
}
}

3.如果您确实想要高度持久性的数据,请考虑在数据库中保存状态。由于Blazor不需要回发,因此无论何时从数据库中保存或加载信息都不会受到惩罚。

4.根据@enet的回答使用服务

最新更新