当API调用完成时,如何从主.RAZOR页中所有子组件触发/刷新主.RAZOR页? [英] How can I trigger/refresh my main .RAZOR page from all of its sub-components within that main .RAZOR page when an API call is complete?

查看:30
本文介绍了当API调用完成时,如何从主.RAZOR页中所有子组件触发/刷新主.RAZOR页?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

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

services.AddScoped<AppState>();

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

SearchResults.razor页面加载后立即从我的作用域服务获取结果,然后绘制该页面的睡觉。

我需要";加载";微调函数来替换数据,直到API返回数据,这可能需要很长时间,具体取决于搜索的数据量。

我的问题是,我不知道使用什么作为TRUE/FALSE&QOOT;触发器&QOOT;来知道是显示数据还是加载微调器,或者在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组件都执行某种筛选(&Q;&Q;),并使用以下内容:

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()

但是,从Index.razor中表单条目第一次加载SearchResults.razor之后,似乎没有任何内容起作用。

我需要做些什么才能使其正常工作?看起来很简单,但我就是想不通。

推荐答案

答案显示如何更新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中的毅力。IT:

  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;
    }
}

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

这篇关于当API调用完成时,如何从主.RAZOR页中所有子组件触发/刷新主.RAZOR页?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆