Home > OS >  Blazor MAUI extracted component not updating list
Blazor MAUI extracted component not updating list

Time:07-26

I'm trying out .NET MAUI with Blazor, and running into issues when I extract some code from one component into a new component.

Given a list of a class which looks like this:

public class Expense
{
    public DateTime Date { get; set; }

    public decimal Amount { get; set; }

    public string Note { get; set; }

    public Expense(DateTime date, decimal amount, string note)
    {
        Date = date;
        Amount = amount;
        Note = note;
    }
}

When I work such a list inside of a Blazor component, running inside of .NET MAUI, everything works fine when I have it all inside of one component, such as:

<button  @onclick="AddExpense"><i ></i></button>

<table >
    <thead>
        <tr>
            <th>Date</th>
            <th>Amount</th>
            <th>Note</th>
            <th></th>
        </tr>
    </thead>
    <tbody>
        @for (var i = 0; i < expenses.Count; i  )
        {
            int copy = i;
            <tr>
                <td>
                    <input type="date" min="2022-01-01" max="@currentDay"
                       @bind="@expenses[copy].Date"
                       @bind:format="yyyy-MM-dd" />
                </td>
                <td>
                    <input type="number" min="0" step="0.01" @bind="@expenses[copy].Amount" />
                </td>
                <td>
                    <input type="text" @bind="@(expenses[copy].Note)" />
                </td>
                <td>
                    <button  @onclick="() => expenses.RemoveAt(copy)"><i ></i></button>
                </td>
            </tr>
        }
    </tbody>
</table>

@code { region

    private readonly string currentDay = $"{DateTime.Now.Year}-{DateTime.Now.Month}-{DateTime.Now.Day}";

    private List<Expense> expenses = new List<Expense>()
    {
        new Expense(DateTime.Today, 100, "test")
    };

    private void AddExpense()
    {
        expenses.Add(new Expense(DateTime.Today, 0, ""));
    }

However, I attempted to extract the table rows into their own ExpenseRow component, which looks like this:

<tr>
    <td>
        <input type="date" min="@MinAttributeValue" max="@MaxAttributeValue"
               @bind="@Date"
               @bind:format="yyyy-MM-dd"/>
    </td>
    <td>
        <input type="number" min="0" step="0.01" @bind="@Amount"/>
    </td>
    <td>
        <input type="text" @bind="@Note"/>
    </td>
    <td>
        <button  @onclick="() => OnRemoveClick()"><i ></i></button>
    </td>
</tr>

@code { region:

    private string MinAttributeValue => Min.ToString("yyyy-MM-dd");
    private string MaxAttributeValue => Max?.ToString("yyyy-MM-dd");

    [Parameter]
    public DateTime Min { get; set; } = new DateTime(2022, 1, 1);

    [Parameter]
    public DateTime? Max { get; set; }

    [Parameter]
    public DateTime Date { get; set; }

    [Parameter]
    public EventCallback<DateTime> DateChanged { get; set; }

    [Parameter]
    public decimal Amount { get; set; }

    [Parameter]
    public EventCallback<decimal> AmountChanged { get; set; }

    [Parameter]
    public string Note { get; set; }

    [Parameter]
    public EventCallback<string> NoteChanged { get; set; }

    [Parameter]
    public Action OnRemoveClick { get; set; }

And I updated the original component to render this new component in the for loop:

<table >
    <thead>
        <tr>
            <th>Date</th>
            <th>Amount</th>
            <th>Note</th>
            <th></th>
        </tr>
    </thead>
    <tbody>
        @for (var i = 0; i < expenses.Count; i  )
        {
            int copy = i;
            <ExpenseRow 
                @bind-Amount="expenses[copy].Amount"
                @bind-Date="expenses[copy].Date"
                @bind-Note="expenses[copy].Note"
                Max="@DateTime.Today"
                OnRemoveClick="() => { expenses.RemoveAt(copy); StateHasChanged(); }" />
        }
    </tbody>
</table>

This change apparently breaks the binding back to the objects in the list:

  • The properties on each item in the list seem to not be updated. This is evidenced by the values being reverted once I tab away from the field, or when I add a new item to the list via the Add button.
  • The 'delete' button does not remove a row from the table after it is clicked.
    • I was able to fix this behavior by adding in the call to StateHasChanged().

I also attempted to invoke StateHasChanged() in this way, based on enter image description here

  • Related