Blazor:如何使用来自两个不同状态的两个不同页面的组件



在Blazor应用程序中,我希望使用来自两个不同页面的组件。组件使用服务来维护其状态。但每个页面都需要让组件使用不同的状态。以下是我的想法(使用默认的Counter组件进行演示(。

CounterService.cs

namespace TestTwoInterfaceToOneService.Shared
{
public interface ICounterServiceA
{
int CurrentCount { get; set; }
void IncrementCount();
}
public interface ICounterServiceB
{
int CurrentCount { get; set; }
void IncrementCount();
}
public class CounterService : ICounterServiceA, ICounterServiceB
{
public int CurrentCount { get; set; }
public void IncrementCount()
{
CurrentCount++;
}
}
}

在程序.cs中添加:

builder.Services.AddScoped<ICounterServiceA, CounterService>();
builder.Services.AddScoped<ICounterServiceB, CounterService>();

柜台剃刀

<h1>Counter</h1>
<p>Current count: @CounterService.CurrentCount</p>
<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>
@code {
[Parameter]
public CounterService CounterService { get; set; }
private void IncrementCount()
{
CounterService.CurrentCount++;
}
}

PageA.剃须刀

@page "/pagea"
@inject ICounterServiceA CounterServiceA
<h3>Page A</h3>
<Counter CounterService="CounterServiceA" /> @*<-- Error: Cannot convert from ICounterServiceB to CounterService*@

PageB.razor

@page "/pageb"
@inject ICounterServiceB CounterServiceB
<h3>Page B</h3>
<Counter CounterService="CounterServiceB" /> @*<-- Error: Cannot convert from ICounterServiceB to CounterService*@

当我尝试将服务传递给组件时,收到错误"无法从ICounterServiceB转换为CounterService"。我发现,使用两个指向相同具体实现的相同接口确实给了我两个作用域实例。但是,我不知道如何将这些实例传递到组件中。

我缺了一块吗?或者,应该采取其他方式吗?

解决方案

结合Henk的回答和Brian的评论,我想出的解决方案是:

CounterService.cs

namespace TestTwoInterfaceToOneService.Shared
{
public class CounterService
{
public int CurrentCount { get; set; }
public void IncrementCount()
{
CurrentCount++;
}
public void ResetCount()
{
CurrentCount = 0;
}
}
}

CounterStateService.cs

using System.Collections.Generic;
namespace TestTwoInterfaceToOneService.Shared
{
public interface ICounterStateService
{
CounterService this[string key] { get; }
}
public class CounterStateService : ICounterStateService
{
private Dictionary<string, CounterService> counterServices = new();
public CounterService this[string key]
{
get
{
if (!counterServices.ContainsKey(key))
{
counterServices.Add(key, new CounterService());
}
return counterServices[key];
}
}
}
}

在Program.cs中,添加

builder.Services.AddScoped<ICounterStateService, CounterStateService>();

柜台剃刀

<p>Current count: @CounterService.CurrentCount</p>
<button class="btn btn-primary" @onclick="IncrementCount">Increment</button>
@code {
[Parameter]
public CounterService CounterService { get; set; }
private void IncrementCount()
{
CounterService.CurrentCount++;
}
}

PageA.剃须刀

@page "/pagea"
@inject ICounterStateService CounterStateService
<h3>Page A</h3>
<Counter CounterService='CounterStateService["A"]' />
<button class="btn btn-primary" @onclick='CounterStateService["A"].ResetCount'>Reset Count</button>

PageB.razor

@page "/pageb"
@inject ICounterStateService CounterStateService
<h3>Page B</h3>
<Counter CounterService='CounterStateService["B"]' />
<button class="btn btn-primary" @onclick='CounterStateService["B"].ResetCount'>Reset Count</button>

或者,应该用其他方式来完成吗?

是。将这种使用模式烘焙到Type结构中不会很好地适应或扩展。如果你需要第三个页面,或者想让它变得动态,该怎么办?

您可以使用包装服务和带有密钥的方案将State绑定到组件:

public class CounterService  { ... }    // no need to register
public class CounterStateService  // register for injection
{
private Dictionary <string, CounterService> stateLookup = new();
public CounterService  this[string key] 
{
if (! stateLookup.tryGetValue (key, out CounterService  service))
{
service = new CounterService();
stateLookup.Add(key, service);
}
return service;
}
}

并像一样使用它

@page "/pagea"
@inject CounterStateService  CounterStateService
<h3>Page A</h3>
<Counter CounterService="CounterStateService["A"]" />

相关内容

  • 没有找到相关文章

最新更新