Reputation: 1977
I followed Darin Dimitrov's example to submit a form (with validation) in a modal dialog:
Using Ajax.BeginForm with ASP.NET MVC 3 Razor
It works perfectly with on exception. When I submit the form with intentional errors, I end up with two copies of the form in the dialog:
Here is my partial view:
@model MvcAppTemplate.ViewModels.SupportClass1ViewModel
<script src="~/Scripts/Jquery/jquery.validate.min.js"></script>
<script src="~/Scripts/Jquery/jquery.validate.unobtrusive.js"></script>
<script>
$(document).ready(function () {
$('#SupportClass1Name').focus();
$('form').submit(function () {
if ($(this).valid()) {
$.ajax({
url: this.action,
type: this.method,
data: $(this).serialize(),
success: function (result) {
$('#result').html(result);
}
});
}
return false;
});
});
</script>
<div id="result"></div>
@using (Html.BeginForm("CreateDialog", "SupportClass1", FormMethod.Post, new { @class = "form-horizontal" }))
{
@Html.AntiForgeryToken()
@Html.ValidationSummary(true, "The following errors occurred:", new { style = "color: red" })
<fieldset>
<legend>MyMainClass1</legend>
@Html.ValidationMessage("CustomError", new { @class = "error" })
@Html.HiddenFor(model => model.IsNew)
<div class="form-group">
<div class="col-lg-3 control-label">
@Html.LabelFor(model => model.SupportClass1Name)
</div>
<div class="col-lg-6">
@Html.TextBoxFor(model => model.SupportClass1Name, new { style = "width: 400px;", @maxlength = "50" })
@Html.ValidationMessageFor(model => model.SupportClass1Name)
</div>
</div>
<div class="form-group">
<div class="col-lg-3 control-label">
@Html.LabelFor(model => model.Active)
</div>
<div class="col-lg-6">
@Html.EditorFor(model => model.Active, new { style = "width: 150px;" })
@Html.ValidationMessageFor(model => model.Active)
</div>
</div>
<p>
<input type="submit" value="Create" class="btn btn-primary" />
@Html.ActionLink("Cancel", "Search", "SupportClass1", null, new { @class = "btn btn-default" })
</p>
</fieldset>
}
The view I call the modal from:
@model MvcAppTemplate.ViewModels.SupportClass1ViewModel
@{
ViewBag.Title = "Test";
}
<link href="~/Scripts/jquery-ui-1.11.1.custom/jquery-ui.min.css" rel="stylesheet" />
<link href="~/Content/dataTables.bootstrap.css" rel="stylesheet" />
<script src="~/Scripts/jquery-ui-1.11.1.custom/jquery-ui.min.js"></script>
<script type="text/javascript">
$(document).ready(function () {
$('#dialog').dialog({
autoOpen: false,
width: 600,
height: 400,
resizable: true,
title: 'Support Class 1',
modal: true,
open: function (event, ui) {
$(this).load("@Url.Action("CreateDialog", "SupportClass1")");
},
buttons: {
"Close": function () {
$(this).dialog("close");
}
}
});
$("#opener").click(function () {
$("#dialog").dialog("open");
});
});
</script>
<div id="dialog" title="Create" >Please wait</div>
<button id="opener">Show Class</button>
And finally my controller:
// create in a pop up dialog
public ActionResult CreateDialog()
{
var lvm = new SupportClass1ViewModel
{
IsNew = true,
};
return PartialView("_CreateDialog",lvm);
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult CreateDialog(SupportClass1ViewModel lvm)
{
SupportClass1 supportClass1 = new SupportClass1();
// Use Injector to handle mapping between viewmodel and model
supportClass1.InjectFrom(lvm);
try
{
if (ModelState.IsValid)
{
supportClass1Service.CreateSupportClass1(supportClass1);
// redirect to the myMainClass1 view
//return RedirectToAction("Details", "SupportClass1", new { id = supportClass1.SupportClass1Id });
return Content("Thanks", "text/html");
}
}
catch (DataException de)
{
//Log the error (add a variable name after DataException)
var s = de.InnerException.ToString();
ModelState.AddModelError("CustomError", "Unable to save changes. Try again, and if the problem persists, see your system administrator. Error: " + s);
}
// rehydrate the view
lvm.IsNew = true;
//return Content("Thanks", "text/html");
return PartialView("_CreateDialog", lvm);
It appears the partial view is loaded twice when there is an error: once in the result div and once from the original @using Html.BeginForm(). I verified this by putting a visible border around the result div.
Any help is appreciated.
Upvotes: 0
Views: 7859
Reputation: 1977
I figured out a fix. As I said in my comment, I wrapped my partial view's form in a div:
<div id="myForm">
@using (Html.BeginForm("CreateDialog", "SupportClass1", FormMethod.Post, new { @class = "form-horizontal" }))
{
some content...
}
</div>
Then, in my jquery form submit function, I cleared the div before repopulating it with the partial view form the controller (the one with the validation errors:
$('form').submit(function () {
if ($(this).valid()) {
$.ajax({
url: this.action,
type: this.method,
data: $(this).serialize(),
success: function (result) {
$('#myForm').html('');
$('#result').html(result);
}
});
}
return false;
});
Upvotes: 2
Reputation: 7172
this line is the issue:
success: function (result) {
$('#result').html(result);
}
This handler is being called in both success and fail scenarios, so you end up with the form displayed twice, once from your original render, and then inside the result div when error happens.
Change your controller code to:
try
{
if (ModelState.IsValid)
{
supportClass1Service.CreateSupportClass1(supportClass1);
// redirect to the myMainClass1 view
//return RedirectToAction("Details", "SupportClass1", new { id = supportClass1.SupportClass1Id });
return Json(new {success = true, message ="Thanks" } );
}
}
catch (DataException de)
{
//Log the error (add a variable name after DataException)
var s = de.InnerException.ToString();
ModelState.AddModelError("CustomError", "Unable to save changes. Try again, and if the problem persists, see your system administrator. Error: " + s);
}
return Json(new {success = "false", message = "Error"}); // do a concatenation of the model state errors
Then your success handler can look like
success: function (result) {
if(result.success) {
$('#result').html(result.message);
}
else {
// provide some highlighting or what have you or just set the message
$('#result').addClass("error").html(result.message);
}
}
Upvotes: 0