Как можно убедиться, что можно вводить только числа, и опционально дробные?

Вопрос или проблема

У меня есть компонент Input в Blazor. Я хочу вводить только цифры и необязательные десятичные знаки.

И я получаю ошибку: “Unhandled exception rendering component: event.preventDefault is not a function”

  • Компонент должен отображать числа с точками для тысяч.

  • И запятыми для десятичных знаков.

  • Десятичное число должно сохраняться.

    @inherits InputNumber<decimal?>
     @using System.Globalization
     @using System.Text.RegularExpressions
    
     <input @attributes="AdditionalAttributes"
            id="@_id"
            class="@CssClass"
            value="@_stringValue"
            @onkeydown="OnKeyDown"
            @oninput="OnInput"
            @onchange="OnValueChanged" />
    
     <script>
         window.validateInput = function (inputId, allowDecimals, event) {
             const key = event.key;
    
             if (
                 (key >= '0' && key <= '9') ||
                 key === 'Backspace' ||
                 key === 'Tab' ||
                 key === 'Enter' ||
                 key === 'ArrowLeft' ||
                 key === 'ArrowRight' ||
                 (allowDecimals && (key === ',' || key === '.'))
             ) {
                 return true;  
             }
    
             event.preventDefault();
    
             return false;
         }
    
         window.formatNumberInput = function (value, allowDecimals) {
             let cleanedValue = value.replace(/[^0-9.,]/g, '');
             let parts = cleanedValue.split(',');
             let integerPart = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, ".");
             let formattedValue = integerPart;
    
             if (allowDecimals && parts.length > 1) {
                 formattedValue += ',' + parts[1].substring(0, 2);  
             }
    
             return formattedValue;
         }
     </script>
    
     @code {
         private string _id { get; set; } = Guid.NewGuid().ToString();
         private string? _stringValue;
         private string? _currentValue;
    
         [Inject] IJSRuntime _jsRuntime { get; set; }
    
         [Parameter] public string FormatString { get; set; } = "#,##0.00";
         [Parameter] public bool AllowDecimals { get; set; } = true;
    
         protected override void OnParametersSet()
         {
             if (Value.HasValue)
             {
                 _stringValue = Value.Value.ToString(FormatString, CultureInfo.CurrentCulture);
             }
             else
             {
                 _stringValue = null;
             }
         }
    
         private async Task OnKeyDown(KeyboardEventArgs e)
         {
             await _jsRuntime.InvokeVoidAsync("validateInput", _id, AllowDecimals, e);
         }
    
         private async Task OnInput(ChangeEventArgs e)
         {
             _currentValue = e.Value?.ToString();
             var formattedValue = await _jsRuntime.InvokeAsync<string>("formatNumberInput", _currentValue, AllowDecimals);
             _stringValue = formattedValue;
    
             var rawValue = formattedValue.Replace(".", "").Replace(",", ".");
             if (decimal.TryParse(rawValue, out var result))
             {
                 Value = result;
                 await ValueChanged.InvokeAsync(Value);
             }
         }
    
         private async Task OnValueChanged(ChangeEventArgs e)
         {
             await OnInput(e);
         }
     }
    

Ответ или решение

Для обеспечения ввода только чисел (включая не обязательные десятичные) в компоненте Blazor, необходимо правильно настроить поведение ввода, обработку событий и форматирование значения. Ниже приведен полный пример реализации, который решает описанную вами задачу, а также устраняет ошибку "Unhandled exception rendering component: event.preventDefault is not a function".

Пример кода

@inherits InputNumber<decimal?>
@using System.Globalization
@using System.Text.RegularExpressions

<input @attributes="AdditionalAttributes"
       id="@_id"
       class="@CssClass"
       value="@_stringValue"
       @onkeydown="OnKeyDown"
       @oninput="OnInput"
       @onchange="OnValueChanged" />

<script>
    window.validateInput = function (inputId, allowDecimals, event) {
        const key = event.key;

        if (
            (key >= '0' && key <= '9') ||
            key === 'Backspace' ||
            key === 'Tab' ||
            key === 'Enter' ||
            key === 'ArrowLeft' ||
            key === 'ArrowRight' ||
            (allowDecimals && (key === ',' || key === '.'))
        ) {
            return true;  
        }

        event.preventDefault();
        return false;
    }

    window.formatNumberInput = function (value, allowDecimals) {
        let cleanedValue = value.replace(/[^0-9.,]/g, '');
        let parts = cleanedValue.split(',');
        let integerPart = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, ".");
        let formattedValue = integerPart;

        if (allowDecimals && parts.length > 1) {
            formattedValue += ',' + parts[1].substring(0, 2);  
        }

        return formattedValue;
    }
</script>

@code {
    private string _id { get; set; } = Guid.NewGuid().ToString();
    private string? _stringValue;
    private string? _currentValue;

    [Inject] IJSRuntime _jsRuntime { get; set; }

    [Parameter] public string FormatString { get; set; } = "#,##0.00";
    [Parameter] public bool AllowDecimals { get; set; } = true;

    protected override void OnParametersSet()
    {
        _stringValue = Value.HasValue ? Value.Value.ToString(FormatString, CultureInfo.CurrentCulture) : null;
    }

    private async Task OnKeyDown(KeyboardEventArgs e)
    {
        // Используйте preventDefault только для реальных событий клавиатуры
        if (!await _jsRuntime.InvokeAsync<bool>("validateInput", _id, AllowDecimals, e))
        {
            e.PreventDefault(); // Используйте e.PreventDefault() только если валидация провалилась
        }
    }

    private async Task OnInput(ChangeEventArgs e)
    {
        _currentValue = e.Value?.ToString();
        var formattedValue = await _jsRuntime.InvokeAsync<string>("formatNumberInput", _currentValue, AllowDecimals);
        _stringValue = formattedValue;

        var rawValue = formattedValue.Replace(".", "").Replace(",", ".");
        if (decimal.TryParse(rawValue, out var result))
        {
            Value = result;
            await ValueChanged.InvokeAsync(Value);
        }
    }

    private async Task OnValueChanged(ChangeEventArgs e)
    {
        await OnInput(e);
    }
}

Объяснение решения

  1. Валидация ввода: Используется JavaScript для проверки нажатых клавиш через validateInput. Это позволяет фильтровать входные данные и разрешить ввод только цифр, а также декодировать символы , и . в зависимости от параметра AllowDecimals.

  2. Форматирование числа: Функция formatNumberInput обрабатывает введённые данные, очищая их от недопустимых символов и форматируя согласно вашему требованию по разделению тысяч с помощью точки и использования запятой в качестве десятичного разделителя.

  3. Обработка событий: События onkeydown, oninput и onchange обрабатываются для динамического обновления значения и обеспечения корректного формата.

  4. Замечание: Проверьте поддержку IJSRuntime и убедитесь, что правильно передаете параметры при вызовах из C#. Ошибка event.preventDefault is not a function может быть связана с тем, как передаются события из JavaScript, убедитесь, что используете корректный тип событий.

Следуя этой реализации, вы сможете создать компонент ввода, который будет соответствовать всем указанным требованиям и корректно работать с вводом чисел и десятичных знаков.

Оцените материал
Добавить комментарий

Капча загружается...