Since the beginning of my current project, I knew that the day would come when the legacy AjaxControlToolkit modals would no longer suffice and we would have to implement something with more control, speed, and scalability. I have been partial to jQuery UI, so I thought that the dialog option it offered was worth a go.
For anyone who has tried this with a form-based solution, I’m sure the first thing you noticed was that the content of the dialog was not being posted to the form. After some scouring, I found some ad hoc solutions, but for a page with many modals with varied purposes, this just wouldn’t work. So, into the jQuery UI code I went…
After beautifying and digging in a bit, I noticed a few things. First, the dialog is appended to the body upon initialization:
_init: function () {
this.originalTitle = this.element.attr("title");
var l = this,
m = this.options,
j = m.title || this.originalTitle || " ",
e = c.ui.dialog.getTitleId(this.element),
k = (this.uiDialog = c("<div/>")).appendTo(document.body).hide()...
}
Here’s the first spot (and the most important for this specific topic) where the dialog is being placed within the context of the body, rather than a submittable form. Before we just go and change this to hit the form in question, it’s a good idea to make it a bit more configurable for later use. Back into the code we go. A little further down, you’ll find the dialog object being extended with some default properties:
c.extend(c.ui.dialog, {
version: "1.7.2",
defaults: {
autoOpen: true,
bgiframe: false,
buttons: {},
closeOnEscape: true,
closeText: "close",
appendToForm: false,
dialogClass: "",
draggable: true,
hide: null,
height: "auto",
maxHeight: false,
maxWidth: false,
minHeight: 150,
minWidth: 150,
modal: false,
position: "center",
resizable: true,
show: null,
stack: true,
title: "",
width: 300,
zIndex: 1000
}
I went ahead and kept the default as false, because it’s quite often that I use these dialogs for quick, dynamic informational popups and I don’t need to be worried about setting a lot of properties.
So, back to the initialization – I added a line above the declaration of k, that grabs the element that you want to append the dialog to as to not disturb the flow of the subsequent code.
var z = this.options.appendToForm ? $('form:first') : document.body;
We’ll call this z, because the other variables are named in alphabetical order and I’d bet they won’t hit z anytime soon. Then, right below we appendTo the selected element.
k = (this.uiDialog = c("<div/>")).appendTo(z).hide()...
All right, we’re almost done. Be more patient. The last spot is in the open, where it’s a little trickier. I am not entirely sure of the intent of the following code, but we’ll talk through it and I’ll show you the changes I made.
open: function () {
if (this._isOpen) {
return
}
var e = this.options,
d = this.uiDialog;
this.overlay = e.modal ? new c.ui.dialog.overlay(this) : null;
(d.next().length && d.appendTo("body"));
Ok, this is confusing to me. The first portion of the highlighted section utilizes the jQuery.next() function, which provides you access to the next sibling in the list. So, what this says is that if a sibling exists (length isn’t 0), append the dialog to the body. In my opinion, the question you want to ask is “is this already appended to the body?” If the answer is no, then go ahead and append it.
Note: In case you aren’t comfortable with this particular nuance of JavaScript, the logical and (&&) is evaluated one side at a time. If the first side is false, the engine will not evaluate the second section. This is a compact way to perform if logic that is completely compatible with minification.
So, I changed the code to read the following:
var z = e.appendToForm ? "form:first" : "body";
(d.parent().length === 0 && d.appendTo(z));
In other words, if the dialog has no parent (it isn’t registered in the DOM at all), then append it to the container of your choice. It seems like this situation will never arise, since the initialization includes the DOM registration code. However, I decided to keep this around because in a future enhancement I will be removing the initialization code and push the content to the form on-demand. Being constrained by the basic principles of WebForms, I want to keep my form as lightweight as possible, so on some of the more complex pages it makes sense to only push data around as it is required when posting back.
Just create and consume the dialog as normal and include { appendToForm: true } to your options and you are off and posting in no time. For your convenience, I’ve attached a modified version of jQueryUI that already has the code in it. Note that this is without warranty and you’re using these enhancements at your own risk.
Note: If anyone cares to add some context to the d.next() situation, please post to the comments.
Attachment: jquery-ui-1.7.2.formdialog.min.js
d.next() thing might be to see if it would be economical to dynamically add some inputs to form and return the values that way. However, I’m not sure what that would mean, server-side.
Just did some research. What it would mean server-side is nothing. Javascript can’t do jack squat with a POST, except to pass event args to __doPostBack(). Frustrating.
Now I see why this solution works *AND* why it is necessary…
Another idea might be to add a chum-load of dynamically created s to mirror the inputs in the dialog. Having to go through all that when a simple hack of jQueryUI is adequate seems futile, though.
*dynamically created <asp:HiddenField>’s …
Last, here’s the human-readable source (none of the single-letter identifier compression):
http://code.google.com/p/jquery-ui/source/browse/tags/1.7.2/ui/ui.dialog.js?r=4039#221
Moar
Another idea might be to add a chum-load of dynamically created s to mirror the inputs in the dialog. Having to go through all that when a simple hack of jQueryUI is adequate seems futile, though.
+1