我有一个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.razor
到Index.razor
、从Index.razor
到ComponentBeta.razor
和ComponentGamma.razor
的数据获取都非常有效。我的问题适用于ComponentBeta.razor
和ComponentGamma.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.razor
、ComponentAlpha.razor
和ComponentBeta.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的回答使用服务