Iteration statements such as for
and foreach
present challenges in Blazor components that you don't normally face. In a classic interation implementation, your loop specific code is confined to the loop - you know you can't reference List[i]
outside the loop. In Blazor components the actual values/references are crystalised and used long after the loop completes.
Let's look at various examples and see what happens.
We need a test page with some supporting code:
@page "/Test" // Our iteration code <div>@_selected</div> @code { private string _selected = "None"; class Model { public int Id { get; set; } public string Country { get; set; } } private Task OnClick(int id) { var x = id; _selected = $"{id}"; return Task.CompletedTask; } private Task OnClick(Model country) { _selected = $"{country.Id}"; return Task.CompletedTask; } private List<Model> Countries => new List<Model>() { new Model() { Id = 0, Country = "UK"}, new Model() { Id = 1, Country = "Spain"}, new Model() { Id = 2, Country = "Portugal"} }; }
This looks like:
@foreach (var country in Countries) { <div><button class="btn btn-dark m-1" @onclick="() => OnClick(country)">Edit</button>@country.Country</div> <div><button class="btn btn-secondary m-1" @onclick="() => OnClick(country.Id)">Edit</button>@country.Country</div> }
Each iteration points OnClick
to the correct country
object in Countries
.
OnClick
- and the correct Id
is displayed.int
value is passed to OnClick
- and the correct Id
is displayed.This looks like:
@{ int x = 0;} @foreach (var country in Countries) { <div><button class="btn btn-danger m-1" @onclick="() => OnClick(x)">Edit</button>@country.Country</div> x++; }
Now we're setting a counter which we increment and pass when the button is clicked. As the loop is complete when we click on the button, x
is at Countries.Count
(one iteration after completion of foreach) - in our case 3. OnClick
is passed 3.
This looks like this:
@for (var i = 0; i < Countries.Count; i++) { <div><button class="btn btn-danger m-1" @onclick="() => OnClick(i)">Edit</button> @Countries[i].Country</div> }
Again we're passing the value of the counter when the button is clicked. As the loop is complete when we click on the button, x
is at Countries.Count
(one iteration after completion of foreach) - in our case 3. OnClick
is passed 3.
This looks like this:
@for (var i = 0; i < Countries.Count; i++) { var item = Countries[i]; <div><button class="btn btn-success m-1" @onclick="() => OnClick(item.Id)">Edit</button> @item.Country</div> }
We now set a local loop variable and reference that within the loop. When the button is pressed the reference to the correct value is passed to OnClick
- and the correct Id
is displayed.
Hopefully you can see that you should either:
foreach
loop - it's fairly bulletproof.for
.