Using HTML Range Inputs with ASP.NET Core TagHelpers

By FoxLearn 2/27/2025 7:36:27 AM   22
HTML offers a variety of input fields that can significantly enhance user experiences. As of this writing, there are 22 HTML input types, and the InputTagHelper in ASP.NET Core supports 14 of them, based on .NET common language runtime types in your models.

However, it’s missing some controls, including the highly useful range input.

What is a Range Input?

The range input is commonly referred to as a “slider,” allowing users to select a value by sliding an indicator along a scale. The range input is defined by a minimum and maximum value, ensuring that users can only select valid values. Additionally, you can specify the step attribute to control the increment between values, and you can add markers using a <datalist> element to help users better understand the scale.

Building a TagHelper for Range Inputs

When working with ASP.NET Core MVC or Razor Pages, you'll typically have a model and a Razor view.

Page Model:

public class IndexModel(ILogger<IndexModel> logger) : PageModel
{
    [BindProperty]
    public int Value { get; set; }

    public string? Message { get; set; }

    public void OnGet()
    {
    }

    public void OnPost()
    {
        Message = $"You selected {Value}!";
    }
}

Razor View:

@page
@model IndexModel
@{
    ViewData["Title"] = "Home page";
}

@if (Model.Message is not null)
{
    <div class="alert alert-info">
        @Model.Message
    </div>
}

<form method="post" asp-page="Index">
    <div>
        <label class="form-label" asp-for="Value"></label>
        <input class="form-range" asp-for="Value" />
    </div>
    <button type="submit" class="btn btn-primary">
        Add
    </button>
</form>

At this point, you’ll notice that the input type for the Value field is text instead of range.

Adding a Range Attribute

First, let's define a custom attribute to handle both the Range and Step metadata.

public class CustomRangeAttribute : RangeAttribute
{
    public CustomRangeAttribute(int minimum, int maximum)
        : base(minimum, maximum)
    {
    }

    public double Step { get; set; } = 1;
}

Now, apply this custom attribute to the Value property in your model:

[BindProperty, CustomRange(1, 5, Step = 1)]
public int Value { get; set; }

Creating the Range Input TagHelper

Next, let's create a TagHelper that will detect the RangeAttribute and configure the HTML input element accordingly.

using System.ComponentModel.DataAnnotations;
using Microsoft.AspNetCore.Mvc.ViewFeatures;
using Microsoft.AspNetCore.Razor.TagHelpers;

namespace Custom
{
    [HtmlTargetElement("input", Attributes = ForAttributeName, TagStructure = TagStructure.WithoutEndTag)]
    public class RangeInputTagHelper : TagHelper
    {
        private const string ForAttributeName = "asp-for";
        private const string TypeAttributeValue = "range";

        public override int Order { get; } = -999;

        [HtmlAttributeName(ForAttributeName)] public ModelExpression For { get; set; }

        public override void Process(TagHelperContext context, TagHelperOutput output)
        {
            var metadata = For.Metadata;

            if (metadata is { ContainerType: not null, PropertyName: not null })
            {
                var attribute =
                    metadata.ContainerType.GetProperty(metadata.PropertyName)
                    ?.GetCustomAttributes(typeof(RangeAttribute), true)
                    .FirstOrDefault();

                if (attribute is RangeAttribute range)
                {
                    output.Attributes.SetAttribute("type", TypeAttributeValue);
                    output.Attributes.SetAttribute("min", range.Minimum);
                    output.Attributes.SetAttribute("max", range.Maximum);

                    if (range is CustomRangeAttribute rws)
                    {
                        output.Attributes.SetAttribute("step", rws.Step);
                    }
                }
            }
        }
    }
}

Registering the Custom TagHelper

To use our custom TagHelper, we need to register it in the _ViewImports.cshtml file. Just add the following line:

@addTagHelper *, Custom

Adjusting the TagHelper Order

We set the Order property of the RangeInputTagHelper to -999 because the default InputTagHelper has an order of -1000. This ensures our custom TagHelper runs right after the original one, allowing us to apply additional functionality without overriding default behavior.

After implementing these changes, running the application will display a range slider input where the min, max, and step values are correctly set. This approach allows you to extend ASP.NET Core’s built-in tag helpers and handle input types that are otherwise missing, like the range input.

You can use this same technique for other HTML5 input types that aren’t natively supported in ASP.NET Core, ensuring your server-side and client-side code stays in sync.