Victor Walch Michnowicz

is a web application developer

Determining AJAX Success and Failure States in ExpressionEngine Modules April 21st, 2011

The Situation

So here is the deal—we have some data that we need to POST to an ExpressionEngine module. We are going to be using jQuery, and we are expecting back some JSON data. Once we get our JSON back we are going to show a success message if there were no errors, or list off all errors if our POST was unsuccessful.

There are a few ways to do this. One way is to put the success or faliure data in the JSON itself. This would result in some JSON like this for success:

{"result": "success", "entry_id": 3, "title": "My Title"}

Or like this for failure:

{"result": "failure", "errors": ["No", "Way", "Bro"]}

And then in our jQuery callback function we could check to see if everything went well like this:

if (data.result == 'success') {
	alert('Great Success! Our entry ID is ' + data.entry_id + ' and our title is ' + data.title);
}
else {
	$.each(data.errors, function(index, value) {
		alert(value);
	});
}

While this works, there is a cooler way to go about this.

The Cool (More Complicated) Way

The cool way is to send custom HTTP status headers along with your JSON. If everything goes well, your HTTP status header will be the typical 200 OK. However, if you encountered some errors, you send back a 500 Internal Server Error.

ExpressionEngine makes this super easy with its send_ajax_response method.

$this->EE->output->send_ajax_response(array('insert_id' => 3, 'title' => 'My Title')); // Success!
$this->EE->output->send_ajax_response(array('errors' => array('No', 'Way', 'Bro'), TRUE); // Failure!

The second parameter of that method dictates if the 500 status header should be sent.

The Coolness

The coolness comes from the CodeIgniter set_status_header() function. This can be found in the CodeIgniter system/core/Common.php file.

This function sets the HTTP header for you. While you can probably just set the header yourself, CodeIgniter does a little work in the background to make sure it is totally legit.

So, Now That We Got HTTP Status Headers…

Now that HTTP status headers tell us whether or not our POST was successful, we have to capture this information with jQuery. Lets have a look at our POST function:

$.ajax({
	type: 'POST',
	url: 'index.php',
	data: $(this).serialize(),
	success: function(data) { // 200 Status Header
		alert('Great Success! Our entry ID is ' + data.entry_id + ' and our title is ' + data.title);
	},
	error: function(data) { // 500 Status Header
		var data = $.parseJSON(data);
		$.each(data.errors, function(index, value) {
			alert(value);
		});
	},
	dataType: 'json'
});

Break it Down!

The first thing to note is that we are using jQuery's .ajax() function. The reason we are not using .post() is because .post() will only return data on a successful AJAX request. Our 500 header will make it so we can not get our JSON in case of an error. The .ajax() function, on the other hand, can return data in both error and success states.

If a request with jQuery.post() returns an error code, it will fail silently unless the script has also called the global .ajaxError() method or. As of jQuery 1.5, the .error() method of the jqXHR object returned by jQuery.post() is also available for error handling.

—The jQuery Manual

Success

The success function accepts three parameters—"data", "textStatus", and "jqXHR." Since our dataType is JSON, our data will be parsed and a JavaScript object will be returned. With this we can easily return our JSON data:

alert('Great Success! Our entry ID is ' + data.entry_id + ' and our title is ' + data.title);

Failure

If a 500 error is sent that the error function will run. This function accepts three parameters—"jqXHR", "textStatus", "errorThrown." Notice that no parsed data is returned. This means that our JSON string will not be parsed. Because of this, we will have to parse it ourselves.

var data = $.parseJSON(data);

And after we parse the JSON, we can easily loop through all of our errors:

$.each(data.errors, function(index, value) {
	alert(value);
});

blog comments powered by Disqus