web analytics

ASP.NET MVC CheckboxFor retains previous value even when ViewModel is posted back with different value

Options

codeling 1560 - 6504
@2022-02-08 21:21:58

I'm attempting to add an accept checkbox on the Terms and Conditions page of an MVC application.

If the user accepts the Terms and Conditions, but fails to log on for some other reason (bad password etc), then I want the accept checkbox not to be checked, so the user is forced to accept the T&Cs on every registration attempt.

The problem is that using Html.CheckboxFor(), after a postback the checkbox retains its previous value, despite the value of the bound ViewModel property.

Here's the code, stripped down to essentials. If you run this code up, check the checkbox, and click the button, you'll be returned to the form with the checkbox still checked, even though the bound model property is false.

The Model:

namespace Namespace.Web.ViewModels.Account
{
    public class RegisterViewModel
    {
        [IsTrue("You must agree to the Terms and Conditions.")]
        public bool AcceptTermsAndConditions { get; set; }
    }
}

The validation attribute:

public class IsTrueAttribute : ValidationAttribute
{
    public IsTrueAttribute(string errorMessage) : base(errorMessage)
    {
    }

    public override bool IsValid(object value)
    {
        if (value == null) return false;
        if (value.GetType() != typeof(bool)) throw new InvalidOperationException("can only be used on boolean properties.");
        return (bool)value;
    }
}

The View:

@model Namespace.Web.ViewModels.Account.LogOnInputViewModel

@using (Html.BeginForm()) {
    @Html.CheckBoxFor(x => x.AcceptTermsAndConditions)
    <input type="submit" value="Log On" />
}

The Controller:

    [HttpGet]
    public ActionResult Register(string returnUrl)
    {
        return View(new RegisterViewModel { AcceptTermsAndConditions = false });
    }

    [HttpPost]
    public ActionResult Register(RegisterViewModel input)
    {
        return View(new RegisterViewModel { AcceptTermsAndConditions = false });
    }
@2022-02-08 21:23:57

By design, CheckBoxFor and other data-bound helpers are bound first against the ModelState, and then against the model if there is no ModelState for the element.

You need to clear the ModelState for AcceptTermsAndConditions by add the following to your POST action:

ModelState.Remove("AcceptTermsAndConditions");

Comments

You must Sign In to comment on this topic.


© 2022 Digcode.com