Question

I'm using jquery unobtrusive validation in a new MVC4 solution. My problem is that on my login page when I click the submit button the page is still posted even though my required fields are empty.

I have another page where I have the same setup and that one doesn't post.

This is my login page:

<div id="loginForm">

    @Html.SiteLogo()
    @using (Html.BeginForm(new { ReturnUrl = ViewBag.ReturnUrl })) {
        @Html.AntiForgeryToken()
        @Html.ValidationSummary(true)

        <div id="loginForm-container" class="spacer-below">            
            <fieldset>
                <legend>Log in Form</legend>
                <div class="input-control text" data-role="input-control">                    
                    @Html.TextBoxFor(m => m.UserName, new { placeholder="Please enter your username"})
                    @Html.ValidationMessageFor(m => m.UserName)
                </div>                    

                <div class="input-control password" data-role="input-control">                                        
                    @Html.PasswordFor(m => m.Password, new { placeholder="Please enter your password"})
                    @Html.ValidationMessageFor(m => m.Password)
                </div>

                <div class="input-control checkbox" data-role="input-control">
                    <label>
                        @Html.CheckBoxFor(m => m.RememberMe)
                        <span class="check"></span>&nbsp;Remember me
                    </label>
                </div>

                <div class="input-control">    
                    <input type="submit" class="primary small" value="Log in" />
                </div>
            </fieldset>                
        </div>

        <div class="forgotPassword">@Html.ActionLink("Forgotten password?", "ForgotPassword")</div>
        <span>@Html.Partial("_CustomerSupport")</span>
    }
</div>

And the viewmodel for this page:

public class LoginModel
{
    [Required]
    [Display(Name = "User name")]
    public string UserName { get; set; }

    [Required]
    [DataType(DataType.Password)]
    [Display(Name = "Password")]
    public string Password { get; set; }

    [Display(Name = "Remember me")]
    public bool RememberMe { get; set; }
}

And this the HTMl for a page that does work i.e. does not post if the input is empty

<div id="resetPasswordForm">

    @Html.SiteLogo()
    <h4>Reset Password</h4>
    @using (Html.BeginForm(new {ReturnUrl = ViewBag.ReturnUrl}))
    {
        @Html.AntiForgeryToken()
        @Html.ValidationSummary(true)

        @Html.HiddenFor(vm => vm.Authentication.Token)                
        @Html.ValidationMessageFor(vm => vm.Authentication.Token)

        <div id="resetPasswordForm-container" class="spacer-below">            
            <fieldset>
                <legend>Reset password form</legend>
                <div class="input-control text" data-role="input-control">                    
                    @Html.TextBoxFor(vm => vm.Authentication.EmailAddress, new { placeholder="Please enter your email address"})                        
                    @Html.ValidationMessageFor(vm => vm.Authentication.EmailAddress)
                </div>                    
                <div class="input-control password" data-role="input-control">                    
                    @Html.PasswordFor(vm => vm.NewPassword, new { placeholder="Please enter a new password"})                        
                    @Html.ValidationMessageFor(vm => vm.NewPassword)
                </div>                    
                <div class="input-control text" data-role="input-control">                    
                    @Html.PasswordFor(vm => vm.ConfirmPassword, new { placeholder="Please enter the password again"})                        
                    @Html.ValidationMessageFor(vm => vm.ConfirmPassword)
                </div>                                    
                <div class="input-control">    
                    <input type="submit" class="primary medium" value="Reset Password" />
                </div>
            </fieldset>                
        </div>
        <span>@Html.Partial("_CustomerSupport")</span>
    }
</div>

And my viewmodel for this page:

public class ResetPasswordViewModel
{
    public ResetPasswordViewModel()
    {
        Authentication = new ResetPasswordConfirmViewModel();
    }

    public ResetPasswordConfirmViewModel Authentication { get; set; }

    [Required]
    [StringLength(100, ErrorMessage = "The {0} must be at least {2} characters long.", MinimumLength = 6)]
    [DataType(DataType.Password)]
    [Display(Name = "New password")]
    public string NewPassword { get; set; }

    [DataType(DataType.Password)]
    [Display(Name = "Confirm new password")]
    [Compare("NewPassword", ErrorMessage = "The new passwords do not match, please enter again.")]
    public string ConfirmPassword { get; set; }
}

My view references the appropriate scripts as checked when I view the source:

<script src="/Scripts/jquery.validate.js"></script>
<script src="/Scripts/jquery.validate.unobtrusive.js"></script>

I should also mention that I'm following the guide from:

http://nickstips.wordpress.com/2011/08/18/asp-net-mvc-displaying-client-and-server-side-validation-using-qtip-tooltips/

In order to try and get hover like validation. This works perfectly except on the Login form. After further investigation it appears that the code is error'ing out in the onError() method change due to possibly there being no validation on this field (I do not require validation on the remember me as it's optional).

var replace = $.parseJSON(container.attr("data-valmsg-replace")) !== false;

I'm guessing as my input is not required for validation it doesn't have the data-valmsg-replace attribute and hence this line is causing the javascript to fail and so the post goes through?

Was it helpful?

Solution

The problem appeared to be because of the Remember Me checkbox not having any validation attributes applied. When the code got into the onError() method it was failing on this line:

var replace = $.parseJSON(container.attr("data-valmsg-replace")) !== false;

as container.attr("data-valmsg-replace") was an empty string.

I replaced this code with:

var elementJson = container.attr("data-valmsg-replace") || 'false';
var replace = $.parseJSON(elementJson) !== false;

and it all seems to work ok now. I'm not sure if that is the best way to write the javascript but seems to do the trick.

The original code as mentioned in the question was from:

MVC clientside and serverside using qTip

OTHER TIPS

Make sure your view references the scripts:

<script src="@Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript"></script>
<script src="@Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")" type="text/javascript"></script>

That might sound obvious, but for certain options on the create view dialog box, even if you have Reference script libraries checked, it won't actually add the scripts to the view, so it's worth double-checking.

Edit

Just noticed you're using MVC 4, in which case, this might be what you're missing at the bottom of your view:

@section Scripts {
    @Scripts.Render("~/bundles/jqueryval")
}

I was actually able to reproduce the problem of the validation not firing if the scripts aren't bundled.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top