I am new to Blazor (and web dev in general). I was following along with Microsoft's Blazor web app Todo List tutorial, and after finishing said tutorial I wanted to go further and add buttons beside each list element to remove them from the list. This is the code I wrote to accomplish that:
@page "/todo"
<h1>Todo (@todos.Count(todo => !todo.IsDone))</h1>
<ul>
@foreach (var todo in todos)
{
<li>
<input type="checkbox" @bind="todo.IsDone" />
<input @bind="todo.Title" />
<button @onclick="RemoveTodo(todo)">Remove</button>
</li>
}
</ul>
<input placeholder="Something to do" @bind="newTodo" />
<button @onclick="AddTodo">Add todo</button>
@code {
private List<TodoItem> todos = new();
private string newTodo;
private void AddTodo()
{
if (!string.IsNullOrWhiteSpace(newTodo))
{
todos.Add(new TodoItem { Title = newTodo });
newTodo = string.Empty;
}
}
private void RemoveTodo(TodoItem item)
{
if (item != null)
{
todos.Remove(item);
}
}
}
I thought I could just copy the syntax from <button @onclick="AddTodo">Add todo</button>
to add this button, but that introduces a bug. As I found through this Stack Overflow answer, in order to fix this bug (and allow the app to build at all), I must change:
<button @onclick="RemoveTodo(todo)">Remove</button>
to include a lambda function like so:
<button @onclick="() => RemoveTodo(todo)">Remove</button>
I know that this change works, because I tested it and the app behaves as I intended it to! But I want to why this change works.
I found this additional Stack Overflow question, wherein the chosen answer explains that in order to pass values to methods called by @onclick in the above manner, one must use a lambda expression. The answer says that using @onclick
will cause the compiler to create an EventCallback object to handle the code I provide to @onclick
.
However, I still do not understand why my original code does not work. I assume that the delegate being produced by the EventCallback object cannot execute properly when a value is being passed to the function it is executing. The second question indicated that invoking via a lambda function produced a different kind of delegate, which could resolve the value passed to the function.
Is my understanding of what is happening close to the truth? Why do I need to package functions inside lambda functions in this way, but only when said functions are being passed values?