Using HTML DataList with ASP.NET Core

By FoxLearn 2/27/2025 7:34:23 AM   25
In this post, I'll explain what a datalist is and why it might be beneficial to use in your ASP.NET Core applications, then we'll implement tag helpers to make the integration of the datalist element easier for your ASP.NET Core MVC and Razor Page applications.

What is a Datalist?

As the name suggests, the <datalist> element allows developers to create a list of options for form input fields. This enables users to select from a predefined list curated by the developer while still having the option to input their own custom text.

<label for="ice-cream-choice">Choose a flavor:</label>
<input list="ice-cream-flavors" id="ice-cream-choice" name="ice-cream-choice"/>

<datalist id="ice-cream-flavors">
    <option value="Chocolate"></option>
    <option value="Coconut"></option>
    <option value="Mint"></option>
    <option value="Strawberry"></option>
    <option value="Vanilla"></option>
</datalist>

In this example, the input field is tied to the datalist element, providing users with a dropdown of available options, while still allowing them to type their own entries.

The list attribute works with various input types, such as text, date, range, and color. However, it is worth noting that Firefox has partial support, and it doesn't work well with the date, time, range, and color types.

How to Use Datalist in ASP.NET Core MVC and Razor Pages?

Now, let's implement a solution for using a datalist in Razor Pages.

C# Page Model

using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.AspNetCore.Mvc.Rendering;

namespace OutputValues.Pages
{
    public class IndexModel : PageModel
    {
        [BindProperty]
        public string? Value { get; set; }

        public string? Message { get; set; }

        public List<SelectListItem> Fruits { get; } = new()
        {
            new("The finest from Tokyo", "Apple"),
            new("The curviest fruit", "Banana"),
            new("The citrus is amazing", "Orange")
        };

        public void OnGet() { }

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

In this code, we define a list of fruits using the SelectListItem class, which will be used to populate the datalist.

Razor View

<form method="post" asp-page="Index">
    <div>
        <label class="form-label" asp-for="Value"></label>
        <input class="form-control" asp-for="Value" asp-list="Fruits" />
    </div>

    <button type="submit" class="mt-3 btn btn-primary">
        Add
    </button>
    
    <datalist asp-items="Fruits"></datalist>
</form>

With this implementation, we now have an input field backed by the datalist.

<form method="post" action="/">
    <div>
        <label class="form-label" for="Value">Value</label>
        <input class="form-control" type="text" id="Value" name="Value" value="" list="Fruits">
    </div>

    <button type="submit" class="mt-3 btn btn-primary">
        Add
    </button>

    <datalist id="Fruits">
        <option label="The finest from Tokyo" value="Apple"></option>
        <option label="The curviest fruit" value="Banana"></option>
        <option label="The citrus is amazing" value="Orange"></option>
    </datalist>
    <input name="__RequestVerificationToken" type="hidden" value="">
</form>

The input element is now linked to the datalist through the list attribute, and the <datalist> is populated with the list of options derived from the Fruits property.

Datalist TagHelper Implementation

To make this integration seamless, we’ll create two tag helpers: one for the <datalist> element and another for the <input> element.

Datalist Helper Tag

using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.AspNetCore.Razor.TagHelpers;

namespace DataList
{
    [HtmlTargetElement("datalist", Attributes = ItemsAttributeName)]
    public class DataListHelper : TagHelper
    {
        private const string ItemsAttributeName = "asp-items";

        [HtmlAttributeName(ItemsAttributeName)]
        public ModelExpression For { get; set; } = default!;

        public override void Process(TagHelperContext context, TagHelperOutput output)
        {
            var items = (IEnumerable<SelectListItem>)For.Model ?? new List<SelectListItem>();
            foreach (var item in items)
            {
                var tagBuilder = new TagBuilder("option")
                {
                    Attributes = { ["value"] = item.Value }
                };

                if (!string.IsNullOrWhiteSpace(item.Text))
                {
                    tagBuilder.Attributes["label"] = item.Text;
                }

                output.PostContent.AppendHtml(tagBuilder);
            }

            if (!output.Attributes.ContainsName("id"))
            {
                var id = GetDatalistId(For);
                output.Attributes.SetAttribute("id", id);
            }
        }

        public static string GetDatalistId(ModelExpression forExpression)
        {
            return TagBuilder.CreateSanitizedId(forExpression.Name, "");
        }
    }
}

Input Helper Tag

using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.AspNetCore.Razor.TagHelpers;

namespace DataList
{
    [HtmlTargetElement("input", Attributes = ItemsAttributeName)]
    public class DataListInputHelper : TagHelper
    {
        private const string ItemsAttributeName = "asp-list";

        [HtmlAttributeName(ItemsAttributeName)]
        public ModelExpression For { get; set; } = default!;

        public override void Process(TagHelperContext context, TagHelperOutput output)
        {
            if (!output.Attributes.ContainsName("list"))
            {
                var listId = DataListHelper.GetDatalistId(For);
                output.Attributes.SetAttribute("list", listId);
            }
        }
    }
}

These tag helpers simplify associating the input and datalist elements. The ModelExpression is key here, allowing us to work with both the metadata and value of the property.

The <datalist> element is a native HTML feature that enhances several input types with completion suggestions. By using simple tag helpers, we can easily create datalist-backed inputs in ASP.NET Core, keeping the UI and data models in sync with minimal effort.