Reputation: 1320
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
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