2009 Nov 12 @ 11:53pm asp.net-mvc javascript jquery

Replacing MicrosoftMvcAjax.js

Update: I’ve written a follow-up, Even Smaller.

I’m a big fan of ASP.NET MVC. However, it requires a couple of bulky Javascript files to do its magic.

  • MicrosoftMvcAjax.js weighs in at 4,870 bytes.
  • It however utilizes a few functions from MicrosoftAjax.js, which is a whopping 99,358 bytes.

And those are the minified and obfuscated copies. Maybe I’m just jaded by ASP.NET WebForms, but I don’t entirely trust 100+ kilobytes of Microsoft Javascript to be included in every one of my pages.

A little research led me to Chris van de Steeg’s post about creating smaller methods that adhered to the same interface as MicrosoftMvcAjax.js’s methods so that they were still compatible with the AjaxHelper class in your Views.

The only point of this seemed to be to retain use of the AjaxHelper.BeginForm() method, which I can do without as it’s merely a convenience method to write an HTML form element.

Using the following call in your View:

using (Ajax.BeginForm("Create", "Widget", new AjaxOptions { OnSuccess = "onCreateWidgetSuccess" }) { }

…results in the following form element:

<form action="/Widget/Create" method="post" onsubmit="Sys.Mvc.AsyncForm.handleSubmit(this, new Sys.UI.DomEvent(event), { insertionMode: Sys.Mvc.InsertionMode.replace, onSuccess: Function.createDelegate(this, onCreateWidgetSuccess) });"></form>

Which relies on the Sys.Mvc namespace implemented by MicrosoftMvcAjax.js.

I decided to scrap the whole use of AjaxHelper, and created the following Javascript namespace and methods for simple Ajax form and link functionality to replace MicrosoftMvcAjax.js.

mvcAjax = {};

mvcAjax.submit = function(form, event, options)
{
	event = mvcAjax.fix(event);

	if (event)
		event.preventDefault();
	
	mvcAjax.request
	(
		$.extend
		(
			options,
			{
				url: $(form).attr("action"),
				data: $(form).serialize()
			}
		)
	);
};

mvcAjax.click = function(anchor, event, options)
{
	event = mvcAjax.fix(event);

	if (event)
		event.preventDefault();

	mvcAjax.request
	(
		$.extend
		(
			options,
			{
				url: $(anchor).attr("href"),
				data: {}
			}
		)
	);
}

mvcAjax.request = function(options)
{
	if (!options || !options.url)
		return;

	$.ajax
	(
		{
			type: "POST",
			url: options.url,
			data: options.data,
			success: function(data)
			{
				if (options.onSuccess)
					options.onSuccess(data);
			},
			error: function(request, status)
			{
				if (options.onError)
					options.onError(status);
			},
			complete: function(request, status)
			{
				if (options.onComplete)
					options.onComplete(status);
			},
			dataType: options.json ? "json" : undefined
		}
	);
};

mvcAjax.fix = function(event)
{
	if (!event)
		event = window.event;

	return event ? $.event.fix(event) : undefined;
};

Using it looks similar to the form element created by Ajax.Begin():

<form action="/Widget/Create" method="post" onsubmit="mvcAjax.submit(this, event, { onSuccess: onCreateWidgetSuccess, json: true })"></form>

It supports onSuccess, onError, and onComplete parameters that correspond to AjaxOption’s OnSuccess, OnFailure, and OnComplete properties.

It has the added feature of a json parameter that, when set to true, tells the underlying Ajax call to expect a JSON object to be returned.

Using the link version looks similar:

<a href="/Widgets/Clear" onclick="mvcAjax.click(this, event, { onSuccess: onClearWidgetsSuccess, json: true })">Clear Widgets</a>

The obfuscated and minified version below is a lean 668 bytes, 0.64% of the 104,228 byte combined size of the replaced files.

mvcAjax={};mvcAjax.submit=function(f,e,o){e=mvcAjax._f(e);if(e)e.preventDefault();mvcAjax._r($.extend(o,{u:$(f).attr("action"),d:$(f).serialize()}));};mvcAjax.click=function(a,e,o){e=mvcAjax._f(e);if(e)e.preventDefault();mvcAjax._r($.extend(o,{u:$(a).attr("href"),d:{}}));};mvcAjax._r=function(o){if(!o||!o.u)return;$.ajax({type:"POST",url:o.u,data:o.d,success:function(d){if(o.onSuccess)o.onSuccess(d);},error:function(r,s){if(o.onError)o.onError(s);},complete:function(r,s){if(o.onComplete)o.onComplete(s);},dataType:o.json?"json":undefined});};mvcAjax._f=function(e){if(!e)e=window.event;return e?$.event.fix(e):undefined;};