I am really struggling with knowing how to put this all together. I've built forms inside .net MVC pages plenty of times, with and without validation. And I've built forms using jQuery, with and without validation. And I've built forms inside of a modal, but never with MVC.
I understand from my original question that because this form is inside a modal that I'll need to use jQuery to handle the submit. I am having a heck of a time figuring out how to put all these moving pieces together. So far, I've not found a tutorial (or combo of tutorials) that puts this all together.
Here is what I need:
EDIT:
Thank you, Jason, for your help! Based on your suggestions, this is how I got it working.
The Parent View:
The modal:
<div class="modal fade" id="AccountEditModal" tabindex="-1" role="dialog" aria-labelledby="AccountEditModalLabel"> <div class="modal-dialog modalAccountEdit" role="document"> <div class="modal-content"> <div class="modal-header"> <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button> <h3><strong>Edit Account Profile - <span class="accountname"></span></strong></h3> </div> <div class="modal-body"> <div id="formContent"> @Html.Partial("_AccountProfileEdit", new GEDCPatientPortal.Models.AccountProfileEditViewModel()) </div> </div> </div> </div> </div>
And then here's the scripts:
@section Scripts { <script> $(document).ready(function () { $('#AccountEditModal').on('shown.bs.modal', function () { $('#myInput').focus() }) $("#AccountEditModal").on("submit", "#form-accountprofileedit", function (e) { e.preventDefault(); // prevent standard form submission var form = $(this); $.ajax({ url: form.attr("action"), method: form.attr("method"), // post data: form.serialize(), success: function (partialResult) { $("#formContent").html(partialResult); } }); }); }); </script> }
The Partial View (slimmed down):
@using (Html.BeginForm("AccountProfileEdit", "Account", FormMethod.Post, new { id = "form-accountprofileedit", @class = "full-form" })) { @Html.CustomTextboxFor(model => model.Address) <div style="text-align:right;"> <button type="submit" id="accountprofileedit-submit" name="accountprofileedit-submit" value="Edit Account" class="btn btn-primary" style="margin-left:5px;">Edit Account</button> <button type="button" class="btn btn-primary" data-dismiss="modal">Cancel</button> </div> }
The Controller:
[HttpPost] public ActionResult AccountProfileEdit(AccountProfileEditViewModel model) { if (ModelState.IsValid) { // logic to store form data in DB } return PartialView("_AccountProfileEdit", model); }
The jQuery validation plugin leverages a CSS selector like syntax to apply a set of validation rules. You can download the plugin (js) file from jQuery website. The password and confirm password objects are matched by the validation plugin for you and shows the message on the equalTo attribute if they don't match.
You can use the built-in MVC validation scripts along with the data annotaions on your model
public class AccountProfileEditViewModel { [Display(Name = "Address")] [Required()] [StringLength(200)] public string Address { get; set; } }
Make a partial view to hold your modal form.
_AccountProfileEdit.cshtml
@model AccountProfileEditViewModel @using(Html.BeginForm("AccountProfileEdit", "Account", FormMethod.Post, new { id = "form-accountedit-appt" }) { @Html.ValidationSummary(true) @Html.LabelFor(m => m.Address) @Html.TextBoxFor(m => m.Address) @Html.ValidationMessageFor(m => m.Address) <button type="submit">Edit</button> }
Then reference this in your modal box. If you want pre-populated model you'll need to render an action:
<div class="modal-body" id="form-container"> @Html.Action("AccountProfileEdit", "Account", new { id=account.Id }) </div>
If you just want a blank form then you can just use:
<div class="modal-body" id="form-container"> @Html.Partial("_AccountProfileEdit") </div>
The action uses the id
parameter to fetch and populate the model
[HttpGet] public ActionResult AccountProfileEdit(int id) { AccountProfileEditViewModel model = db.GetAccount(id); // however you do this in your app return PartialView("_AccountProfileEdit", model); }
AJAX POST
Now you'll need AJAX to submit this form. If you rely on a standard form submission the browser will navigate away from your page (and close your modal).
$("#myModal").on("submit", "#form-accountedit", function(e) { e.preventDefault(); // prevent standard form submission var form = $(this); $.ajax({ url: form.attr("action"), method: form.attr("method"), // post data: form.serialize(), success: function(partialResult) { $("#form-container").html(partialResult); } }); });
You need to use the event delegate $(staticParent).on(event, target, handler)
for the submit event because the form content may be replaced later.
Post Action
[HttpPost] public ActionResult AccountProfileEdit(AccountProfileEditViewModel model) { // Request.Form is model if (ModelState.IsValid) { // do work return PartialView("_AccountEditSuccess"); } return PartialView("_AccountProfileEdit", model); }
Client-side validation scripts should prevent them from ever submitting. But if that somehow failed or if you can't validate something on the client then you have ModelState.IsValid
. You might also invalidate something server-side manually.
_AccountEditSuccess.cshtml
And the "success" partial view.
<div>Success! <button>Click to close</button></div>
Not Valid is a Fail, Right?
From your AJAX success handler you have
success: function(partialResult) { $("#form-container").html(partialResult); }
But the problem here is we don't know if you are getting a "success" or "validation failure". Adding an error: function(err){ }
handler won't help because the validation failure is considered a HTTP 200 response. In both cases the div content is replaced the user will need to manually close the modal. There are ways to pass additional data to distinguish both conditions but that's another long answer post.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With