答案显示了如何更新 Blazor WeatherForecast 应用程序以演示状态/通知模式以及如何在组件中使用它。我使用了 Weather Forecast 应用程序,因为您的问题中没有足够的细节来使用您的代码作为答案的基础,而且 Weather Forecast 应用程序提供了一个很好的模板来构建。
起点是标准 Blazor Server 模板项目。我的叫StackOverflow.Answers
添加一个Loading.razor 组件。这将检测加载状态并在记录加载时显示一个旋转器。
@if (this.IsLoaded)
{
@this.ChildContent
}
else
{
<div class="loader"></div>
}
@code {
[Parameter] public RenderFragment ChildContent { get; set; }
[Parameter] public bool IsLoaded { get; set; }
}
添加组件 CSS 文件 - Loading.razor.css - 以格式化旋转器:
.page-loader {
position: absolute;
left: 50%;
top: 50%;
z-index: 1;
width: 150px;
height: 150px;
margin: -75px 0 0 -75px;
border: 16px solid #f3f3f3;
border-radius: 50%;
border-top: 16px solid #3498db;
width: 120px;
height: 120px;
-webkit-animation: spin 2s linear infinite;
animation: spin 2s linear infinite;
}
.loader {
border: 16px solid #f3f3f3;
/* Light grey */
border-top: 16px solid #3498db;
/* Blue */
border-radius: 50%;
width: 120px;
height: 120px;
animation: spin 2s linear infinite;
margin-left: auto;
margin-right: auto;
}
@-webkit-keyframes spin {
0% {
-webkit-transform: rotate(0deg);
}
100% {
-webkit-transform: rotate(360deg);
}
}
@keyframes spin {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
我将原始服务拆分为单独的数据和视图服务(良好的设计实践)。
更新WeatherForecastService。它现在是数据服务,它需要做的就是提供数据。在一个真实的应用程序中,这将与数据代理交互以获取真实数据。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace StackOverflow.Answers.Data
{
public class WeatherForecastService
{
private static readonly string[] Summaries = new[]
{
"Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
};
private List<WeatherForecast> recordsShort;
private List<WeatherForecast> recordsLong;
public WeatherForecastService()
{
recordsShort = GetForecastsShort;
recordsLong = GetForecastsLong;
}
public async Task<List<WeatherForecast>> GetForecastsAsync(bool islong = false)
{
await Task.Delay(3000);
return islong ? this.recordsLong : this.recordsShort;
}
public List<WeatherForecast> GetForecastsShort
{
get
{
var rng = new Random();
return Enumerable.Range(1, 3).Select(index => new WeatherForecast
{
Date = DateTime.Now.AddDays(index),
TemperatureC = rng.Next(-20, 55),
Summary = Summaries[rng.Next(Summaries.Length)]
}).ToList();
}
}
public List<WeatherForecast> GetForecastsLong
{
get
{
var rng = new Random();
return Enumerable.Range(1, 6).Select(index => new WeatherForecast
{
Date = DateTime.Now.AddDays(index),
TemperatureC = rng.Next(-20, 55),
Summary = Summaries[rng.Next(Summaries.Length)]
}).ToList();
}
}
}
}
将新的WeatherForecastViewService 类添加到Data 文件夹。这是我们的视图服务。它保存我们的数据,是 UI 使用的服务。它从数据服务中获取数据并公开Records 列表和ListChanged 事件,该事件在列表更改时触发。
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace StackOverflow.Answers.Data
{
public class WeatherForecastViewService
{
public List<WeatherForecast> Records { get; set; }
private WeatherForecastService weatherForecastService;
public WeatherForecastViewService(WeatherForecastService weatherForecastService)
{
this.weatherForecastService = weatherForecastService;
}
public async Task GetForecastsAsync(bool islong = false)
{
this.Records = null;
this.NotifyListChanged(this.Records, EventArgs.Empty);
this.Records = await weatherForecastService.GetForecastsAsync(islong);
this.NotifyListChanged(this.Records, EventArgs.Empty);
}
public event EventHandler<EventArgs> ListChanged;
public void NotifyListChanged(object sender, EventArgs e)
=> ListChanged?.Invoke(sender, e);
}
}
添加一个新组件 - WeatherForecastList.razor。这是来自Fetchdata 的胆量。它:
- 使用新的
Loading 组件。
- 使用新的 WeatherForecastViewService。
- 直接使用来自
WeatherForecastViewService 的列表。它没有自己的副本 - 所有组件都使用相同的列表。
- 在触发 evwnt 时连接到视图服务
ListChanged 事件并调用 StateHasChanged。
@implements IDisposable
@using StackOverflow.Answers.Data
<h1>Weather forecast</h1>
<Loading IsLoaded="this.isLoaded" >
<table class="table">
<thead>
<tr>
<th>Date</th>
<th>Temp. (C)</th>
<th>Temp. (F)</th>
<th>Summary</th>
</tr>
</thead>
<tbody>
@foreach (var forecast in viewService.Records)
{
<tr>
<td>@forecast.Date.ToShortDateString()</td>
<td>@forecast.TemperatureC</td>
<td>@forecast.TemperatureF</td>
<td>@forecast.Summary</td>
</tr>
}
</tbody>
</table>
</Loading>
@code {
[Inject] private WeatherForecastViewService viewService { get; set; }
private bool isLoaded => viewService.Records is not null;
protected override async Task OnInitializedAsync()
{
await GetForecastsAsync();
this.viewService.ListChanged += this.OnListChanged;
}
private async Task GetForecastsAsync()
=> await viewService.GetForecastsAsync();
private void OnListChanged(object sender, EventArgs e)
=> this.InvokeAsync(this.StateHasChanged);
public void Dispose()
{
this.viewService.ListChanged -= this.OnListChanged;
}
}
为新服务更新Startup服务。
services.AddSingleton<WeatherForecastService>();
services.AddScoped<WeatherForecastViewService>();
更新FetchData。它现在使用WeatherForecastList 组件。该按钮提供了一种更改列表和查看 UI 更新的机制。
@page "/fetchdata"
@using StackOverflow.Answers.Data
<WeatherForecastList/>
<div class="m-2">
<button class="btn btn-dark" @onclick="this.LoadRecords">Reload Records</button>
</div>
@code {
[Inject] WeatherForecastViewService viewService { get; set; }
private bool isLong = true;
private async Task LoadRecords()
{
await this.viewService.GetForecastsAsync(isLong);
this.isLong = !this.isLong;
}
}
希望我第一次就搞定了所有代码!我相信有人会指出任何明显的错误或改进。