将mvc3单选button绑定到模型的正确方法
我有一个视图,其中包含我的网站条款和条件的单选button列表。
例如
Yes @Html.RadioButtonFor(model => model.TermsAndConditions, "True") No @Html.RadioButtonFor(model => model.TermsAndConditions, "False", new { Checked = "checked" }) </div> @Html.ValidationStyledMessageFor(model => model.TermsAndConditions)
如果用户完成表单没有任何错误,一切都可以,但是如果我在服务器端进行validation并刷新页面,我将失去用户为单选button所做的select,并且所选无线电回到默认的假字段。
我如何绑定单选button,所以如果用户selecttrue,那么即使在经过服务器validation之后,该值仍然保留?
任何build议将是伟大的!
简而言之,你需要做三件事:
- 从第二个单选button中删除
new { Checked = "checked" }
。 这个硬编码的检查值将覆盖所有的魔法。 - 当您从控制器操作中返回您的ViewResult时,给它一个您的模型类的实例,其中TermsAndConditions为false。 这将提供您需要的默认假值,以便为您预先select假单选button。
- 使用
true
和false
作为单选button的值而不是"True"
和"False"
。 这是因为你的财产属于bool
types。 严格来说,您巧合地select了正确的string表示true
和false
,但RadioButtonFor方法的值参数是object
types的。 最好传入你想要比较的实际types,而不是把它自己转换成string。 更多关于这个下面。
以下是深入的内容:
框架想自动为你做所有这些,但是你错误地做了这两件事情,这使得你必须与框架作斗争才能获得你想要的行为。
RadioButtonFor方法在您指定的属性的值上调用.ToString()
,并将其与您在创build单选button时传入的值的.ToString()
进行比较。 如果它们相等,则它在内部设置isChecked = true
并在HTML中结束呈现checked="checked"
。 这是如何决定要检查哪个单选button。 它只是比较单选button的值和属性的值并检查匹配的值。
你可以通过这种方式为任何属性渲染单选button,它会神奇的工作。 string,整数,甚至枚举types都工作! 任何具有ToString
方法的对象都会返回一个唯一表示对象值的string。 您只需确保将单选button的值设置为您的属性实际可能具有的值。 最简单的方法是传入值本身,而不是值的string表示。 让框架将它转换为一个string给你。
(既然你恰好传递了正确的string表示的false
,那么只要你解决了两个实际的错误,这些值就会工作,但传递实际值而不是string是明智的。)
你的第一个真正的错误是硬编码Checked = "checked"
为“否”单选button。 这将覆盖框架正在为您做的事情,并导致这个单选button总是被检查。
显然,你想要预先select“否”单选button,但是你必须以与上述所有内容兼容的方式来完成。 您需要为视图提供您的模型类的一个实例,其中TermsAndConditions设置为false,并让它“绑定”到单选button。 通常情况下,响应URL的初始GET请求的控制器操作不会为View提供模型类的实例。 通常,您只需return View();
。 但是,由于您需要select默认值,因此必须为视图提供一个模型实例,其中的TermsAndConditions设置为false。
下面是一些说明所有这些的源代码:
某种帐户类,您可能已经有。 (您的视图模型):
public class Account { public bool TermsAndConditions { get; set; } //other properties here. }
控制器中的一些方法:
//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(); }
整个视图:
@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> }
奖金:你会注意到我增加了一些额外的function,单选button。 我使用HTML label
元素,将for
属性设置为每个单选button的ID,而不是仅仅使用纯文本作为单选button标签。 这让用户点击标签来select单选button,而不必单击单选button本身。 这是标准的HTML。 为了这个工作,我必须在单选button上设置手动ID,否则他们都会得到相同的ID“TermsAndConditions”,这是行不通的。
为了确保在服务器端validation之后维护用户的select,您需要在这里执行一些操作。
a)在视图中将每个无线电的“已检查”属性绑定到您的模型,例如:
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)首次显示视图时定义初始默认值,初始化返回到GET请求中的视图(在控制器操作中)的模型,例如:
public ActionResult SomeForm() { return View(new SomeModel { TermsAndConditions = false }); }
b)确保您的[HttpPost]控制器操作在validation失败时返回模型,例如:
[HttpPost] public ActionResult SomeForm(SomeModel model) { if (!ModelState.IsValid) return View(model); // Do other stuff here }
这样当validation失败后,视图在响应中呈现时,它将具有传入的实际模型状态(从而保持用户的select)。
因为你没有显示你的代码,所以我不能确定,但我怀疑如果你在服务器端validation失败,你只是返回原始视图。 如果失败,则需要使用提交的模型填充视图,就像返回任何其他validation错误一样。 否则,你会得到默认的模型值(对于注册布尔值,这将始终是错误的)。
也许你可以发布你的服务器端代码?
在这里,我提供了另一个更复杂的例子。
public enum UserCommunicationOptions { IPreferEmailAndSMS = 1, IPreferEmail = 2, IPreferSMS = 3 }
HTML
@model UserProfileView // Some other code <div class="form-group"> <label class="col-lg-2 control-label">Communication</label> <div class="col-lg-10"> <div class=" col-xs-"> @if (Model.UserCommunicationOption.ToString() == UserCommunicationOptions.IPreferEmailAndSMS.ToString()) { @Html.RadioButtonFor(x => x.UserCommunicationOption, (int)UserCommunicationOptions.IPreferEmailAndSMS, new { @checked = "checked" }) } else { @Html.RadioButtonFor(x => x.UserCommunicationOption, (int)UserCommunicationOptions.IPreferEmailAndSMS) } <label class=" control-label" for="@Model.UserCommunicationOption">I Prefer Email And SMS</label> </div> <div class=" col-xs-"> @if (Model.UserCommunicationOption.ToString() == UserCommunicationOptions.IPreferEmail.ToString()) { @Html.RadioButtonFor(x => x.UserCommunicationOption, (int)UserCommunicationOptions.IPreferEmail, new { @checked = "checked" }) } else { @Html.RadioButtonFor(x => x.UserCommunicationOption, (int)UserCommunicationOptions.IPreferEmail) } <label class=" control-label" for="@Model.UserCommunicationOption">I Prefer Email</label> </div> <div class=" col-xs-"> @if (Model.UserCommunicationOption.ToString() == UserCommunicationOptions.IPreferSMS.ToString()) { @Html.RadioButtonFor(x => x.UserCommunicationOption, (int)UserCommunicationOptions.IPreferSMS, new { @checked = "checked" }) } else { @Html.RadioButtonFor(x => x.UserCommunicationOption, (int)UserCommunicationOptions.IPreferSMS) } <label class=" control-label" for="@Model.UserCommunicationOption">@DLMModelEntities.Properties.Resource.IPreferSMS</label> </div> </div> </div> Model [Required(ErrorMessageResourceName = "Communications", ErrorMessageResourceType = typeof(Resource))] [Display(Name = "Communications", ResourceType = typeof(DLMModelEntities.Properties.Resource))] public UserCommunicationOptions UserCommunicationOption { get; set; }
得到
var client = AppModel.Clients.Single(x => x.Id == clientId); if (Convert.ToBoolean(client.IsEmailMessage) && Convert.ToBoolean(client.IsSMSMessage)) { model.UserCommunicationOption = UserCommunicationOptions.IPreferEmailAndSMS; } else if (Convert.ToBoolean(client.IsEmailMessage)) { model.UserCommunicationOption = UserCommunicationOptions.IPreferEmail; } else if ( Convert.ToBoolean(client.IsSMSMessage)) { model.UserCommunicationOption = UserCommunicationOptions.IPreferSMS; }
POST
[HttpPost] public ActionResult MyProfile(UserProfileView model) { // Some code var client = AppModel.Clients.Single(x => x.Id == clientId); if (model.UserCommunicationOption == UserCommunicationOptions.IPreferEmail) { client.IsSMSMessage = false; client.IsEmailMessage = true; } else if (model.UserCommunicationOption == UserCommunicationOptions.IPreferEmailAndSMS) { client.IsSMSMessage = true; client.IsEmailMessage = true; } else if (model.UserCommunicationOption == UserCommunicationOptions.IPreferSMS) { client.IsSMSMessage = true; client.IsEmailMessage = false; } AppModel.SaveChanges(); //Some code }
数据库
网页
我有一个类似的问题,并通过在控制器中设置一个ViewData值来解决这个问题,以跟踪用户select的内容。