5

I have a page with multiple forms, each as a partial. I want to post each partial on submit. If there are errors, I want the validation errors to show in the partial as part of the main page i.e. I don't want to just see the partial on it's own page if there are errors. Am I correct in saying this behavior is only possible with an ajax post? How would I return the model state errors WITHOUT an ajax post, just a normal form post?

Edit: Still seeing the partial on it's own page

Partial -

@using (Html.BeginForm("Login", "Account", FormMethod.Post, new { id = "LoginForm" }))
{
    @Html.ValidationMessage("InvalidUserNamePassword")
    <fieldset class="fieldset">
        <div>
            <label for="form-field-user_id">User ID</label>
            <span>
                @Html.TextBoxFor(x => x.Username, new { @class = "form-field__input form-field__input--text", @id = "form-field-user_id"})                      
            </span>
        </div>
    </fieldset>
    <div class="form-field__button">
        <button id="loginButton" type="submit" class="button button--primary">Login</button>
    </div>
}

<script>
    $('#loginButton').click(function () {
        $.ajax({
            type: "POST",
            url: '@Url.Action("Login", "Account")',
            data: $('form').serialize(),
            success: function (result) {
                if (result.redirectTo) {
                    window.location.href = result.redirectTo;
                } else {
                    $("#LoginForm").html(result);
                }
            },
            error: function () {
                $("#LoginForm").html(result);
            }
        });
    });
</script>

Controller -

[HttpPost]
public ActionResult Login(LoginModel model)
{
    if (!ModelState.IsValid)
    {
        return PartialView("~/Views/Account/_Login.cshtml", model);
    }
    return Json(new { redirectTo = Url.Action("Index", "Profile") });
}
4
  • You want the model state errors on the main page apart from the partials? Commented Sep 29, 2015 at 18:39
  • You just return a partial view of the form and replace the original form with the returned html in the ajax success callback Commented Sep 30, 2015 at 0:15
  • I want the model state errors in the partial, but the partial must be shown on the main page as it was before form submit, not on its own Commented Sep 30, 2015 at 7:18
  • @StephenMuecke Can you give me an example/link please? Commented Sep 30, 2015 at 7:31

1 Answer 1

7

Yes, you are correct in saying this behavior is only possible with an ajax post.

There are a few problems with your current script meaning that you will not get the desired results.

Firstly your button is a submit button meaning that it will do a normal submit in addition to the ajax call unless you cancel the default event (by adding return false; as the last line of code in your script). However it would be easier to just change the button type to type="button"

<button id="loginButton" type="button" class="button button--primary">Login</button>

The ajax call will now update the existing page, however it will add the returned partial inside the existing <form> element resulting in nested forms which is invalid html and not supported. Change your html to wrap the main views form in another element

<div id="LoginFormContainer">
    @using (Html.BeginForm("Login", "Account", FormMethod.Post, new { id = "LoginForm" }))
    {
        ....
        <button id="loginButton" type="button" class="button button--primary">Login</button>
    }
</div>

and then modify the script to update the html of the outer element

success: function (result) {
    if (result.redirectTo) {
        window.location.href = result.redirectTo;
    } else {
        $("#LoginFormContainer").html(result); // modify
    }
},

Finally, your rendering dynamic content so client side validation will not work for the returned form. Assuming your properties have validation attributes (for example the [Required] attribute on the Userame property), you need to reparse the validator after loading the content

var form = $('#LoginForm');
....
} else {
    $("#LoginFormContainer").html(result);
    // reparse validator
    form.data('validator', null);
    $.validator.unobtrusive.parse(form);
}

You noted that you have multiple forms on the page, in which case your ajax options should be

data: $('#LoginForm').serialize(),

or if your declare var form = $('#LoginForm'); as per the above snippet, then data: form.serialize(), to ensure you are serializing the correct form.

Side note: There is no real need to change the id attribute of the textbox (it will be id=Username" by default and you can simply use

@Html.LabelFor(x => x.UserName, "User ID")
@Html.TextBoxFor(x => x.Username, new { @class = "form-field__input form-field__input--text" })  

or just @Html.LabelFor(x => x.UserName) of the property is decorated with [Display(Name = "User ID")]

Sign up to request clarification or add additional context in comments.

1 Comment

This should be the perfect example to display the errors in a partial view.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.