ASP.NET MVC 5 Group of Radio Buttons

ASP.NET MVC 5 group of radio buttons

There are a number of problems with your code including generating duplicate id's (invalid html), generating duplicate name attributes (which is why you're creating only one group, but more importantly this will prevent you from binding to the model when you post back) and you're not actually binding to a valid property anyway.

You will need to create view models to represent what you want to display and edit and generate the radio buttons in a for loop (or using an EditorTemplate) so they are correctly named with indexers.

View models

public class QuestionVM
{
public int ID { get; set; } // for binding
public string Text { get; set; }
[Required]
public int? SelectedAnswer { get; set; } // for binding
public IEnumerable<AnswerVM> PossibleAnswers { get; set; }
}

public class SubjectVM
{
public int? ID { get; set; }
[DisplayFormat(NullDisplayText = "General")]
public string Name { get; set; }
public List<QuestionVM> Questions { get; set; }
}

public class AnswerVM
{
public int ID { get; set; }
public string Text { get; set; }
}

public class StudentVM
{
public int ID { get; set; }
public string Name { get; set; }
// plus any other properties of student that you want to display in the view
public List<SubjectVM> Subjects { get; set; }
}

View

@model YourAssembly.StudentVM
@using(Html.BeginForm())
{
@Html.HiddenFor(m => m.ID)
@Html.DisplayFor(m => m.Name)
for(int i = 0; i < Model.Subjects.Count; i++)
{
@Html.HiddenFor(m => m.Subjects[i].ID)
@Html.DisplayFor(m => m.Subjects[i].Name) // will display "General" if no name
for (int j = 0; j < Model.Subjects[i].Questions.Count; j++)
{
@Html.HiddenFor(m => m.Subjects[i].Questions[j].ID)
@Html.DisplayFor(m => m.Subjects[i].Questions[j].Text)
foreach(var answer in Model.Subjects[i].Questions[j].PossibleAnswers )
{
<div>
@Html.RadioButtonFor(m => m.Subjects[i].Questions[j].SelectedAnswer, answer.ID, new { id = answer.ID})
<label for="@answer.ID">@answer.Text</label>
</div>
}
@Html.ValidationMessageFor(m => m.Subjects[i].Questions[j].SelectedAnswer)
}
}
<input type="submit" value="save" />
}

Controller

public ActionResult Edit(int ID)
{
StudentVM model = new StudentVM();
// populate your view model with values from the database
return View(model);
}

[HttpPost]
public ActionResult Edit(StudentVM model)
{
// save and redirect
}

Note I am a little confused by the database structure implied by your models (for example why do you need separate models for Question and SubjectQuestion when a null value for SubjectID identifies it as a "General" question). I suggest you start by just hard-coding some values in the GET method to see how it works and posts back.

StudentVM model = new StudentVM();
model.ID = 1;
model.Name = "bambiinela";
model.Subjects = new List<SubjectVM>()
{
new SubjectVM()
{
Questions = new List<QuestionVM>()
{
new QuestionVM()
{
ID = 1,
Text = "Question 1",
SelectedAnswer = ?, // set this if you want to preselect an option
PossibleAnswers = new List<AnswerVM>()
{
new AnswerVM()
{
ID = 1,
Text = "Answer A"
},
new AnswerVM()
{
ID = 1,
Text = "Answer B"
}
}
},
new QuestionVM()
{
ID = 2,
Text = "Question 2",
PossibleAnswers = new List<AnswerVM>()
{
// similar to above
}
}
}
},
new SubjectVM()
{
ID = 1,
Name = "Math",
Questions = new List<QuestionVM>()
{
// similar to above
}
}
};

When you post, the model is populated with the ID of the selected answer for each question in each subject. Note the use of DisplayFor() for some properties. These won't post back so you would need to repopulate these properties if you return the view (e.g. ModelState is not valid). Alternatively you can generate a read-only textbox or add a hidden input for those properties. I also suggest you inspect the HTML that is generated, in particular the name attributes which will look something like

<input type="radio" name="Subjects[0].Questions[0].SelectedAnswer" ...

to give you an understanding of how collections are bound to your model on post back

Multiple radio button groups in MVC 4 Razor

Ok here's how I fixed this

My model is a list of categories. Each category contains a list of its subcategories.

with this in mind, every time in the foreach loop, each RadioButton will have its category's ID (which is unique) as its name attribue.

And I also used Html.RadioButton instead of Html.RadioButtonFor.

Here's the final 'working' pseudo-code:

@foreach (var cat in Model.Categories)
{
//A piece of code & html here
@foreach (var item in cat.SubCategories)
{
@Html.RadioButton(item.CategoryID.ToString(), item.ID)
}
}

The result is:

<input name="127" type="radio" value="110">

Please note that I HAVE NOT put all these radio button groups inside a form. And I don't know if this solution will still work properly in a form.

Thanks to all of the people who helped me solve this ;)

get selected value of multiple group of radio button asp.net mvc 5

If you convert your foreach loop within your View to a for loop, you can associate your values by using the same index i for your input names:

@for (int i = 0; i < Model.Questions.Count(); i++)
{
@Html.HiddenFor(model => model.Questions[i].QuestionId)
<div class="row m-0 bg-silver-light border">
<div class="col-12 bg-light pt-2 pb-2">
<h6>
@Model.Questions[i].QuestionTitle
</h6>
<p class="text-secondary small m-0">
@Model.Questions[i].QuestionBody
</p>
</div>


<div class="col-12 pt-2 pb-2">
<div class="form-check-inline">
<label class="form-check-label" for="radio2">
@Html.RadioButtonFor(model => model.Questions[i].Answer, 20, htmlAttributes: new { @class = "form-check-input", @id = "radio2"})
20
</label>
</div>
<div class="form-check-inline">
<label class="form-check-label" for="radio3">
@Html.RadioButtonFor(model => model.Questions[i].Answer, 40, htmlAttributes: new { @class = "form-check-input", @id = "radio3" })
40
</label>
</div>
<div class="form-check-inline">
<label class="form-check-label" for="radio4">
@Html.RadioButtonFor(model => model.Questions[i].Answer, 60, htmlAttributes: new { @class = "form-check-input", @id = "radio4" })
60
</label>
</div>
<div class="form-check-inline">
<label class="form-check-label" for="radio5">
@Html.RadioButtonFor(model => model.Questions[i].Answer, 80, htmlAttributes: new { @class = "form-check-input", @id = "radio5" })
80
</label>
</div>
<div class="form-check-inline">
<label class="form-check-label" for="radio6">
@Html.RadioButtonFor(model => model.Questions[i].Answer, 100, htmlAttributes: new { @class = "form-check-input", @id = "radio6" })
100
</label>
</div>
</div>
</div>
}

And assuming your ViewModel has a property Questions, like so:

public class QuestionReviewViewModel
{
public List<QuestionModel> Questions { get; set; } = new List<QuestionModel>();
}

You can bind your QuestionId and Answer values to your QuestionModel:

public class QuestionModel
{
public int QuestionId { get; set; }
public int Answer { get; set; }
}

By simply binding to the ViewModel in your HttpPost action:

[HttpPost]
public ActionResult PostAction(QuestionReviewViewModel vm)
{
if (ModelState.IsValid)
{
for (int i = 0; i < vm.Questions.Count; i++)
{
int id = vm.Questions[i].QuestionId;
int answer = vm.Questions[i].Answer;
}

return Content("Something good");
}
else
{
return Content("Something rotten");
}
}

Post back example

EDIT

I would use a SelectList in this case -> https://docs.microsoft.com/en-us/dotnet/api/system.web.mvc.selectlist?view=aspnet-mvc-5.2

You would add a SelectList property to your QuestionModel (see doc above for arguments), which allows you to set a selected value (Answer in this case):

// List of possible answers to this question
// populated from data source
public List<int> AnswerList = new List<int> { 20, 40, 60, 80, 100 };

// An MVC SelectList Class
public SelectList PossibleAnswers => new SelectList(AnswerList, Answer);

Then, in your view, you can loop through the possible answers, conditionally applying the checked property if the item is selected:

<div class="col-12 pt-2 pb-2">
@foreach (var item in Model.Questions[i].PossibleAnswers)
{
var htmlAttr = new Dictionary<string, object>{{ "@class", "form-check-input" }};

if (item.Selected)
{
htmlAttr.Add("@checked", true);
}

<div class="form-check-inline">
<label class="form-check-label" for="radio2">
@Html.RadioButtonFor(model => model.Questions[i].Answer, item.Text, htmlAttributes: htmlAttr)
@item.Text
</label>
</div>
}
</div>

C# MVC Radio Button Names and Values groups pass into [HTTPPost]

Asp.net core bind model with name attribute.You can change your name value of radiobutton.And add hidden input of QuestionId and choiceOption.

Here is a demo,after each radiobutton,I add a hidden input with choiceoption,and before form submit,I change the name of hiddeninput after checked radiobutton to right format.

View:

@using (Html.BeginForm("Surveys", "Home"))
{
@Html.AntiForgeryToken()
var i = 0;
foreach (var qu in Model.Questions)
{
<input hidden name="Answers[@i].AQuestionID" value=@qu.QID>
<dt>
@Html.DisplayFor(modelItem => qu.Question)
</dt>
foreach (var ch in Model.Choices)
{
if (qu.QID == ch.QuestionID)
{
<dd>
<div>
@Html.RadioButton("Answers[" + i + "].ChoiceID", ch.CID)
@Html.TextBoxFor(modelItem => ch.ChoiceOption, new { @hidden = "hidden" })
@*@Html.EditorFor(modelItem => ch.ChoiceOption, new { @hidden = true })*@
@*<input asp-for=@ch.ChoiceOption hidden />*@
@Html.DisplayFor(modelItem => ch.ChoiceOption)
</div>
</dd>
}
}
i++;
}
<dt>
<input type="submit" class="btn btn-primary btn-sm" value="Submit" />
</dt>
}
<script>
$("form").submit(function () {
$("input[type='radio']").each(function () {
if ($(this).is(":checked")) {
$(this).next().attr("name", $(this).attr("name").split("ChoiceID")[0] + "AnswerText");
}
})
})
</script>

Models:

public class ListQuestions
{
public int QID { get; set; }
public string Question { get; set; }
}
public class ListChoices
{
public int CID { get; set; }
public int QuestionID { get; set; }
public string ChoiceOption { get; set; }
}
public class ListAnswers
{
public int AID { get; set; }
public int AQuestionID { get; set; }
public string AnswerText { get; set; }
public int ChoiceID { get; set; }
}
public class ViewSurvey
{
public IEnumerable<ListQuestions> Questions { get; set; }
public IEnumerable<ListChoices> Choices { get; set; }
}

Controller(I use fake data to test):

[HttpGet]
public ActionResult Surveys()
{
ViewSurvey v = new ViewSurvey
{
Questions = new List<ListQuestions> {
new ListQuestions{ QID=1, Question="How often during the lifecycle of your project you use lesson learned?" },
new ListQuestions{ QID=2, Question="Do you know how to build a lesson learned template?" },

},
Choices = new List<ListChoices> {
new ListChoices{ CID=11,QuestionID=1, ChoiceOption="All the Time"},
new ListChoices{ CID=12,QuestionID=1, ChoiceOption="Sometimes"},
new ListChoices{ CID=13,QuestionID=1, ChoiceOption="Never"},
new ListChoices{ CID=14,QuestionID=1, ChoiceOption="Rarely"},
new ListChoices{ CID=21,QuestionID=2, ChoiceOption="No"},
new ListChoices{ CID=22,QuestionID=2, ChoiceOption="Yes"},
new ListChoices{ CID=23,QuestionID=2, ChoiceOption="Sometimes"},



}
};
return View(v);
}
[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult Surveys(List<ListAnswers> Answers)
{
return Ok();
}

result:
Sample Image

Correct Way to Code a Group of Radio Buttons using RadioButtonFor

Ok, let's do some research. First of all, you don't need unique id to toggle checkbox/radio by clicking a label. Actually you don't need id at all! All you have to do is wrap your input inside <label> tag:

<label>
<input type="radio" name="radio" value="1" /> Radio 1
</label>

So far so good, ASP.NET MVC provides you ability to code your own html helpers, let's write one for Enum model field!

Imagine, we've got some registration model:

public class RegisterViewModel
{
[Required]
[EmailAddress]
[Display(Name = "Email")]
public string Email { get; set; }

[Required]
[StringLength(100, ErrorMessage = "The {0} must be at least {2} characters long.", MinimumLength = 6)]
[DataType(DataType.Password)]
[Display(Name = "Password")]
public string Password { get; set; }

[DataType(DataType.Password)]
[Display(Name = "Confirm password")]
[Compare("Password", ErrorMessage = "The password and confirmation password do not match.")]
public string ConfirmPassword { get; set; }
}

Damn! I've just copy-pasted tutorial model o_O. What we want now - is to get user's gender, so we add a Gender enum:

public enum Gender
{
Iamparanoic = 0,

Male = 1,

Female = 2,
}

and add Gender field of Gender type to our model (wish, c# had a Gender access modifier!)

public Gender Gender { get; set; }

Now, time to use some asp.net magic and write our html helper:

public static MvcHtmlString RadioButtonsListForEnum<TModel, TEnum>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TEnum>> expression)
{
var sb = new StringBuilder();
sb.Append("<ul>"); //we want it to look cool, dont' we?
foreach (var value in Enum.GetValues(typeof(TEnum)))
{
var radio = htmlHelper.RadioButtonFor(expression, value).ToHtmlString(); //you can generage any id and pass it to helper, or use a tagbuilder

sb.AppendFormat(
"<li><label>{0} {1}</label></li>",
radio,
((Enum)value).GetEnumName() //instead of it you can grab e.g. Display/Description attribute value or w/e else
);
}
sb.Append("</ul");
return MvcHtmlString.Create(sb.ToString());
}

and usage of that will look like:

@Html.RadioButtonsListForEnum(m => m.Gender)

So pretty, isn't it? Of course you can add hella lot's of customization, like rendering radiobuttons with unique mvc-style id's, all kindes of html attributes etc. but this is just a sample, ass kick to right way.

Next, we also want to render checkboxlist!

public static MvcHtmlString CheckBoxListForEnum<TModel, TEnum>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TEnum>> expression, IDictionary<string, object> htmlAttributes = null)
{
var metadata = ModelMetadata.FromLambdaExpression(expression, htmlHelper.ViewData);
var enumValue = Convert.ToInt32(metadata.Model);

var sb = new StringBuilder();
foreach (var value in Enum.GetValues(typeof (TEnum)))
{
var val = Convert.ToInt32(value);
var checkbox = new TagBuilder("input");

checkbox.MergeAttribute("type", "checkbox");
checkbox.MergeAttribute("value", value.ToString());
checkbox.MergeAttribute("name", htmlHelper.NameFor(expression).ToString());

if ((enumValue & val) == val)
checkbox.MergeAttribute("checked", "checked");
if (htmlAttributes != null)
checkbox.MergeAttributes(htmlAttributes);

var label = new TagBuilder("label") { InnerHtml = checkbox + ((Enum)value).GetEnumName() };

sb.Append(label);
sb.Append("<br/>");
}
return new MvcHtmlString(sb.ToString());
}

And again, simple usage:

@Html.CheckBoxListForEnum(m => m.Gender)

Btw, it makes sence just for enums marked with Flags attribute, and of course you'll have troubles with model binding - it requires custom binder. But it's another question, and i hope Jon Skeet can answer it :)

ASP.NET MVC Binding unique radio button for list of objects in Razor @for loop, allowing only one to be selected

I had to change the radio button to @Html.RadioButtonFor(x => Model.SelectedImageId, Model.ImageFileDetails[i].Id) and pass an Id for each object in my list.

ViewModel:

public class ViewModel
{
public List<ImageFileDetail> ImageFileDetails { get; set; } = new List<ImageFileDetail>();
public int? SelectedImageId { get; set; }
}

public class ImageFileDetail {
public int Id { get; set; } // for main image radio button
public HttpPostedFileBase File { get; set; }
public bool IsMainImage { get; set; }
public string SourceURL { get; set; }
}

View:

@for (var i = 0; i < Model.ImageFileDetails.Count(); i++)
{
<tr>
<td>
@Html.TextBoxFor(x => x.ImageFileDetails[i].File, new { type = "file" })
</td>
<td>
@Html.EditorFor(x => x.ImageFileDetails[i].SourceURL, new { htmlAttributes = new { @class = "form-control" } })
@Html.ValidationMessageFor(x => x.ImageFileDetails[i].SourceURL, "", new { @class = "text-danger" })
</td>
<td>
@*Html.EditorFor(x => x.ImageFileDetails[i].IsMainImage)*@
@Html.RadioButtonFor(x => Model.SelectedImageId, Model.ImageFileDetails[i].Id)
@Html.ValidationMessageFor(x => x.ImageFileDetails[i].IsMainImage, "", new { @class = "text-danger" })
</td>
</tr>
}

Then in my controller, when iterating through the posted list, I set the IsMainImage to true for the SelectedImageId.

Post Controller:

foreach (var fileDetail in viewModel.ImageFileDetails)
{
if (fileDetail.Id == viewModel.SelectedImageId)
fileDetail.IsMainImage = true;
}

Thanks everyone for believing in me.



Related Topics



Leave a reply



Submit