Radio Button Tag Helpers in ASP.NET 5 MVC 6
Asked Answered
C

4

35

I don't see any tag helpers for radio buttons in ASP.NET 5 MVC 6. What's the right way of handling form elements where I need to use radio buttons?

Caernarvonshire answered 2/1, 2016 at 21:26 Comment(0)
M
50

There is a TagHelper for all the input types which includes the radio button type as well. Assuming you have a view model like this

public class CreateUserVm
{
    public string UserName { set; get; }
    public IEnumerable<RoleVm> Roles { set; get; } 
    public int SelectedRole { set; get; }
}
public class RoleVm
{
    public int Id { set; get; }
    public string RoleName { set; get; }        
}

And in your GET action,

public IActionResult Index()
{
    var vm = new CreateUserVm
    {
        Roles = new List<RoleVm>
        {
            new RoleVm {Id = 1, RoleName = "Admin"},
            new RoleVm {Id = 2, RoleName = "Editor"},
            new RoleVm {Id = 3, RoleName = "Reader"}
        }
    };
    return View(vm);
}

In the view, You can simply use markup for the input tag.

@model YourNamespaceHere.CreateUserVm
<form asp-action="Index" asp-controller="Home">
    <label class="label">User name</label>
    <div class="col-md-10">
        <input type="text" asp-for="UserName" />
    </div>
    <label class="label">Select a Role</label>
    <div class="col-md-10">
    @foreach (var item in Model.Roles)
    {
       <input asp-for="SelectedRole" type="radio" value="@item.Id" /> @item.RoleName
    }
    </div>
    <input type="submit" />
</form>

When you post your form, The Rold Id for the selected role will be in the SelectedRole property

Keep in mind that, the above razor code will generate input element with same Id attribute value and name attribute value for each input generated by the loop. In the above example, it will generate 3 input elements(radio button type) with the Id and name attribute value set to SelectedRole. Model binding will work as the name attribute value matches with the property name(SelectedRole) in our view model, but the duplicate Id attribute values might give you trouble with client side code (duplicate Ids in a document is invalid)

Merill answered 2/1, 2016 at 22:35 Comment(7)
I'm using a JS click event on the radio button to submit to form, however the value from the radio is never being passed to my action method. Do I have to set the name or id attributes on the radio input to something specific?Jawbone
Question: Could I set the value to be true or false? value="true"?Paean
If that is what you want to pass when the radio button is selectedMerill
Doesn't this produce an error in browser console? with me ASP.Net core (as I think) generates the same Id for each radio input.Temporal
@MohammedNoureldin Yes. the tag helper generates same Id for all the input elements generated by the loop, which is invalid HTML! As of this moment, i do not know a clean solution to handle that. But model binding will work with the above approach as it only cares about the name attribute value of the inputs.Merill
Apparently the work around now is to set the id html property manually to override tag helper behavior.Temporal
A snippit of the post code would be handyVedis
M
8

While there are solutions to use asp-for="SomeField", I found that the easiest solution was to just match a view model field with the radio button's name field.

View Model:

public class MyViewModel
{
   public string MyRadioField { get; set; }
}

Form (without labels for clarity):

@model MyViewModel
<form asp-action="SomeAction" asp-controller="SomeController">
   <input type="radio" name="MyRadioField" value="option1" checked />
   <input type="radio" name="MyRadioField" value="option2" />
   <input type="radio" name="MyRadioField" value="option3" />

   <input type="submit" />
</form>

When the form is submitted, MyRadioField is populated with value of the checked radio button.

Mich answered 6/11, 2018 at 16:41 Comment(0)
P
0

I've written my own tag helper to do that. It replaces the input tag with a label and a radiobutton for each enum variant:

using System.Collections.Generic;
using System.Linq;
using System.Text;
using DigitScpi.Web.Helpers;
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.AspNetCore.Mvc.ViewFeatures;
using Microsoft.AspNetCore.Razor.TagHelpers;

namespace TagHelpers
{
    /// Generates the radio buttons for an enum. Syntax: `<input radio asp-for="MyMappedEnumField"/>`.
    [HtmlTargetElement("input", Attributes = RadioAttributeName)]
    public class RadioButtonTagHelper : TagHelper
    {
        private const string RadioAttributeName = "radio";
        private const string ForAttributeName = "asp-for";

        [HtmlAttributeNotBound] [ViewContext] public ViewContext ViewContext { get; set; }
        private readonly IHtmlGenerator _generator;

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

        public RadioButtonTagHelper(IHtmlGenerator generator)
        {
            _generator = generator;
        }

        /// <inheritdoc />
        public override void Process(TagHelperContext context, TagHelperOutput output)
        {
            output.SuppressOutput();

            foreach (var enumItem in For.Metadata.EnumNamesAndValues)
            {
                var id = VariantId(enumItem);
                var name = For.Metadata.EnumGroupedDisplayNamesAndValues.FirstOrDefault(v => v.Value == enumItem.Value).Key.Name;
                var radio = _generator.GenerateRadioButton(ViewContext, For.ModelExplorer, For.Name, enumItem.Key, false, new {id});
                var label = _generator.GenerateLabel(ViewContext, For.ModelExplorer, For.Name, name, new {@for = id});

                output.PreElement.AppendHtml(radio);
                output.PreElement.AppendHtml(label);
            }
        }

        /// Computes the variant to be unique for each radiobutton.
        private string VariantId(KeyValuePair<string, string> enumItem) =>
            new StringBuilder()
                .Append(ViewContext.CreateUniqueId(_generator.IdAttributeDotReplacement, For.Name))
                .Append(_generator.IdAttributeDotReplacement)
                .Append(enumItem.Key)
                .ToString();
    }
}
Pointtopoint answered 10/10, 2019 at 13:4 Comment(0)
E
0

Here is an extension method that renders a radio button list given a SelectList. You use it just like you use Html.DropDownListFor

public static HtmlString RadioButtonListFor<TModel, TResult>(this IHtmlHelper<TModel> helper, Expression<Func<TModel, TResult>> expression, SelectList selectList, string className = "radio-group")
{
  //render a list of radio buttons to bind to the specified value
  MemberExpression member = expression.Body as MemberExpression;
  string propertyName = member.Member.Name;

  Func<TModel, TResult> method = expression.Compile();
  TResult selectedValue = method(helper.ViewData.Model);

  var output = new StringBuilder();
  if (!string.IsNullOrEmpty(className))
    output.AppendLine($"<div class='{className}'>");
  else
    output.AppendLine("<div>");

  foreach (SelectListItem item in selectList)
  {
    bool selected = Convert.ToString(selectedValue) == item.Value;
    string html = $"<label> <input type='radio' name='{propertyName}' value='{item.Value}' {(selected ? "checked" : "")} /> {item.Text} </label>";
    output.AppendLine(html);
  }
  output.AppendLine("</div>");

  return new HtmlString(output.ToString());
}

You call it like this in your razor file:

 @Html.RadioButtonListFor(m => m.ModelProperty, selectList)
England answered 5/10, 2022 at 14:15 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.