Nijenhof
Nijenhof

Reputation: 692

InputDate field not updating after backend change

I'm trying to make a component that allows you to pick a start and end date. A part of this is of course that the start date cannot be before the end date and vice versa. To accomplish this I've created the following component:

@if (!IsDisabled)
{
    <div class="form-row">
        <div class="col">
            <label for="@($"startDate-{_id}")">Start datum</label>
            <InputDate class="form-control" id="@($"startDate-{_id}")" TValue="DateTime" Value="@StartDate" ValueChanged="@((e) => HandleStartDateChanged(e))" ValueExpression="(() => StartDate)" />
        </div>
        <div class="col">
            <label for="@($"endDate-{_id}")">Eind datum</label>
            <InputDate class="form-control" id="@($"endDate-{_id}")" TValue="DateTime" Value="@EndDate" ValueChanged="@((e) => HandleEndDateChanged(e))" ValueExpression="(() => EndDate)" />
        </div>
    </div>
}
else
{
    <div class="form-row">
        <div class="col">
            <label for="@($"startDate-{_id}")">Start datum</label>
            <input type="date" class="form-control" id="@($"startDate-{_id}")" @bind-value="@StartDate" disabled />
        </div>
        <div class="col">
            <label for="@($"endDate-{_id}")">Eind datum</label>
            <input type="date" class="form-control" id="@($"endDate-{_id}")" @bind-value="@EndDate" disabled />
        </div>
    </div>
}

@code{
    private string _id = Guid.NewGuid().ToString();

    [Parameter] public bool IsDisabled { get; set; }

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

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

    private void HandleStartDateChanged(DateTime newStartDate)
    {
        if (DateTime.Compare(newStartDate, EndDate) < 0)
        {
            StartDate = newStartDate;
            StartDateChanged.InvokeAsync(StartDate);
        }
        else
        {
            StateHasChanged();
        }
    }
    private void HandleEndDateChanged(DateTime newEndDate)
    {
        if (DateTime.Compare(StartDate, newEndDate) < 0)
        {
            EndDate = newEndDate;
            EndDateChanged.InvokeAsync(EndDate);
        }
        else
        {
            StateHasChanged();
        }
    }
}

And to use it you could do:

<EditForm Model=@TempModel>
    <DateTimePeriodComponent @bind-StartDate="DEBUG_START" @bind-EndDate="DEBUG_END" IsDisabled="IsDisabled" />
</EditForm>

The problem I'm having is that while on the backend everything is working correctly, meaning that the startdate is not being set if it's after the enddate, but in the InputDate the wrong value is still shown. If I toggle the IsDisabled in the example, the correct date (e.g. the previous one) is shown. I suspect this is due to InputDate somehow not updating, but I can't seem to figure out why. As far as I know, when you add an additional @ to the binding Value=@StartDate it enables two way binding and should change.

Am I missing something stupid or am I just doing it wrong?

Upvotes: 0

Views: 397

Answers (1)

dani herrera
dani herrera

Reputation: 51665

In my opinion your control should to work with nullable dates and clear values when are wrongs, maybe something like this:


    <div class="form-row">
        <div class="col">
            <label for="@($"startDate-{_id}")">Start datum</label>
            <InputDate class="form-control" id="@($"startDate-{_id}")" TValue="DateTime?" Value="@_StartDate" ValueChanged="@((e) => HandleStartDateChanged(e))" ValueExpression="(() => _StartDate)" />
        </div>
        <div class="col">
            <label for="@($"endDate-{_id}")">Eind datum</label>
            <InputDate class="form-control" id="@($"endDate-{_id}")" TValue="DateTime?" Value="@_EndDate" ValueChanged="@((e) => HandleEndDateChanged(e))" ValueExpression="(() => _EndDate)" />
        </div>
    </div>



@code{
    private string _id = Guid.NewGuid().ToString();

    [Parameter] public bool IsDisabled { get; set; }

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

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

    private DateTime? _StartDate { get; set; }
    private DateTime? _EndDate { get; set; }
    
    protected override void OnParametersSet()
    {
        _StartDate = StartDate;
        _EndDate = EndDate;
    }

    private void HandleStartDateChanged(DateTime? newStartDate)
    {
        if (newStartDate != null && newStartDate.Value != StartDate && newStartDate.Value < EndDate)
        {
            StartDate = newStartDate.Value;
            _StartDate = newStartDate;
            StartDateChanged.InvokeAsync(StartDate);
        }
        else
        {
            _EndDate = null;
            StateHasChanged();
        }
    }
    private void HandleEndDateChanged(DateTime? newEndDate)
    {
        if (newEndDate  != null && newEndDate.Value != EndDate &&  StartDate < newEndDate.Value )
        {
            EndDate = newEndDate.Value;
            _EndDate = newEndDate;
            EndDateChanged.InvokeAsync(EndDate);
        }
        else
        {
            _StartDate = null;
            StateHasChanged();
        }
    }
}

Check it out at https://blazorfiddle.com/s/u0h87rmq

ScreenShot

Also, because this component is inside an EditForm, you should to notify changes to EditContext, check Option 1: Raising from EditContext

Upvotes: 2

Related Questions