ross dickinson: web and desktop software developer

Submitting a Nested Form in ASP MVC3

by Ross on February 3, 2012 at 7:06 PM under .NET | ASP.Net | MVC

In case you're using the jQuery unobtrusive ajax library with your ASP MVC3 site, you'll probably find that it can be difficult to submit a nested form in certain situations. The problem comes from this bit of code:

$("form[data-ajax=true]").live("submit", function (evt) {
    var clickInfo = $(this).data(data_click) || [];
    evt.preventDefault();
    if (!validate(this)) {
        return;
    }
    asyncRequest(this, {
        url: this.action,
        type: this.method || "GET",
        data: clickInfo.concat($(this).serializeArray())
    });
});

When your nested form's submit event gets raised, it bubbles up to the parent form as well. You can fix this in two ways. The easiest way is to modify the code above to look like this:

$("form[data-ajax=true]").live("submit", function (evt) {
    if (evt.currentTarget != evt.target) { return; }
    var clickInfo = $(this).data(data_click) || [];
    evt.preventDefault();
    if (!validate(this)) {
        return;
    }
    asyncRequest(this, {
        url: this.action,
        type: this.method || "GET",
        data: clickInfo.concat($(this).serializeArray())
    });
});

This stops the unobtrusive ajax library's handler from submitting the wrong form.

Another way is to add your own submit handler to your nested form that calls evt.stopPropagation(). This tells the event to stop bubbling up and calling other event handlers. This can be tricky, though, because you have to ensure that your handler is called before the unobtrusive handler.

Getting MVC3 to load scripts from a Partial View

by Ross on January 6, 2012 at 12:37 PM under ASP.Net | MVC | AJAX | jQuery

On an MVC3 project I'm working on, one of the things we do when allowing a user to edit a record s open up an ajax form inside a jQuery UI Dialog. This is done through a really simple setup of

  1. Call controller action.
  2. Controller action returns a PartialViewResult.
  3. Using some custom hooks into the jquery.unobtrusive-ajax library, render the partial view into a dialog.

Seems simple enough, right? This works well, except if the partial view includes script tags. By design, when using $.append, jQuery will remove all the script tags in the created element, then attempt to load them via ajax before appending the main content. This can be a problem if the script you're loading depends on elements inside the partial view's content.

Luckily, there's an easy way to get around this. When you do something like:

var newElement = $('<div><script src='somewhere'></script></div>');

jQuery doesn't return an array with one element, it actually returns with two elements!

// Assuming you have Firebug or Chrome, you can use console.debug:
console.debug(newElement.length); // This will equal 2
console.debug(newElement[0]; // This will be the main content with the script tags removed
console.debug(newElement[1]); // This will be the script tag.

So jQuery's actually nice enough to give us back everything all organized before anything happens. If you need to load the scripts after the elements have been added to the DOM, you need to manually append everything instead of just calling $.append(newElement). Here's how to do it:

// Create a parent container to hold our content. If
// you already have a parent attached to the DOM,
// you can use that instead.
var $parent = $('<div></div>');

// Create an array to hold on to our script references.
var scripts = [];

// Append any elements that aren't scripts,
$newElement.each(function() {
	var el = $(this);
	// tagName is always all caps.
	if (el.prop('tagName') === 'SCRIPT') {
		scripts.push(el);
	}
	else {
		d.append(el);
	}
});

// If your parent element isn't attached to the DOM yet, you need to do
// that now before adding the scripts, otherwise any scripts that reference
// elements from the html fragment will fail.

$(document).append($parent);

// Now we just need to append each script from our scripts array.
// jQuery does this synchronously so the scripts will still load
// in the same order they were originally in.
$(scripts).each(function(i, script) {
	d.append(script);
});

TIP: Because your new elements will be displayed before your scripts are loaded, you should consider hiding them before the scripts are appended, and then show them afterwards.

About the author

rossisdead is a 26 year old web and desktop software developer from New Jersey. He has two cats and likes long walks on the beach.

On Stackoverflow

On Stackoverflow Careers

On Codeplex

On Github