Search code examples
blazorblazor-client-side

Parameter / CascadingParameter not updating object


I want to update a TodoItem in a child component TodoComponent passing a EventCallback.

In my TodoPage.razor.cs I've implemented an update method:

public async void UpdateTodo(TodoItem todoItem)
{
    await Http.PutAsJsonAsync("todo", todoItem);
    await RefreshTodos();
}

And then I'm trying to pass to the TodoComponent that is responsible to render the object

@foreach (var item in @TodoItems)
{
    <div class="row">
            <CascadingValue Value=@item Name="TodoItem">
                <TodoComponent UpdateCallback="@(async () => UpdateTodo(item))">
                </TodoComponent>
            </CascadingValue>
    </div>
}

TodoComponent.razor

@inherits ListOfTodos.Client.Components.TodoComponentBase
<div class="input-group col-12">
    <div class="input-group-prepend">
        <div class="input-group-text">
            <input type="checkbox" @bind="@TodoItem.IsDone" onclick="@UpdateCallback">
        </div>
    </div>

    <div class="col-8">
        <input type="text" class="form-control" @bind="@TodoItem.TaskName" oninput="@UpdateCallback" />
    </div>

    <div class="input-group-append">
        <input type="date" @bind="@TodoItem.DueDate" />
    </div>
</div>

Aditional info:

public async Task RefreshTodos()
{
    TodoItems = await Http.GetFromJsonAsync<List<TodoItem>>("todo");
    StateHasChanged();
}
public class TodoComponentBase : ComponentBase
{
    [CascadingParameter(Name = "TodoItem")]
    public TodoItem TodoItem { get; set; }

    [Parameter]
    public EventCallback<TodoItem> UpdateCallback { get; set; }

    protected override Task OnInitializedAsync()
    {
        return base.OnInitializedAsync();
    }

}

When I call the UpdateCallback though onchange or oninput the TodoItemcomes like if it wasn't updated at all, I don't know if the binding is working as I intend it to do.

How do I properly pass the object to the child component and have it update correctly?

This is the github repo: https://github.com/DgoNeves/Blazor.ListOfTodos It works in memory so you don't need any database to run it.

Edit: I don't exactly know why this happens but apparently the controller call is behing for some reason. Blazor Update Behind


Solution

  • I've figured it out!

    @bind="@TodoItem.TaskName" executes by default onChange

    onInput apparently happens before the onChange so in the screenshot when I get 1 character behind it means that it did not update the TodoItem.TaskName because that's the next step.

    If I really want to call the API on every keystroke I need to change it to:

    @bind-value="@TodoItem.TaskName" @bind-value:event="oninput" onchange="@UpdateCallback"

    This way I change the binding to happen onInputand I call the UpdateCallback onChange. Because the onInput happens first I get the updated value on onChange therefore passing the correct value.

    Source from where I've found this: https://github.com/dotnet/aspnetcore/issues/15554 He had exactly the same error as I did