Reputation: 1836
I've been searching for a way to update data using a modal pop-up. Right now I'm using devexpress, because we're already using other devexpress controls (but this could change if a jquery library would be easier!!)
I'm stuck with the validation aspect. Frankly, the entire process seems quite hard for what I'm trying to achieve.
Anyways, let me describe the process that I've currently worked out:
-The Index page contains an overview of the different elements that can be updated. Using a HtmlExtension, I was able to create a devexpress popup which loads the edit page when you open the popup. => @Html.PopupControl().WithText("Edit").PopupGoesTo(Url.Action("EditPopup", etc etc)
-The edit page -which is just a partial view- works just fine. I've created a little test page, which contains 1 textbox, which takes in a decimal.
I want to submit the form using ajax (because frankly, I have no idea how I can show the validation if I do a full post back, since I need to be able to create the popup and bind the data to it AND trigger the validation errors).
<script type="text/javascript">
function EndPopUpUpdate(message) {
if (message.url) {
window.locatin.href = url;
}
$("#submitButtonPopUp, #loadingPopUp").toggle();
}
function BeginPopUpUpdate() {
$("#submitButtonPopUp, #loadingPopUp").toggle();
}
</script>
using (Ajax.BeginForm("Edit", "xxx", new AjaxOptions { UpdateTargetId = "PopUpDiv", HttpMethod = "Post", OnBegin = "BeginPopUpUpdate", OnComplete = "EndPopUpUpdate"}, new { id = "PopUpForm" }))
{
<div id="PopUpDiv">
@Html.Partial("EditPopup", new xxxViewModel())
</div>
}
I was able to achieve validation when I postback by manually rehooking the jquery events (because these don't get hooked since the page gets loaded dynamicly)
function ReconnectValidation() {
$("#PopUpForm").ready(function () {
$.validator.unobtrusive.parse("#PopUpForm");
});
$("#submitButton").click(function (e) {
var form = $("#PopUpForm");
if (!form.valid()) {
e.preventDefault();
}
});
}
So this handles my client side validation, which works.
Now, my actual problem! Server side validation.
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit([ModelBinder(typeof(CommandModelBinder))] UpdateCommand command)
{
if (!ModelState.IsValid)
{
//using the command pattern
var handlerResult = HandlerLocator.GetQueryHandler<IGetOverviewHandler>().Execute(..);
return PartialView("EditPopUp", handlerResult.ViewModel);
}
HandlerLocator.GetCommandHandler<UpdateCommand>().Handle(command);
var returnLink = Url.Action("Index", new {..});
return Json(new { url = returnLink }, JsonRequestBehavior.AllowGet);
}
I've written a CustomModelBinder, which does nothing but look for the properties in my command object (my return model if you will) and looks in the formcollection if it can find a matching object with the same name. Then it tries to convert it and it binds a ModelError to my ModelState if it fails.
So, now we have a ModelState which is either valid or is invalid. If it's valid, I want to redirect to the Index (so my overview can update). I've read that I should handle this in the client side, because the ajax.BeginForm is going to replace the "PopUpDiv"-div with the result (which just creates the same page within my page).
Here is the onComplete event:
function EndPopUpUpdate(message) {
if (message.url) {
window.locatin.href = url;
}
$("#submitButtonPopUp, #loadingPopUp").toggle();
}
The problem is, that I don't receive a json message, but I receive a PartialView. This means I can't access the message.url..because that's not what I recieve :/
So that is problemo number 1
If the object is not valid, I want to return a partialview with the model and give an error to the user. When I return the partialview, it just replaces the current view but it doesn't show any validation errors..
That is problem number 2 :)
Also, if you know a better way to solve this problem, please don't hesitate to respond (because this method just seems really convoluted for what it does -or should do-)
Sorry for the lengthy post, but I hope everything is clear.
Thanks for your help & time!
Upvotes: 2
Views: 2459
Reputation: 4042
I've used the dialog plugin from jQuery UI () before, which I've found works well. I normally get the links to open up in an iframe within the popup, which avoids the problem you describe where the jQuery validation events don't get hooked up because the page gets loaded dynamically - both client side and server side validation should work as normal, just within this iframe.
The nice thing about this technique is that you can generate action links as normal within your index page, just add a class of popup to them e.g.
@Html.ActionLink("Edit", "Edit", new { id = Model.Id }, new { @class = "popup" })
Then, you can get these links to open in a dialog iframe with jQuery like:
$("a.popup").click(function(e) {
e.preventDefault();
$("<iframe />").attr("src", $(this).attr("href") + "?popup=true").dialog({ show: "fadeIn", modal: true, width: 300, height: 300});
});
This basically looks for popup links, cancels the default behaviour (page navigation) and opens that URL in a iframe within a popup, adding a querystring to identify that the page is within a popup. The reason for this querystring and knowing its a popup allows you to load a different layout page within the view, maybe through an action filter e.g.:
public class Popup : ActionFilterAttribute
{
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
if (filterContext.Result != null
&& filterContext.Result is ViewResult
&& filterContext.RequestContext.HttpContext.Request["popup"] == "true")
(filterContext.Result as ViewResult).MasterName = "~/Views/Shared/_PopupLayout.cshtml";
}
}
This means you can easily apply this attribute to classes where you want action methods to apply. This method also means if you change your mind about the implementation in the future (ie removing the popups) then you can easily remove the jQuery that cancels the clicks and your app will continue to function as a normal MVC app with separate pages, and all the navigation/validation etc will 'just work'.
I hope that all makes sense and helps.
Upvotes: 4