lördag 30 oktober 2021

Blazor gotcha: Use the @key attribute when visibility of list items can be toggled

Finding the "bug"

We're building a Blazor web from scratch for the first time in a project at work. I like it and we've been able to go pretty fast although it's a new framework for all the devs in the team. But we ran into a strange behavior that looked like a bug.

In the web app a user can add locomotives and wagons to a list to create a train. Each of the vehicles in the list is represented of a header and a body, where the body can be collapsed or expanded.

User input for each vehicle is validated after entry. When a field is invalid a red frame is shown around the field and a validation error message is shown beneath it.

The strange behavior appeared when the body for one vehicle was expanded at the same time as the body for another vehicle was collapsed. When the use entered an invalid value in a field for one of the vehicles, the field for the other vehicle was marked as erroneous!

Reporting the "bug"

I recreated the strange behavior in a minimal project and reported it as a bug in Blazor: Input validation status gets mixed up when toggling content

After a few days I got the reponse:

Thanks for contacting us. It looks like you're missing the @key attribute for the elements which are rendered in a loop. You can learn more about this from https://docs.microsoft.com/en-us/aspnet/core/blazor/components/?view=aspnetcore-6.0#use-key-to-control-the-preservation-of-elements-and-components

The linked page says: 

The mapping process of elements or components to a collection can be controlled with the @key directive attribute. Use of @key guarantees the preservation of elements or components based on the key's value. 

It can be used like this:

<ol>
    @foreach (var person in people)
    {
        <li>
            <Details @key="person" Data="@person.Data" />
        </li>
    }
</ol>

Not seen in any material

I dont't remember seeing this in any Blazor material I've studied so far (pluralsight.com and the book "Blazor in Action"), so if you're going to use Blazor and create something with similar behavior, keep this post in mind! :)