Dagovax
Dagovax

Reputation: 165

Blazor update UI inside component foreach loop

I have Blazor components in a way similar to this:

Main.razor

@foreach (Vehicle vehicle in _vehicles) 
{
    if (vehicle.Visible)
    {
        <VehicleComponent Vehicle=@vehicle></VehicleComponent>
    }
}

@code {
    protected override void OnInitialized() 
    {
        _vehicles = new List<Vehicles>();
        // -> _vehicles being filled here
        base.OnInitialized();
    }
}

VehicleComponent.razor

@if (Vehicle != null) 
{
    <img src="@(Vehicle.src)"/>
    <div id="@(Vehicle.Id)" tabindex="@(Vehicle.tabindex)">
        <h3>@(Vehicle.text)</h3>
    </div>
}

@code {
    [Parameter] public Vehicle Vehicle { get; set; }
}

The problem is that all the VehicleComponents inside the loop are rendered after the loop is finished. But I want the component to completely render, before the next List item will be rendered.

I tried using StateHasChanged() after each item in Main.razor, but I got an infinite loop. Is there a way I can render each component in the loop after each other (and update the UI)?

Upvotes: 1

Views: 7854

Answers (2)

Henk Holterman
Henk Holterman

Reputation: 273179

If you want the visual effect of 'gradually appearing' items:

protected override async Task OnInitializedAsync () 
{
    var temp =  new List<Vehicles>(); 
    // -> _vehicles being filled here

    _vehicles = new List<Vehicles>();
    foreach (var vehicle in temp)
    {
       _vehicles.Add(vehicle);
       StateHasChanged();
       await task.Delay(500);  // adjust the delay to taste
    }
}

but be aware that this is an expensive (slow) way to show them.
Using @key won't improve this but it might help when you change the list later:

 <VehicleComponent @key=vehicle Vehicle=@vehicle></VehicleComponent>

Upvotes: 7

MrC aka Shaun Curtis
MrC aka Shaun Curtis

Reputation: 30001

I think you are misunderstanding the Render process.

Your Razor code gets compiled into a C# class with your Razor compiled as a RenderFragment (a delegate) - you can see it in the project obj/Debug/Net5 folder structure. The component "Render" events queue this delegate onto the Renderer's Queue. The Renderer executes these delegates and applies changes to the Renderer's DOM. Any changes in the Renderer's DOM get passed to the browser to modify it's DOM.

Henk's answer adds the items slowly to the list. It adds a "Render" event through StateHasChanged and then yields through the Task.Delay to let the Renderer re-render the list on each iteration.

Upvotes: 3

Related Questions