Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Correct way to bind an mvc3 radiobutton to a model

I have a view that contains a radiobutton list for my terms and conditions of the site.

e.g.

Yes @Html.RadioButtonFor(model => model.TermsAndConditions, "True") No @Html.RadioButtonFor(model => model.TermsAndConditions, "False",      new { Checked = "checked" }) </div> @Html.ValidationStyledMessageFor(model => model.TermsAndConditions) 

All is ok if the user completes the form without any errors however if I do serverside validation and the page is refreshed I lose the selection that the user made for the radiobutton and the selected radio goes back to the default false field.

How am I meant to be binding the radiobutton so if a user selects true this value is maintained even after serverside validation?

Any suggestions would be great!

like image 685
Diver Dan Avatar asked Apr 24 '11 02:04

Diver Dan


People also ask

How do you bind a radio button to a model?

Right-click on Model and Add Model with name CustomerDetails. After adding the Model inside Model I added the following Properties: List of Mobiledata (for binding the radiobutton) SelectedAnswer (for getting the selected value from the database)

What property used to set the radio buttons to a group?

Use the GroupName property to specify a grouping of radio buttons to create a mutually exclusive set of controls. You can use the GroupName property when only one selection is possible from a list of available options. When this property is set, only one RadioButton in the specified group can be selected at a time.

Is it possible to add an image on the radio button control?

To use a custom image in a radio button control or a checkbox control, ensure the Add display image check box is selected. If you right-click on the Display field, this will open a prompt allowing the user to select an image on either the Reporting Server or WebFOCUS Client. The formats allowed are .


2 Answers

For the short answer, you need to do three things:

  1. Remove the new { Checked = "checked" } from the second radio button. This hard-coded checked value will override all of the magic.
  2. When you return your ViewResult from the controller action, give it an instance of your model class where TermsAndConditions is false. This will provide the default false value you need in order to have the false radio button preselected for you.
  3. Use true and false as the values for your radio buttons instead of "True" and "False". This is because your property is of type bool. Strictly speaking, you coincidentally chose the correct string representations for true and false, but the value parameter for the RadioButtonFor method is of type object. It's best to pass in the actual type you want to compare to rather than converting it to a string yourself. More on this below.

Here's what's going on in depth:

The framework wants to do all of this for you automatically, but you did those first two things incorrectly which makes you have to fight with the framework to get the behavior you want.

The RadioButtonFor method calls .ToString() on the value of the property you specified and compares it to the .ToString() of the value you passed in when creating the radio button. If they are equal, then it internally sets isChecked = true and ends up rendering checked="checked" in the HTML. This is how it decides which radio button to check. It simply compares the value of the radio button to the value of the property and checks the one that matches.

You can render radio buttons for pretty much any property this way and it will magically work. Strings, ints, and even enum types all work! Any object that has a ToString method that returns a string which uniquely represents the object's value will work. You just have to make sure you're settings the radio button's value to a value that your property might actually have. The easiest way to do this is just to pass in the value itself, not the string representation of the value. Let the framework convert it to a string for you.

(Since you happened to pass in the correct string representations of true and false, then those values will work as long as you fix your two actual mistakes, but it's still wise to pass in the actual values and not their strings.)

Your first real mistake was hard-coding Checked = "checked" for the "No" radio button. This will override what the framework is trying to do for you and results in this radio button always being checked.

Obviously you want the "No" radio button to be preselected, but you have to do it in a way that's compatible with everything above. You need to give the view an instance of your model class where TermsAndConditions is set to false, and let it "bind" that to the radio buttons. Normally, a controller action which responds to the initial GET request of a URL doesn't give the View an instance of the model class at all. Typically, you just return View();. However, since you want a default value selected, you must provide the view with a instance of your model that has TermsAndConditions set to false.

Here is some source code illustrating all of this:

Some sort of Account class that you probably already have. (Your View's model):

public class Account {     public bool TermsAndConditions { get; set; }     //other properties here. } 

Some methods in your controller:

//This handles the initial GET request. public ActionResult CreateAccount() {     //this default instance will be used to pre-populate the form, making the "No" radio button checked.     var account = new Account     {         TermsAndConditions = false     };      return View( account ); }  //This handles the POST request. [HttpPost] public ActionResult CreateAccount( Account account ) {     if ( account.TermsAndConditions )     {         //TODO: Other validation, and create the account.         return RedirectToAction( "Welcome" );     }     else     {         ModelState.AddModelError( "TermsAndConditionsAgreement", "You must agree to the Terms and Conditions." );         return View( account );     }            }  //Something to redirect to. public ActionResult Welcome() {     return View(); } 

The entire View:

@model Account @{     ViewBag.Title = "Create Account"; } @using ( Html.BeginForm() ) {     <div>         <span>Do you agree to the Terms and Conditions?</span>         <br />         @Html.RadioButtonFor( model => model.TermsAndConditions, true, new { id = "TermsAndConditions_true" } )         <label for="TermsAndConditions_true">Yes</label>         <br />         @Html.RadioButtonFor( model => model.TermsAndConditions, false, new { id = "TermsAndConditions_false" } )         <label for="TermsAndConditions_false">No</label>         <br />         @Html.ValidationMessage( "TermsAndConditionsAgreement" )     </div>     <div>         <input id="CreateAccount" type="submit" name="submit" value="Create Account" />     </div> } 

BONUS: You'll notice that I added a little extra feature to the radio buttons. Rather than just use plain text for the radio button labels, I used the HTML label element with the for attribute set to the IDs of the each radio button. This lets users click on the label to select the radio button instead of having to click on the radio button itself. This is standard HTML. For this to work I had to set manual IDs on the radio buttons, otherwise they would both get the same ID of just "TermsAndConditions", which wouldn't work.

like image 99
Glazed Avatar answered Oct 12 '22 01:10

Glazed


There are a few things you need to do here in order to ensure the user's selection is maintained after server side validation.

a) Bind the "checked" property of each radio to your model in the view, for example:

Yes @Html.RadioButtonFor(model => model.TermsAndConditions, "True", model.TermsAndConditions == true ? new { Checked = "checked" } : null) No @Html.RadioButtonFor(model => model.TermsAndConditions, "False", model.TermsAndConditions == false ? new { Checked = "checked" } : null) 

b) To define the initial default value when the view is first displayed, initialise the model returned to the view in the GET request (in the controller action), for example:

public ActionResult SomeForm() {     return View(new SomeModel { TermsAndConditions = false }); } 

b) Ensure in your [HttpPost] controller action that you return the model when the validation fails, for example:

[HttpPost] public ActionResult SomeForm(SomeModel model) {     if (!ModelState.IsValid)         return View(model);      // Do other stuff here } 

This way when the view is rendered in the response after validation fails, it will have the actual model state that was passed in (thus maintaining the user's selection).

like image 43
Michael Avatar answered Oct 12 '22 00:10

Michael