1

I have a component called DataPageComponent. It essentially has a data range and a search button. With a list of GraphComponents.

When I hit that search button I need to tell all the children GraphComponent to do a http request and fetch their new data.

<div class="py-2">
    <MudDatePicker @bind-value="dateTime" />
    <MudButton OnClick="OnSearchClicked">Search</MudButton>
</div>

@foreach (var group in Groups)
{
    <MudPaper Class="mt-2 pt-2 pb-4">
        <MudText Class="pl-2" Typo="Typo.h3">@group.Title</MudText>
        <GenericModbusGraphComponent Group="@group" Model="QueryModel" />
    </MudPaper>
}

@code {
    [Parameter, EditorRequired]
    public long DeviceId { get; set; }

    [Parameter, EditorRequired]
    public List<DeviceGroupDto> Groups { get; set; }    

    private DateTime dateTime { get; set; }    
  
    private SensorQueryModel QueryModel { get; set; }

    protected override async Task OnInitializedAsync()
    {
        var offset = await _tzService.GetLocalDateTime(DateTimeOffset.UtcNow);
        //Default Query Model
        QueryModel = new SensorQueryModel
            {
                SensorId = DeviceId,
                DateTo = DateTime.Now,
                DateFrom = DateTime.Now.AddDays(-7),
                Interval = Global.TenMinInSeconds,
                TimeZoneOffset = offset.Offset.Hours
            };
    }

    private async Task OnSearchClicked()
    {
        foreach (var views in Groups.Where(x => x.ViewReferenceGraphs != null))
        {
            await ((GenericModbusGraphComponent)views.ViewReferenceGraphs).UpdateData(QueryModel);
            //Note: Each child will call update view on thier own, no need to do it here
        }
    }

}

Now I have horribly confused myself, but all my graphs are now loading synchronously, and I need each graph component to run its own http get and render its view independently.

How am I meant to correctly tell each child component to update its data and then view each in their own thread.

Here is the code for my renderGraphComponent.

@if (Loading)
{
    <LoadingIcon />
}
else
{
    <SimpleChartComponent DataSet="_response" Heading="@Group.Title"></SimpleChartComponent>
}

@code {
    [Parameter]
    public SensorQueryModel Model { get; set; }
    
    [Parameter]
    public DeviceGroupDto Group { get; set; }    
    private List<XyStatsResponse> _response;    
    private bool Loading { get; set; } = false;    
    
    protected override async Task OnInitializedAsync()
    {
        //Only load data one time.
        if (_response == null && Loading == false)
        {
            if (Model != null)
            {
                await UpdateData();
            }
        }
    }    
 
    // used to externally update the graph data     
    public async Task UpdateData(SensorQueryModel model)
    {
        Model = model;
        
        Loading = true;
        StateHasChanged();

    //Make query
        var queryModel = new SensorGroupQueryModel
        {
            SensorId = Model.SensorId,
            DateFrom = Model.DateFrom,
            DateTo = Model.DateTo,
            TimeZoneOffset = Model.TimeZoneOffset,
            Group = Group.Title,
            Interval = Model.Interval
        };

        _response = await _repository.GetHistoryDataForGroup(queryModel);
       
    //State has change due to loading signs and hopefully response data
        Loading = false;
        StateHasChanged();
    }
Zapnologica
  • 22,170
  • 44
  • 158
  • 253
  • Question: What is a `GenericModbusGraphComponent`? Someone else's framework component or your own? It matters in how to answer the question. – MrC aka Shaun Curtis Jan 13 '22 at 09:28
  • That is my own component written, I have added the code above. – Zapnologica Jan 13 '22 at 09:53
  • As a good solution you can create a service that implements the State and notification patterns. This service handles the retrieval of the data, pushing it to the parent component, from which data can be down streamed to the child components. Child components should not access the data store directly. I've already answered questions regarding services, state and notification patterns, etc. Google search something like this string: "enet stackoverflow blazor notifierservice" – enet Jan 13 '22 at 10:28
  • @Zapnologica - Enet has answered the question the same way as I would have done. Get all your data out of the components into DataViewServices and then use events in those services to notify whoever registers for the events. I would point you at some resources I have, but I've had a warning from the "powers that be" that by doing so I'm promoting my own business - difficult when I'm retired, but... use enet's search, or for a wider view "clean design blazor". – MrC aka Shaun Curtis Jan 13 '22 at 16:30

1 Answers1

0

Create a list of tasks:

List<Task> tasks = new();
foreach (var views in Groups.Where(x => x.ViewReferenceGraphs != null))
{
    var task = ((GenericModbusGraphComponent)views.ViewReferenceGraphs).UpdateData(QueryModel);
    tasks.Add(task);
}

await Task.WhenAll(tasks);
Brian Parker
  • 11,946
  • 2
  • 31
  • 41
  • I feel like the actual component should be doing the awaiting not me? Shouldn't I be invoking some sort of event which triggers the child component to do the work and re-render – Zapnologica Jan 13 '22 at 08:59
  • @Zapnologica - yes, events are probably the answer with some refactoring of the data access. See my question above before I attempt an answer. – MrC aka Shaun Curtis Jan 13 '22 at 09:31