将mvc3 radiobutton绑定到模型的正确方法

时间:2011-04-24 02:24:43

标签: asp.net-mvc-3

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

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)

如果用户完成表单没有任何错误,一切正常,但是如果我进行服务器端验证并刷新页面,则会丢失用户为单选按钮做出的选择,并且选定的无线电将返回到默认的假字段。 / p>

我是如何绑定radiobutton所以如果用户选择为true,即使在服务器端验证之后,该值仍然保留?

任何建议都会很棒!

5 个答案:

答案 0 :(得分:95)

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

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

    框架希望自动为您完成所有这些操作,但是前两个方面做错了,这使得您必须与框架斗争才能获得所需的行为。

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

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

    (因为您碰巧传递了truefalse的正确字符串表示形式,那么只要您修复了两个实际错误,这些值就会起作用,但是仍然可以通过实际值,而不是他们的字符串。)

    你的第一个真正的错误是对“No”单选按钮进行硬编码Checked = "checked"。这将覆盖框架尝试为您做的事情,并导致始终检查此单选按钮。

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

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

    您可能已经拥有的某种Account类。 (你的View模型):

    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>
    }
    

    奖励:您会注意到我在单选按钮上添加了一些额外的功能。我使用HTML label元素而不是仅使用纯文本作为单选按钮标签,并将for属性设置为每个单选按钮的ID。这使用户可以单击标签来选择单选按钮,而不必单击单选按钮本身。这是标准的HTML。为了实现这个目的,我不得不在单选按钮上设置手动ID,否则它们都会获得相同的ID“条款和条件”,这不会起作用。

答案 1 :(得分:15)

您需要在此处执行一些操作,以确保在服务器端验证后维护用户的选择。

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]控制器操作中确保在验证失败时返回模型,例如:

[HttpPost]
public ActionResult SomeForm(SomeModel model)
{
    if (!ModelState.IsValid)
        return View(model);

    // Do other stuff here
}

这种方式在验证失败后在响应中呈现视图时,它将具有传入的实际模型状态(从而保持用户的选择)。

答案 2 :(得分:4)

我无法告诉您,因为您没有显示您的代码,但我怀疑如果您在服务器端验证失败,那么您只是返回原始视图。如果失败,则需要使用提交的模型填充视图,就像返回任何其他验证错误一样。否则,您将获得默认模型值(对于注册布尔值,它将始终为false)。

也许您可以发布服务器端代码?

答案 3 :(得分:2)

我在这里提供另一个更复杂的例子。

 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;
                }

发表

  [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


}

<强>数据库

enter image description here

<强>网页

enter image description here

答案 4 :(得分:1)

我有类似的问题并通过在控制器中设置ViewData值来跟踪用户选择的内容来解决问题。