Radio Button generates duplicate HTML id-s
Asked Answered
A

3

38

It seems that the default ASP.NET MVC2 Html helper generates duplicate HTML IDs when using code like this (EditorTemplates/UserType.ascx):

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<UserType>" %>

<%: Html.RadioButton("", UserType.Primary, Model == UserType.Primary) %>
<%: Html.RadioButton("", UserType.Standard, Model == UserType.Standard) %>
<%: Html.RadioButton("", UserType.ReadOnly, Model == UserType.ReadOnly) %>

The HTML it produces is:

<input checked="checked" id="UserType" name="UserType" type="radio" value="Primary" /> 
<input id="UserType" name="UserType" type="radio" value="Standard" /> 
<input id="UserType" name="UserType" type="radio" value="ReadOnly" /> 

That clearly shows a problem. So I must be misusing the Helper or something.

I can manually specify the id as html attribute but then I cannot guarantee it will be unique.

So the question is how to make sure that the IDs generated by RadioButton helper are unique for each value and still preserve the conventions for generating those IDs (so nested models are respected? (Preferably not generating IDs manually.)

Accra answered 10/5, 2010 at 8:54 Comment(0)
B
12

I faced the same problem. Specyfying IDs manually seems to be the only solution. If you don't need the ids for anything (like javascript), but want it only to be unique you could generate Guids for them:

<%: Html.RadioButton("", UserType.Primary, Model == UserType.Primary, new { id="radio" + Guid.NewGuid().ToString()}) %>

A more elegant solution would be to create your own extension method on HtmlHelper to separate ID creation logic from the view. Something like:

public static class HtmlHelperExtensions
{

    public static MvcHtmlString MyRadioButtonFor<TModel, TValue>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TValue>> expression, bool value)
    {
        string myId = // generate id...
        return htmlHelper.RadioButtonFor(expression, value, new {id=myId});
    }
}

The helper method could use ViewContext and Model data to create more meaningfull IDs.

UPDATE:

If you use EditorTemplates to render the control like this

<%= Html.EditorFor(m=>m.User, "MyUserControl") %>

Then inside the MyUserControl.ascx (placed in ~/Views/Shared/EditorTemplates) you can use ViewData.TemplateInfo.HtmlFieldPrefix property to access the parent control ID or Html.ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldId("MyPostfixPart") to generate prefixed id. Theese methods could be used in the helper extension above. The same works with controls rendered with Html.Editor(...) and Html.EditorForModel(...). In the Html.Editor helper you can also specify htmlFiledName manually if you want.

When you embed the control with

<%= Html.Partial("UserControl", Model.User) %>

generation of meaningfull IDs is harder because the Html.Partial will not provide information about the prefix - the ViewData.TemplateInfo.HtmlFieldPrefix will be always empty. Then, the only solution would be to pass the prefix manually to the ascx control in as ViewData key of as a model field which is not as elegant a solution as the previous one.

Belaud answered 10/5, 2010 at 9:11 Comment(3)
Maybe you can also suggest how to generate IDs taking into account current name of the model so the IDs can be properly generated and have IDs like: UserType_Primary, Company_User_UserType_Primary and so on?Accra
@Dmitriy: I've just updated my response. ViewData.TemplateInfo.HtmlFieldPrefix should help you.Belaud
Thanks a lot. That should do the job for me.Accra
L
16

In addition to PanJanek's answer:
If you don't really need the elements to have an id, you can also specify id="" in the htmlAttributes (i.e. new { id="" }) parameter of helpers. This will result in the id attribute being left out completely in the generated html.

source: https://mcmap.net/q/411370/-can-asp-net-mvc-html-helpers-render-an-element-without-an-id-attribute

Lenz answered 8/12, 2011 at 10:22 Comment(0)
B
12

I faced the same problem. Specyfying IDs manually seems to be the only solution. If you don't need the ids for anything (like javascript), but want it only to be unique you could generate Guids for them:

<%: Html.RadioButton("", UserType.Primary, Model == UserType.Primary, new { id="radio" + Guid.NewGuid().ToString()}) %>

A more elegant solution would be to create your own extension method on HtmlHelper to separate ID creation logic from the view. Something like:

public static class HtmlHelperExtensions
{

    public static MvcHtmlString MyRadioButtonFor<TModel, TValue>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TValue>> expression, bool value)
    {
        string myId = // generate id...
        return htmlHelper.RadioButtonFor(expression, value, new {id=myId});
    }
}

The helper method could use ViewContext and Model data to create more meaningfull IDs.

UPDATE:

If you use EditorTemplates to render the control like this

<%= Html.EditorFor(m=>m.User, "MyUserControl") %>

Then inside the MyUserControl.ascx (placed in ~/Views/Shared/EditorTemplates) you can use ViewData.TemplateInfo.HtmlFieldPrefix property to access the parent control ID or Html.ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldId("MyPostfixPart") to generate prefixed id. Theese methods could be used in the helper extension above. The same works with controls rendered with Html.Editor(...) and Html.EditorForModel(...). In the Html.Editor helper you can also specify htmlFiledName manually if you want.

When you embed the control with

<%= Html.Partial("UserControl", Model.User) %>

generation of meaningfull IDs is harder because the Html.Partial will not provide information about the prefix - the ViewData.TemplateInfo.HtmlFieldPrefix will be always empty. Then, the only solution would be to pass the prefix manually to the ascx control in as ViewData key of as a model field which is not as elegant a solution as the previous one.

Belaud answered 10/5, 2010 at 9:11 Comment(3)
Maybe you can also suggest how to generate IDs taking into account current name of the model so the IDs can be properly generated and have IDs like: UserType_Primary, Company_User_UserType_Primary and so on?Accra
@Dmitriy: I've just updated my response. ViewData.TemplateInfo.HtmlFieldPrefix should help you.Belaud
Thanks a lot. That should do the job for me.Accra
C
1

If you do need to access the radio buttons via jQuery, I find that often the better place to set the id's will be on the page, close to their intended usage. This is how I did the same:

@Html.Label(Resource.Question)
@Html.RadioButtonFor(m => m.Question, true, new {id = "QuestionYes"}) Yes
@Html.RadioButtonFor(m => m.Question, false, new {id = "QuestionNo"}) No

Then my jQuery:

if ($("input[id='QuestionNo']").is(":checked")) {
        $('#YesOptionalBlock').removeClass('showDiv');
        $('#YesOptionalBlock').addClass('hideDiv');
}
Colloquium answered 25/9, 2015 at 17:57 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.