MVC3, Ajax and a modal form with client-side unobtrusive validation
I can't get the combination of things in the title to work together. I've read various questions but not found one that covers everything I'm trying to do.
I'm using jqModal for my modal window. I'm using Ajax.BeginForm. My models are decorated with [Required]
, [Display(...)]
etc.
This stuff is all being placed on a page using @Html.RenderAction(...)
Depending on whether I use OnBegin
or OnComplete
in the ajax call, I either get no client-side validation (the update just doesn't take), or the modal gets stuck in a state where the whole page is "modalled out" without a dialog in place. But actually what I want is for the modal to stay in place if there is either a client or server side validation error.
The site does not require any functionality to work without javascript being available/enabled.
head:
<link href="@Url.Content("~/Content/Site.css")" rel="stylesheet" type="text/css" />
<link href="@Url.Content("~/Content/jquery.cleditor.css")" rel="stylesheet" type="text/css" />
<link href="@Url.Content("~/Content/jqModal.css")" rel="stylesheet" type="text/css" />
<script type="text/javascript" src="@Url.Content("~/Scripts/jquery-1.6.1.min.js")"></script>
<script type="text/javascript" src="@Url.Content("~/Scripts/jquery.validate.min.js")"></script>
<script type="text/javascript" src="@Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")"></script>
<script type=开发者_Python百科"text/javascript" src="@Url.Content("~/Scripts/jquery.unobtrusive-ajax.min.js")"></script>
<script type="text/javascript" src="@Url.Content("~/Scripts/jquery.cleditor.min.js")"></script>
<script type="text/javascript" src="@Url.Content("~/Scripts/jquery.timeago.min.js")"></script>
<script type="text/javascript" src="@Url.Content("~/Scripts/jqModal.js")"></script>
<script type="text/javascript" src="@Url.Content("~/Scripts/functions.js")"></script>
The model:
public class ReplacedTextModel {
public ReplacedTextModel() { }
public ReplacedTextModel(int id, string text, string replaced) {
Id = id;
Text = text;
Replaced = replaced;
}
public int Id { get; set; }
[Required, Display(Description = "The text of the question", Prompt = "Please enter the question text."), UIHint("MultilineString")]
public string Text { get; set; }
public string Replaced { get; set; }
}
The editor template (this contains some extension methods but they're not relevant to the problem, so please ignore):
@model string
<div class="form-element">
@Html.Label("", new Dictionary<string, object> { { "title", Html.Description("") } })
@if (Html.IsRequired()) {
<span class="form-required">*</span>
}
<div class="form-control">
@Html.TextArea("", Model, new Dictionary<string, object> { { "title", Html.Prompt("") } })
<div class="form-validation">
@Html.ValidationMessage("")
</div>
</div>
</div>
The view (there may be many of these on a page, and they may be repeated, so I'm using the Guids for uniqueness):
@model ReplacedTextModel
@{
var id = Guid.NewGuid().ToString("N");
var windowId = "id_" + id + "_window";
var triggerId = "id_" + id + "_trigger";
var replaceId = "id_" + id + "_replace";
var functionId = "id_" + id + "_closeForm";
}
<div id="@replaceId">
@Html.Raw(Model.Replaced)
<span class="edit-mode"><a id="@triggerId" href="#">edit</a></span>
<div id="@windowId" class="jqmWindow" style="display: none">
@using (Ajax.BeginForm("Text", new AjaxOptions { UpdateTargetId = replaceId, OnBegin = functionId, HttpMethod = "POST" })) {
@Html.HiddenFor(x => x.Id)
@Html.EditorFor(x => x.Text)
<input type="submit" value="Save" />
}
<div><a href="#" class="jqmClose">Cancel</a></div>
</div>
<script type="text/javascript">
$(function () { $("#@windowId").jqm({ trigger: "#@triggerId" }); });
function @(functionId)() {
// THIS IS A PROBLEM
// if it's not called or is called on OnBegin then the modal doesn't close properly
// if OnComplete is used, the dialog disappears but the validation doesn't work
// in either case, validation failure on backend (try posting whitespace) doesn't show validation errors.
$("#@windowId").jqmHide();
};
</script>
</div>
The controller methods:
public ActionResult Text(int id) {
return PartialView("Text", ReplacedTextModel.Get(id));
}
[HttpPost]
public ActionResult Text(int id, string text) {
if (ModelState.IsValid) {
try {
if (string.IsNullOrWhiteSpace(text)) throw new ArgumentException("No text was provided for the question.", "text");
ReplacedTextModel.Update(id, text);
} catch (Exception ex) {
ModelState.AddModelError(ex);
}
}
return Text(id);
}
Sorry for the long post, but I've seen others mention that it's not a bad thing! Happy to post any additional code/info if requested. I may have missed something as I have copy/pasted from a few locations in the project.
Thanks in advance.
精彩评论