Quantcast
Channel: Active questions tagged blazor - Stack Overflow
Viewing all articles
Browse latest Browse all 4839

Inputfield only updates when it looses focus on Android only

$
0
0

In my Blazor application, I have a date input field where the users can input a date that is not below 01.01.1800. If the value is below the mentioned date, it gets automatically replaced with the mentioned date. Due to this restriction, I can't use oninput or onchange to handle date inputs because the value gets replaced instantly when the user is still typing. After the user edits the date, the onblur event fires and updates the Property. After this, you can press a save button to save the value.

But only on Android, I have the issue that onblur from the input field does not fire when you press the save button below when the focus is still on the date input. Users must press anywhere else to lose focus from the input field. Only then, the value gets updated and you can press the save button. On Windows and IOS you can press save directly without pressing anywhere else to lose focus and the value gets updated before the value gets saved.

My whole Input field component:

@using Microsoft.JSInterop<input class="duration-[100ms] rounded-md border border-solid bg-white transition transition-shadow ease-out disabled:text-gray-500 disabled:bg-gray-200 disabled:border-gray-400 @errorStyling @AdditionalClasses"       type=@variantType       value=@formattedValue       disabled=@Disabled       id=@HtmlId       autocomplete=@AutoComplete       placeholder=@Placeholder       step="any"       maxlength=@MaxLenght       @oninput=OnInputHandler       @onblur=OnBlurHandler       @ref=@element />@code {    #region Dependencies    [Inject] public IJSRuntime JS { get; set; }    #endregion    #region Variant Values    [Parameter] public InputFieldVariant Variant { get; set; }    [Parameter] public string StringTextValue { get; set; }    [Parameter] public EventCallback<string> StringTextValueChanged { get; set; }    [Parameter] public double DoubleNumberValue { get; set; }    [Parameter] public EventCallback<double> DoubleNumberValueChanged { get; set; }    [Parameter] public decimal DecimalNumberValue { get; set; }    [Parameter] public EventCallback<decimal> DecimalNumberValueChanged { get; set; }    [Parameter] public int IntNumberValue { get; set; }    [Parameter] public EventCallback<int> IntNumberValueChanged { get; set; }    [Parameter] public DateTime TimeValue { get; set; }    [Parameter] public EventCallback<DateTime> TimeValueChanged { get; set; }    [Parameter] public DateTime DateValue { get; set; }    [Parameter] public EventCallback<DateTime> DateValueChanged { get; set; }    [Parameter] public DateTime DateTimeValue { get; set; }    [Parameter] public EventCallback<DateTime> DateTimeValueChanged { get; set; }    #endregion    #region Utility    [Parameter, EditorRequired] public string HtmlId { get; set; }    [Parameter] public string AdditionalClasses { get; set; }    [Parameter] public string Placeholder { get; set; }    [Parameter] public bool HasError { get; set; }    [Parameter] public bool Disabled { get; set; }    [Parameter] public string AutoComplete { get; set; } = "on";    [Parameter] public int MaxLenght { get; set; } = 100000;    [Parameter] public bool OnlyUpdateOnBlur { get; set; }    [Parameter] public bool SelectTextOnFocus { get; set; }    #endregion    #region Private members    private ElementReference element;    private string errorStyling => HasError                ? "bg-red-200 border-red-500 focus-visible:border-red-500 focus-visible:shadow-shadowred" // Error styling                : "border-gray-400 focus-visible:border-watomain-800 focus-visible:shadow-shadowblue"; // Default styling    // Set minimum valid date to 1800, because the Proffix Rest API crashes (500), because of some convert exceptions.    private DateTime minimumValidDate = new DateTime(1800, 01, 01);    private string variantType    {        get        {            switch (Variant)            {                case InputFieldVariant.StringText: return "text";                case InputFieldVariant.StringSearch: return "search";                case InputFieldVariant.PasswordText: return "password";                case InputFieldVariant.DoubleNumber: return "number";                case InputFieldVariant.DecimalNumber: return "number";                case InputFieldVariant.IntNumber: return "number";                case InputFieldVariant.DateTimeTime: return "time";                case InputFieldVariant.DateTimeDate: return "date";                case InputFieldVariant.DateTime: return "datetime-local";                default: return "";            }        }    }    private string formattedValue    {        get        {            switch (Variant)            {                case InputFieldVariant.StringText: return StringTextValue?.ToString();                case InputFieldVariant.StringSearch: return StringTextValue?.ToString();                case InputFieldVariant.PasswordText: return StringTextValue?.ToString();                case InputFieldVariant.DoubleNumber: return DoubleNumberValue.ToString("0.##", CultureInfo.InvariantCulture);                case InputFieldVariant.DecimalNumber: return DecimalNumberValue.ToString("0.##", CultureInfo.InvariantCulture);                case InputFieldVariant.IntNumber: return IntNumberValue.ToString();                case InputFieldVariant.DateTimeTime: return TimeValue.ToString("HH:mm:ss");                case InputFieldVariant.DateTimeDate: return DateValue.ToString("yyyy-MM-dd");                case InputFieldVariant.DateTime: return DateTimeValue.ToString("yyyy-MM-ddTHH:mm:ss");                default: return "";            }        }    }    private object rawValue    {        get        {            switch (Variant)            {                case InputFieldVariant.StringText: return StringTextValue;                case InputFieldVariant.StringSearch: return StringTextValue;                case InputFieldVariant.PasswordText: return StringTextValue;                case InputFieldVariant.DoubleNumber: return DoubleNumberValue;                case InputFieldVariant.DecimalNumber: return DecimalNumberValue;                case InputFieldVariant.IntNumber: return IntNumberValue;                case InputFieldVariant.DateTimeTime: return TimeValue;                case InputFieldVariant.DateTimeDate: return DateValue;                case InputFieldVariant.DateTime: return DateTimeValue;                default: return "";            }        }    }    private object currentValue;    #endregion    #region Public methods    public async Task FocusAsync()    {        if (!string.IsNullOrEmpty(element.Id))            await JSUtilities.FocusElementAsync(JS, element);    }    #endregion    #region Private methods    private async Task ConvertAndSaveCurrentValueAsync()    {        if (Variant == InputFieldVariant.DateTimeTime && DateTime.TryParse(currentValue?.ToString(), out DateTime timeResult))        {            TimeValue = timeResult;            await TimeValueChanged.InvokeAsync(TimeValue);        }        else if (Variant == InputFieldVariant.StringText || Variant == InputFieldVariant.PasswordText || Variant == InputFieldVariant.StringSearch)        {            StringTextValue = currentValue?.ToString();            await StringTextValueChanged.InvokeAsync(StringTextValue);        }        else if (Variant == InputFieldVariant.DoubleNumber && double.TryParse(currentValue?.ToString(), NumberStyles.Float, CultureInfo.InvariantCulture, out double doubleResult))        {            DoubleNumberValue = doubleResult;            await DoubleNumberValueChanged.InvokeAsync(DoubleNumberValue);        }        else if (Variant == InputFieldVariant.DecimalNumber && decimal.TryParse(currentValue?.ToString(), NumberStyles.Float, CultureInfo.InvariantCulture, out decimal decimalResult))        {            DecimalNumberValue = decimalResult;            await DecimalNumberValueChanged.InvokeAsync(DecimalNumberValue);        }        else if (Variant == InputFieldVariant.IntNumber && int.TryParse(currentValue?.ToString(), out int intResult))        {            IntNumberValue = intResult;            await IntNumberValueChanged.InvokeAsync(IntNumberValue);        }    }    #endregion    #region Handlers    private async Task OnInputHandler(ChangeEventArgs args)    {        currentValue = args.Value;        if (!OnlyUpdateOnBlur)            await ConvertAndSaveCurrentValueAsync();    }    private async Task OnBlurHandler(FocusEventArgs args1)    {        if (Variant == InputFieldVariant.DateTimeDate)        {            var inputValue = await JS.InvokeAsync<string>("eval", $"document.getElementById('{HtmlId}').value");            if (DateTime.TryParse(inputValue, out DateTime dateResult))            {                if (dateResult < minimumValidDate)                    DateValue = minimumValidDate;                else                    DateValue = dateResult;                await JSUtilities.SetInputFieldAsync(JS, element, DateValue.ToString("yyyy-MM-dd"));                await DateValueChanged.InvokeAsync(DateValue);            }            else            {                HasError = true;                StateHasChanged();            }        }        else if (Variant == InputFieldVariant.DateTime)        {            var inputValue = await JS.InvokeAsync<string>("eval", $"document.getElementById('{HtmlId}').value");            if (DateTime.TryParse(inputValue, out DateTime dateResult))            {                if (dateResult < minimumValidDate)                    DateTimeValue = minimumValidDate;                else                    DateTimeValue = dateResult;                await JSUtilities.SetInputFieldAsync(JS, element, DateTimeValue.ToString("yyyy-MM-ddTHH:mm:ss"));                await DateTimeValueChanged.InvokeAsync(DateTimeValue);            }            else            {                HasError = true;                StateHasChanged();            }        }        await ConvertAndSaveCurrentValueAsync();    }    private async Task OnFocusHandler()    {        currentValue = rawValue;        await OnFocus.InvokeAsync();        if (SelectTextOnFocus && !string.IsNullOrEmpty(element.Id))            await JSUtilities.SelectElementValueAsync(JS, element);    }    #endregion}

Use component

<InputField Variant="InputFieldVariant.DateTimeDate"            HtmlId="date"            @bind-DateValue="VM.Date" />

My Property

public DateTime Date{    get => date;    set    {        date = value;        DateChanged.Invoke(this, value);    }}

I reckon that on Android, if the calendar for the date input is open and you press on "Set" the focus is still on the Input field. On the other hand, on Windows, the focus is lost after the calendar is used. I tried manually setting the focus to another element when the save button is pressed before the value is saved, but that did not work. My issue is that I have no way of knowing when the input is done when oblur is not fired. When onblur is not fired, the property also doesn't get updated, so the setter doesn't work either.

Android calendar screenshot


Viewing all articles
Browse latest Browse all 4839

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>