Search code examples
bindingblazorblazor-webassembly

Blazor resets bind value


I'm having trouble understanding blazor binding. I have created a basic sample to illustrate the problem I'm facing:

Suppose I want to create a component to choose a day and time like this: Day time component, with a Value property of type DateTime.

First, I create a component to hold the time input:

@* TimeInput.razor *@
<input type="time" @onchange=Changed [email protected]("HH:mm") required>

@code {
    [Parameter] public DateTime Value { get; set; }

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

    public void Changed(ChangeEventArgs e)
    {
        Value = DateTime.Parse((string)e.Value!);
        ValueChanged.InvokeAsync(Value);
    }
}

Then I create a component with a select input and the TimeInput component created in the previous step:

@* DateTimeSelector.razor *@

<select @onchange=DateChanged>
    @for (int i = 0; i < 7; i++)
    {
        var date = new DateTime(2022, 1, 1).AddDays(i);
        <option [email protected]("yyyy-MM-dd") 
                selected=@(Value.Date == date)>
            @date.ToString("dddd")
        </option>
    }
</select>
<TimeInput Value=Value ValueChanged=TimeChanged />

@code
{
    [Parameter] public EventCallback<DateTime> ValueChanged { get; set; }

    private void DateChanged(ChangeEventArgs arg)
        => Value = DateTime.Parse((string)arg.Value!).Add(Value.TimeOfDay);

    private void TimeChanged(DateTime time)
        => Value = Value.Date.Add(time.TimeOfDay);

    private DateTime value;

    [Parameter]
    public DateTime Value
    {
        get => value;
        set
        {
            Console.WriteLine($"Value is {value}"); // FOR DEBUGGING
            if (this.value != value)
            {
                this.value = value;
                ValueChanged.InvokeAsync();
            }
        }
    }
}

Finally, I test it in a page:

@page "/"

<p><DateTimeSelector @bind-Value=@dateTime /></p>
<p>Debug: <input type="text" value=@dateTime /></p>

@code {
    private DateTime dateTime = new DateTime(2022, 1, 3, 17, 0, 0);
}

When the page is loaded, the component shows with value 2022-01-03 17:00 as expected: enter image description here

But as soon as the user change a value (for example, from 5 pm to 6 pm), then the value resets to 0001-01-01 00:00: enter image description here

If i take a look at the console I see this: enter image description here

So why is this happening? Who's calling (twice) the setter for property Value with a default date? How can I fix it?


Solution

  • Well, just a small omission. In your DateTimeSelector, you have to invoke with the value.

    ValueChanged.InvokeAsync(); => ValueChanged.InvokeAsync(value);