nogood
nogood

Reputation: 1320

Blazor how to await properly for data to be loaded before printing?

I want to print some data on a'blank-page' the data gets loaded async from mongodb in a List (print is done by JS via JSRuntime). I call a razor page with a blank layout and do a foreeach loop from the List to create a table. The problem that I have is, that the pages gets rendered before the list of the foreeachloop is loaded. How do I wait properly till list is loaded 100% and then show/ print the page?

Right now I use a work around; look at the 'OnInitialized()' while loop. Pretty ugly.

@page "/PrintPages/Praxen"

@inject PraxisKontaktePrintVM ViewModel
@inject IJSRuntime IJS
@inject NavigationManager NavigationManger

       <table class="table">
           <tbody>
               @foreach (var praxis in ViewModel.AerzteLst)
               {
                   <tr>
                       <td>
                           @foreach (var dr in praxis.PraxisDresNamensLst)
                           {
                               <p>@dr</p>
                           }
                       </td>
           </tr>
               }
           </tbody>
       </table>

@code {
   protected override void OnInitialized()
   {
       //If I dont wait till Lists are loaded a empty list is show on the print because of async load DB
       //TODO get rid of this work around
       var TODO3 = 0;

       bool myWait = true;
       var counter = 0;

       while (ViewModel.AerzteLst.Count <= 0 && myWait)
       {
           System.Threading.Thread.Sleep(100);
           counter += 1;
           if (counter >= 50)
           {
               myWait = false;
           }
       }
   }

   protected override void OnAfterRender(bool firstRender)
   {
       // execute conditionally for loading data, otherwise this will load
       // every time the page refreshes; on the second button press data is there
       if (firstRender)
       {
           StateHasChanged();

           IJS.InvokeVoidAsync("Print");
       }
       NavigationManger.NavigateTo("/praxen");
   }
} 

The ViewModel is an injected Class via Startup.cs. And looks like this:

    public class PraxisKontaktePrintVM : BaseViewModel
    {
        readonly MongoAux _mongoAux;

        public PraxisKontaktePrintVM(MongoAux mongoAux)
        {
            _mongoAux = mongoAux;
            IniStartup();
        }

        public List<ArztPraxis> HeadLst = new();
        public List<ArztPraxis> BodyLst = new();

        public async void IniStartup()
        {
            var res = await _mongoAux.ReadAllAsync<ArztPraxis>(MySecret.MyArztPraxisColletionName);
            HeadsLst =  res.Where(x => x.IsActiv == true && x.PraxisFachrichtung == MySecret.FachrichtungEnum.HeadArzt).ToList();
            BodyLst = res.Where(x => x.IsActiv == true && x.PraxisFachrichtung == MySecret.FachrichtungEnum.BodyArzt).ToList();
        }
    }

Upvotes: 1

Views: 2792

Answers (1)

Henk Holterman
Henk Holterman

Reputation: 273179

The direct problem is async void IniStartup(). An async void method runs unobserved, the root cause of your problems. Almost always avoid async void.
And never use Thread.Sleep() in real code.

Step 1: make it an async Task and don't call it from the constructor:

    public PraxisKontaktePrintVM(MongoAux mongoAux)
    {
        _mongoAux = mongoAux;
        //IniStartup();        // remove
    }
        
    public async Task IniStartup()
    {
        ... // as before
    }

Note that this is also in line with best practices for DI, don't put (heavy) logic in a constructor. And constructors are not async.

Step 2: call it from your page in the correct order:

protected override async Task OnInitializedAsync()
{
    await ViewModel.IniStartup();
    StateHasChanged();      // in case IniStartup has multiple async steps
    await Task.Delay(1);    // make sure the last state is rendered
    await IJS.InvokeVoidAsync("Print");     // await it        
    NavigationManger.NavigateTo("/praxen"); // and off again 
}

Remove the OnAfterRender, you don't seem to need it.

Upvotes: 3

Related Questions