将 mvc3 单选按钮绑定到模型的正确方法 [英] Correct way to bind an mvc3 radiobutton to a model

查看:29
本文介绍了将 mvc3 单选按钮绑定到模型的正确方法的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个视图,其中包含我的网站条款和条件的单选按钮列表.

例如

是@Html.RadioButtonFor(model => model.TermsAndConditions, "True")不@Html.RadioButtonFor(model => model.TermsAndConditions, "False",新 { 已检查 = "已检查" })

@Html.ValidationStyledMessageFor(model => model.TermsAndConditions)

如果用户完成表单没有任何错误,一切都可以,但是如果我进行服务器端验证并刷新页面,我会丢失用户为单选按钮所做的选择,并且所选单选返回到默认的 false 字段.

我打算如何绑定单选按钮,以便如果用户选择 true,即使在服务器端验证之后,该值仍会保留?

任何建议都会很棒!

解决方案

简而言之,您需要做三件事:

  1. 从第二个单选按钮中删除 new { Checked = "checked" }.这个硬编码的检查值将覆盖所有的魔法.
  2. 当你从控制器操作返回你的 ViewResult 时,给它一个你的模型类的实例,其中 TermsAndConditions 是假的.这将提供您需要的默认 false 值,以便为您预选 false 单选按钮.
  3. 使用 truefalse 作为单选按钮的值,而不是 "True""False".这是因为您的属性属于 bool 类型.严格来说,您巧合地为 truefalse 选择了正确的字符串表示,但是 RadioButtonFor 方法的 value 参数是 object 类型.最好传入要比较的实际类型,而不是自己将其转换为字符串.更多内容请参见下文.

以下是正在发生的事情:

框架希望自动为您完成所有这一切,但是您没有正确地做前两件事,这使您不得不与框架斗争以获得您想要的行为.

RadioButtonFor 方法对您指定的属性值调用 .ToString() 并将其与您在创建时传入的值的 .ToString() 进行比较单选按钮.如果它们相等,则它在内部设置 isChecked = true 并最终在 HTML 中呈现 checked="checked".这就是它决定要检查哪个单选按钮的方式.它只是将单选按钮的值与属性值进行比较并检查匹配的值.

您可以通过这种方式为几乎任何属性呈现单选按钮,并且它会神奇地工作.字符串、整数,甚至枚举类型都可以使用!任何具有 ToString 方法的对象都可以返回唯一表示对象值的字符串.您只需要确保将单选按钮的值设置为您的属性可能实际具有的值.最简单的方法是只传递值本身,而不是值的字符串表示.让框架为您将其转换为字符串.

(因为您碰巧传入了 truefalse 的正确字符串表示,那么只要您修复了两个实际错误,这些值就会起作用,但它是传递实际值而不是它们的字符串仍然是明智的.)

你的第一个真正的错误是硬编码了否"单选按钮的Checked = "checked".这将覆盖框架尝试为您执行的操作,并导致始终选中此单选按钮.

显然您希望预先选择否"单选按钮,但您必须以与上述所有内容兼容的方式进行选择.您需要为视图提供一个模型类的实例,其中 termAndConditions 设置为 false,并让它绑定"到单选按钮.通常,响应 URL 的初始 GET 请求的控制器操作根本不会为 View 提供模型类的实例.通常,您只需return View();.但是,由于您希望选择默认值,因此您必须为视图提供一个模型实例,该实例的 termAndConditions 设置为 false.

以下是一些说明所有这些的源代码:

您可能已经拥有的某种 Account 类.(您的视图模型):

公共类帐户{公共布尔条款和条件{得到;放;}//这里的其他属性.}

控制器中的一些方法:

//这里处理初始的GET请求.公共 ActionResult CreateAccount(){//此默认实例将用于预填充表单,选中否"单选按钮.var 帐户 = 新帐户{条款和条件 = 假};返回视图(帐户);}//这将处理POST请求.[HttpPost]公共 ActionResult CreateAccount( 账户 account ){if ( account.TermsAndConditions ){//TODO: 其他验证,并创建帐户.return RedirectToAction("欢迎");}别的{ModelState.AddModelError( "TermsAndConditionsAgreement", "您必须同意条款和条件.");返回视图(帐户);}}//要重定向到的东西.公共 ActionResult 欢迎(){返回视图();}

整个视图:

@model 帐号@{ViewBag.Title = "创建账户";}@using ( Html.BeginForm() ){<div><span>您同意这些条款和条件吗?</span><br/>@Html.RadioButtonFor(model => model.TermsAndConditions, true, new { id = "TermsAndConditions_true" } )<label for="TermsAndConditions_true">是</label><br/>@Html.RadioButtonFor(model => model.TermsAndConditions, false, new { id = "TermsAndConditions_false" } )<label for="TermsAndConditions_false">否</label><br/>@Html.ValidationMessage("TermsAndConditionsAgreement")

<div><input id="CreateAccount" type="submit" name="submit" value="创建账户"/>

}

奖励:您会注意到我为单选按钮添加了一些额外的功能.我使用 HTML label 元素并将 for 属性设置为每个单选按钮的 ID,而不是仅使用纯文本作为单选按钮标签.这使用户可以单击标签来选择单选按钮,而不必单击单选按钮本身.这是标准的 HTML.为此,我必须在单选按钮上设置手动 ID,否则它们都会获得相同的 ID,即TermsAndConditions",这是行不通的.

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!

解决方案

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.

这篇关于将 mvc3 单选按钮绑定到模型的正确方法的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
其他开发最新文章
热门教程
热门工具
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆