A better <InputNumber /> for Blazor

I’ve recently been trying C# Blazor for web application development, and found the <InputNumber /> experience to be a little unintuitive from a user-experience perspective.

The following component – which I’m calling InputParsable – when rendered via InteractiveServer will attempt to parse the value and “correct” user input in near real-time. You can see a demonstration of this below.

NB: This generates client/server traffic per keypress, and may not be runtime efficient on the server. This almost certainly is not an appropriate solution for traffic heavy applications – but appears to work in lightweight conditions.

Advantages over <InputNumber> and <InputText>:

  • User is given near real-time feedback of mistakes when inputting data
  • Input supports nullable and unsigned types (uint, for example, as shown in the screen grab below)
  • Uses a plain text input, which may be preferred from a browser perspective, over number (although could easily be modified to use either).

The TryParseValueFromString may be an appropriate place for adding max and min constraints too, if you want those handled in near real-time as well.

@using System.Linq.Expressions
@using System.Numerics
@using System.ComponentModel
@using System.Diagnostics.CodeAnalysis
@typeparam TValue

@inherits InputBase<TValue>

<input @attributes="@AdditionalAttributes" @bind="@CurrentValueAsString" @bind:event="oninput" />

@code {
    protected override bool TryParseValueFromString(string? value, [MaybeNullWhen(false)] out TValue? result, [NotNullWhen(false)] out string? validationErrorMessage)
    {
        // Cite: https://stackoverflow.com/a/2961702/817132

        validationErrorMessage = null;

        var converter = TypeDescriptor.GetConverter(typeof(TValue));
        if (converter is null)
        {
            throw new NotSupportedException("No converter is available via TypeDescriptor.GetConverter for this type.");
        }

        try
        {
            result = (TValue?)converter.ConvertFromString(value ?? "");
        }
        catch
        {
            // restore the previous value.
            // this behaves more as an near-realtime input constraint -
            //   rather than reverting to a default value on bad input.
            result = Value;
        }

        return true;
    }
}

Full credit to the luke on StackOverflow for the core of the solution.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.