看起来这里的问题是您将警报呈现硬连线到您的 AlertService 单例,但您需要的行为是您的视图响应服务但独立存储信息。
从服务设置中读取列表的方式是,它使用服务中的列表作为它正在呈现的列表,这会将所有内容联系在一起,因为它们是引用类型。服务列表中的任何变化都会自然地反映在页面中,无论好坏。您提出问题的方式是,我假设您希望警报仅针对页面加载后添加的列表项显示。您需要一种机制来将服务中的警报与页面上显示的内容分离。因此,也许更好的方法是使用该服务仅将警报信息作为事件传递。这种方式实际上设置起来相当简单,需要在 4 个地方进行设置。
StringEventArgs.cs
这里扩展了基本的 EventArgs 类,以允许在事件调用中传递字符串。这个StringEventArgs 类型稍后将在我们的AlertService、View.razor 和List.razor 类中使用。
public class StringEventArgs : EventArgs
{
public string Value { get; set; }
}
这就像EventArgs 一样工作,但它允许设置Value 属性并使用它发送一个字符串值。 (这也可以与您想要发送的任何其他值一起使用,甚至可以用于复杂的对象类型。只需扩展 EventArgs 以满足您的需求。)
AlertService.cs
如果您使用此方法,您的服务类可以非常简单,因为它所做的只是充当消息中心来传递信息。
public class AlertService
{
public Func<object, StringEventArgs, Task> SendAlert;
public Task OnAlertRequested(object sender, StringEventArgs e)
{
SendAlert?.Invoke(sender, e);
return Task.CompletedTask;
}
}
这里是非常标准的基于事件的东西,只是使用StringEventArgs 而不是标准的EventArgs。
View.Razor
您需要在此文件中更改的唯一内容是在您提交有效表单时通过您的服务传递消息。您提到您有一个在有效提交表单时调用的方法,该方法将警报添加到列表中。所以该方法应该看起来像这样。请注意,我展示了注入服务以澄清名称。
[Inject] public AlertService Service { get; set;}
void HandleValidSubmit(string alertMessage, other params...)
{
// code to handle other params
Service.OnAlertRequested(this, new StringEventArgs() { Value = alertMessage});
}
这样做是调用服务中触发事件的方法,使用传递警报值的新 StringEventArgs 实例。只要服务中的 SendAlert 事件有任何订阅者,服务就会调用任何订阅的方法,我们接下来会这样做。
List.Razor
这是您需要包含的最后一项功能,所有这些都将开始工作。我们将列出每次加载新页面时都会初始化的警报值列表,并在警报消息通过服务发送时填充它。
@implements IDisposable
@foreach (var alert in AlertList)
{
<Alert AlertBox="alert" />
}
@code {
[Inject] public AlertService Service { get; set;}
// Always initializes to a fresh list on component start
public List<string> AlertList { get; set; } = new List<string>();
//This handler will be called whenever the event defined in AlertService
//triggers, and will accept StringEventArgs which can be passed from the event caller
//Note that this needs to be ASYNC so you can await the 'InvokeAsync' call below
private async Task OnAlertReceived(object sender, StringEventArgs e)
{
AlertList.Add(e.Value);
await InvokeAsync(StateHasChanged);
}
//Triggered when the page builds and renders
protected override void OnInitialized()
{
//Unsubsribe once to make sure you only have one event subscription
//This prevents event propogation, and won't do anything unless you are
//already subscribed for some reason
Service.SendAlert -= OnAlertReceived;
//Subscribe to the event
Service.SendAlert += OnAlertReceived;
}
//IMPORTANT - add Dispose and unsubscribe on teardown
//prevents event propogation and memory leaks.
public void Dispose()
{
Service.SendAlert -= OnAlertReceived;
}
}
这样做是在您的列表中为您提供一个名为 OnAlertReceived 的方法来响应来自服务的SendAlert 通知,然后使用OnInitialized 注册该方法以在服务调用它时触发。该方法接收 StringEventArgs 对象,并将Value 属性添加到AlertList 列表中。警报列表现在完全独立于服务,并且仅存在于List.razor 组件中,因此您的列表组件的每个实例将仅显示在初始化后生成的警报。最后,Dispose 方法确保事件订阅在您完成后被删除,因此您不会发生内存泄漏。
这应该会让你动起来,但要指出一件事。因为您将服务注册为单例,所以只要在该窗口中的任何内容中包含List.razor,警报就会发送给他们打开的所有单独窗口中的所有用户。如果您只希望它为单个用户工作,您可以改用范围服务,并确保您没有从OwningComponentBase 继承。 Further reading here。我还在 Github here 上发布了一个演示项目,如果你想要一些可以下载和浏览的东西。
希望这会有所帮助!