API 调用完成后,如何从 .RAZOR 主页面内的所有子组件触发/刷新我的 .RAZOR 主页面?

问题描述 投票:0回答:1

我正在开发一款可让用户搜索我们的数据库的应用程序。当用户输入搜索词时,应用程序会访问 API 端点并返回数据。然后我显示数据。

当 API 返回数据时,我有一个作用域服务:

services.AddScoped<AppState>();

这将保留每个返回的数据集以供应用程序的所有组件使用。

一旦

SearchResults.razor
页面加载,它就会从我的范围服务中获取结果,然后绘制页面的其余部分。

我需要一个“加载”微调器来代替数据,直到 API 返回数据,这可能需要很长时间,具体取决于搜索的数据量。

我的问题是,我无法弄清楚使用什么作为真/假“触发器”来了解是否显示数据或加载旋转器,或者如何在 API 向我发送数据后刷新页面。

我下面的内容仅适用于第一次初始搜索(来自我的

Index.razor
页面),但不适用于任何包含的“过滤器”组件。

SearchResults.razor:

@page "/searchresults"
@layout PageTopComponents

<Header.razor></Header.razor>

<LeftMenu.razor>

    <FilterRazorComponent01.razor></FilterRazorComponent01.razor>

    <FilterRazorComponent02.razor></FilterRazorComponent02.razor>

    <FilterRazorComponent03.razor></FilterRazorComponent03.razor>

    <FilterRazorComponent04.razor></FilterRazorComponent04.razor>

</LeftMenu.razor>

<MainContentComponent.razor>

    // CONTENT HERE SHOULD BE VISIBLE WHEN DATA HAS ARRIVED, OTHERWISE IT SHOULD SHOW A "WAITING" SPINNER
    @if(API_Data_Received != null && API_Data_Received.count > 0){
        foreach(){
            // API Retrieved Data Here
        }
    } else {
        // Loading Spinner
    }

    <ContinueSearch.razor></ContinueSearch.razor>

    <Paginator.razor @ref="PaginatorComponentReference">

        <ChildContent>

            // THIS IS WHERE I DISPLAY ALL SEARCH DATA ...
            // CONTAINS: public Paginator PaginatorComponentReference;

        </ChildContent>

    </Paginator.razor>

</MainContentComponent.razor>

@code {
    // code here ...

    public async Task GetQueryStringValues()
    {
        Uri uri = navigationManager.ToAbsoluteUri(System.Net.WebUtility.UrlDecode(navigationManager.Uri));
        Dictionary<string, StringValues> queryStrings = QueryHelpers.ParseQuery(uri.Query);
    }
}

Paginator.razor:

<div> [ << ] [ < ] NAVIGATION [ > ] [ >> ] </div>

    @ChildContent // Is "ChildContent" in SearchResults.razor

<div> [ << ] [ < ] NAVIGATION [ > ] [ >> ] </div>

我包含的大多数 .RAZOR 组件都会进行某种“过滤”并使用以下内容:

String href = "/searchresults" + // other parameters here ...
NavigationManager.NavigateTo(href);

意思是,每当我“过滤”时,我总是会点击

SearchResults.razor
页面。

我相信我已经在所有可重写方法中尝试了

await InvokeAsync(StateHasChanged);
的一些组合:

  1. OnInitialized()
  2. OnInitializedAsync()
  3. OnParametersSet()
  4. OnParametersSetAsync()
  5. OnAfterRender()
  6. OnAfterRenderAsync()

但是,在从

SearchResults.razor
中的表单条目中首次加载
Index.razor
后,似乎没有任何效果。

我需要做什么才能让它发挥作用?它看起来很简单,但我就是无法弄清楚。

c# asp.net razor blazor
1个回答
10
投票

答案展示了如何更新 Blazor WeatherForecast 应用程序以演示状态/通知模式以及如何在组件中使用它。我使用了天气预报应用程序,因为您的问题没有足够的详细信息,无法使用您的代码作为答案的基础,而天气预报应用程序提供了一个很好的构建模板。

起点是标准 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
的胆量。它:

  1. 使用新的
    Loading
    组件。
  2. 使用新的 WeatherForecastViewService。
  3. 直接使用来自
    WeatherForecastViewService
    的列表。它没有自己的副本 - 所有组件都使用相同的列表。
  4. 连接到视图服务
    ListChanged
    事件并在触发 evwnt 时调用
    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;
    }
}

希望我第一次就把所有代码都正确了!我确信有人会指出任何明显的错误或改进。

© www.soinside.com 2019 - 2024. All rights reserved.