Never been to CodeSnippets before?

Snippets is a public source code repository. Easily build up your personal collection of code snippets, categorize them with tags / keywords, and share them with the world (or not, you can keep them private!)

Displaying ActiveRecord validation errors from AJAX requests

I haven’t been able to find anything built into Rails to present errors from model validation when a request is sent via AJAX.

So I came up with this handly little hack, which I’ve put in my application.rb file.

def render_javascript_alert_for_errors_on(object)
  errors = object.errors.full_messages
  alert_text = errors.collect { |error| '-' + error }.join("\n")
  render :update do |page|
    page.alert alert_text
  end
end


This takes the object and displays a nicely-formatted version of all the errors on that object, taking advantage of inline RJS to render the javascript to create an alert().

It works perfectly, but it still feels a little “hacky” to me. Any better ideas?

Initialize XMLHttpRequest/ActiveXObject for use with AJAX

xmlHttp = false;
		
try {
	xmlHttp = new XMLHttpRequest();
} catch(Microsoft1) {
	try {
		xmlHttp = new ActiveXObject("Microsoft.XMLHTTP");
	} catch(failed) {
		xmlHttp = false;
	}
}
		
if(!xmlHttp) {
	alert("Error: Could Not Initialize XMLHttpRequest()!\r\n\r\nJavaScript must be enabled to view this page.");
}

Initialize XMLHttpRequest/ActiveXObject for use with AJAX

xmlHttp = false;
		
try {
	xmlHttp = new XMLHttpRequest();
} catch(Microsoft1) {
	try {
		xmlHttp = new ActiveXObject("Microsoft.XMLHTTP");
	} catch(failed) {
		xmlHttp = false;
	}
}
		
if(!xmlHttp) {
	alert("Error: Could Not Initialize XMLHttpRequest()!\r\n\r\nJavaScript must be enabled to view this page.");
}

A Caching XmlHttpRequest Wrapper



/* 
	XmlHttpRequest Wrapper
	Version 1.2.2
	29 Jul 2005 
	adamv.com/dev/
*/

var Http = {
	ReadyState: {
		Uninitialized: 0,
		Loading: 1,
		Loaded:2,
		Interactive:3,
		Complete: 4
	},
		
	Status: {
		OK: 200,
		
		Created: 201,
		Accepted: 202,
		NoContent: 204,
		
		BadRequest: 400,
		Forbidden: 403,
		NotFound: 404,
		Gone: 410,
		
		ServerError: 500
	},
		
	Cache: {
		Get: 1,
		GetCache: 2,
		GetNoCache: 3,
		FromCache: 4
	},
	
	Method: {Get: "GET", Post: "POST", Put: "PUT", Delete: "DELETE"},
	
	enabled: false,
	logging: false,
	_get: null,	// Reference to the XmlHttpRequest object
	_cache: new Object(),
	
	Init: function(){
		Http._get = Http._getXmlHttp()
		Http.enabled = (Http._get != null)
		Http.logging = (window.Logging != null);
	},
	
	_getXmlHttp: function(){
	/*@cc_on @*//*@if (@_jscript_version >= 5)
		try { return new ActiveXObject("Msxml2.XMLHTTP"); } 
		catch (e) {} 
		try { return new ActiveXObject("Microsoft.XMLHTTP"); } 
		catch (e) {} 
	@end @*/
		try { return new XMLHttpRequest();}
		catch (e) {}

		return null;
	},

/*
	Params:
		url: The URL to request. Required.
		cache: Cache control. Defaults to Cache.Get.
		callback: onreadystatechange function, called when request is completed. Optional.
		method: HTTP method. Defaults to Method.Get.
*/
	get: function(params, callback_args){	
		if (!Http.enabled) throw "Http: XmlHttpRequest not available.";
		
		var url = params.url;
		if (!url) throw "Http: A URL must be specified";
				
		var cache = params.cache || Http.Cache.Get;
		var method = params.method || Http.Method.Get;
		var callback = params.callback;
		
		if ((cache == Http.Cache.FromCache) || (cache == Http.Cache.GetCache))
		{
			var in_cache = Http.from_cache(url, callback, callback_args)

			if (Http.logging){
				Logging.log(["Http: URL in cache: " + in_cache]);
			}

			if (in_cache || (cache == Http.Cache.FromCache)) return in_cache;
		}
		
		if (cache == Http.Cache.GetNoCache)
		{
			var sep = (-1 < url.indexOf("?")) ? "&" : "?"	
			url = url + sep + "__=" + encodeURIComponent((new Date()).getTime());
		}
	
		// Only one request at a time, please
		if ((Http._get.readyState != Http.ReadyState.Uninitialized) && 
			(Http._get.readyState != Http.ReadyState.Complete)){
			this._get.abort();
			
			if (Http.logging){
				Logging.log(["Http: Aborted request in progress."]);
			}
		}
		
		Http._get.open(method, url, true);

		Http._get.onreadystatechange =  function() {
			if (Http._get.readyState != Http.ReadyState.Complete) return;
			
			if (Http.logging){
				Logging.log(["Http: Returned, status: " + Http._get.status]);
			}

			if ((cache == Http.Cache.GetCache) && (Http._get.status == Http.Status.OK)){
				Http._cache[url] = Http._get.responseText;
			}
			
			if (callback_args == null) callback_args = new Array();

			var cb_params = new Array();
			cb_params.push(Http._get);
			for(var i=0;i<callback_args.length;i++)
				cb_params.push(callback_args[i]);
				
			callback.apply(null, cb_params);
		}
		
		if(Http.logging){
			Logging.log(["Http: Started\n\tURL: " + url + "\n\tMethod: " + method + "; Cache: " + Hash.keyName(Http.Cache,cache)])
		}
		
		Http._get.send(params.body || null);
	},
	
	from_cache: function(url, callback, callback_args){
		var result = Http._cache[url];
		
		if (result != null) {
			var response = new Http.CachedResponse(result)
			
			var cb_params = new Array();
			cb_params.push(response);
			for(var i=0;i<callback_args.length;i++)
				cb_params.push(callback_args[i]);
							
			callback.apply(null, cb_params);
				
			return true
		}
		else
			return false
	},
	
	clear_cache: function(){
		Http._cache = new Object();
	},
	
	is_cached: function(url){
		return Http._cache[url]!=null;
	},
	
	CachedResponse: function(response) {
		this.readyState = Http.ReadyState.Complete
		this.status = Http.Status.OK
		this.responseText = response
	}	
}

Http.Init()

function json_response(response){
	var js = response.responseText;
	try{
		return eval(js); 
	} catch(e){
		if (Http.logging){
			Logging.logError(["json_response: " + e]);
		}
		else{
			alert("Error: " + e + "\n" + js);
		}
		return null;
	}
}

function getResponseProps(response, header){
	try {
		var s = response.getResponseHeader(header || 'X-Ajax-Props');
		if (s==null || s=="")
			return new Object()
		else
			return eval("o="+s)
	} catch (e) { return new Object() }
}



Caching Options
These options only make sense to use with the GET method. POST operations should not be cached.

Http.Cache.Get
Perform a normal request, and do not add the result to the local cache. Note: on IE, requesting the same URL via GET multiple times will cause IE to cache the result internally.
Http.Cache.GetCache
Perform a request and cache the result if sucessful. If the requested URL's response is already cached locally, do not perform the server request.
Http.Cache.GetNoCache
Perform a request and add a time-based variable to the querystring to force IE not to cache the result. The result is not placed in the local cache.
Http.Cache.FromCache
If the requested URL's response has already been cached, call the supplied callback on the locally cached version. Http.get will return true or false based on whether or not the response is in the cache.

AJAX Edit In Place With Prototype

// description of your code here

JS Source
/*
 * Edit In Place
 * http://josephscott.org/code/js/eip/
 *
 * Version: 0.2.0
 * License: http://josephscott.org/code/js/eip/license.txt
 */
EditInPlace = function() { };
EditInPlace.settings = function(set) {
	var settings = {
		id:				false,
		save_url:		false,
		css_class:		'eip_editable',
		savebutton:		'eip_savebutton',
		cancelbutton:	'eip_cancelbutton',
		saving:			'eip_saving',
		type:			'text',
		orig_text:		false
	};

	for(var i in set) { settings[i] = set[i]; }

	return($H(settings));
};

EditInPlace.formField = function(set) {
	var field = '';
	set['orig_text'] = $(set['id']).innerHTML;

	if(set['type'] == 'text') {
		var size = set['orig_text'].length + 10;
		if(size >= 100) { size = 100; }
		if(set['size']) { size = set['size']; }

		field = '<span id="' + set['id'] + '_editor"><input id="'
			+ set['id'] + '_edit" class="' + set['css_class'] + '" name="'
			+ set['id'] + '_edit" type="text" size="' + size
			+ '" value="' + set['orig_text'] + '" />';
	}
	else if(set['type'] == 'textarea') {
		var cols = 50;
		if(set['cols']) { cols = set['cols']; }
		var rows = (set['orig_text'].length / cols) + 3;
		if(set['rows']) { rows = set['rows']; }

		field = '<span id="' + set['id'] + '_editor"><textarea id="'
			+ set['id'] + '_edit" class="' + set['css_class'] + '" name="'
			+ set['id'] + '_edit" rows="' + rows + '" cols="'
			+ cols + '">' + set['orig_text'] + '</textarea>';
	}

	return(field);
};

EditInPlace.formButtons = function(set) {
	return(
		'<br /><span><input id="' + set['id'] + '_save" class="'
		+ set['savebutton'] + '" type="button" value="SAVE" /> OR '
		+ '<input id="' + set['id'] + '_cancel" class="' 
		+ set['cancelbutton'] + '" type="button" value="CANCEL" />'
		+ '</span></span>'
	);
};

EditInPlace.setEvents = function(set) {
	Event.observe(
		set['id'],
		'mouseover',
		function() { Element.addClassName(set['id'], set['css_class']); },
		false
	);
	Event.observe(
		set['id'],
		'mouseout',
		function() { Element.removeClassName(set['id'], set['css_class']); },
		false
	);
	Event.observe(
		set['id'],
		'click',
		function() {
			Element.hide(set['id']);

			var field = EditInPlace.formField(set);
			var button = EditInPlace.formButtons(set);

			new Insertion.After(set['id'], field + button);
			Field.focus(set['id'] + '_edit');

			Event.observe(
				set['id'] + '_save',
				'click',
				function() { EditInPlace.saveChanges(set); },
				false
			);
			Event.observe(
				set['id'] + '_cancel',
				'click',
				function() { EditInPlace.cancelChanges(set); },
				false
			);
		},
		false
	);
};

EditInPlace.saveComplete = function(t, set) {
	$(set['id']).innerHTML = t.responseText;
};

EditInPlace.saveFailed = function(t, set) {
	$(set['id']).innerHTML = set['orig_text'];
	Element.removeClassName(set['id'], set['css_class']);
	alert('Failed to save changes.');
};

EditInPlace.saveChanges = function(set) {
	var new_text = escape($F(set['id'] + '_edit'));
	$(set['id']).innerHTML = 
		'<span class="' + set['saving'] + '">Saving ...</span>';

	Element.remove(set['id'] + '_editor');
	Element.show(set['id']);

	var params = 'id=' + set['id'] + '&content=' + new_text;
	var ajax_req = new Ajax.Request(
		set['save_url'],
		{
			method: 'post',
			postBody: params,
			onSuccess: function(t) { EditInPlace.saveComplete(t, set); },
			onFailure: function(t) { EditInPlace.saveFailed(t, set); }
		}
	);
};

EditInPlace.cancelChanges = function(set) {
	Element.remove(set['id'] + '_editor');
	Element.removeClassName(set['id'], set['css_class']);
	Element.show(set['id']);
}

EditInPlace.makeEditable = function(args) {
	EditInPlace.setEvents(EditInPlace.settings(args));
}



HTML source
<html>
<head><title>Ajax Edit In Place (EIP) Example</title>
<style type="text/css">
	.eip_editable { background-color: #ff9; padding: 3px; }
	.eip_savebutton { background-color: #36f; color: #fff; }
	.eip_cancelbutton { background-color: #000; color: #fff; }
	.eip_saving { background-color: #903; color: #fff; padding: 3px; }
</style>
<script type="text/javascript" src="prototype.js"></script>
<script type="text/javascript" src="EditInPlace.js"></script>
<script type="text/javascript">
Event.observe(window, 'load', init, false);
function init() {
	EditInPlace.makeEditable( {
		type: 'text',
		id: 'editme',
		save_url: 'edit.php'
	} );
	EditInPlace.makeEditable( {
		type: 'textarea',
		id: 'anotheredit',
		save_url: 'edit.php'
	} );
}
</script>
</head>
<body>

<h1><a href="example.html">Ajax Edit In Place (EIP) Example</a></h1>
<hr />

<span id="editme">AJAX edit in place like flickr.</span>

<br />
<br />

<span id="anotheredit">If you want room for more text you can use a textarea to edit instead.</span>

</body>
</html>




PHP source
<?php
	$id = $_POST["id"];
	$content = $_POST["content"];
	print(htmlspecialchars($content));
?>

Ajax Request Object Constructor

Wrapper function for constructing a request object.
Parameters:
reqType: The HTTP request type, such as GET or POST.
url: The URL of the server program.
asynch: Whether to send the request asynchronously or not.

function httpRequest(reqType,url,asynch) {

	// Mozilla-based browsers
	if (window.XMLHttpRequest) {
		request = new XMLHttpRequest();
	} else if (window.ActiveXObject) {
		request = new ActiveXObject("Msxml2.XMLHTTP");
		if (!request) {
			request = new ActiveXObject("Microsoft.XMLHTTP");
		}
	}
	
	// Request could still be null if neither ActiveXObject
	//   initialization succeeded
	if (request) {
		initReq(reqType,url,asynch);
	} else {
		alert("Your browser does not permit the use of all " +
			"of this application's features!");
	}

}


Initialize a request object that is already constructed.

function initReq(reqType,url,asynch) {
	// Specify the function that will handle the HTTP response
	request.onreadystatechange = handleResponse;
	request.open(reqType,url,bool);
	request.send(null);
}

Load external file into div

// javascript to go into head of document or external .js file

function ahah(url, target) {
  document.getElementById(target).innerHTML = ' Fetching data...';
  if (window.XMLHttpRequest) {
    req = new XMLHttpRequest();
  } else if (window.ActiveXObject) {
    req = new ActiveXObject("Microsoft.XMLHTTP");
  }
  if (req != undefined) {
    req.onreadystatechange = function() {ahahDone(url, target);};
    req.open("GET", url, true);
    req.send("");
  }
}  

function ahahDone(url, target) {
  if (req.readyState == 4) { // only if req is "loaded"
    if (req.status == 200) { // only if "OK"
      document.getElementById(target).innerHTML = req.responseText;
    } else {
      document.getElementById(target).innerHTML=" AHAH Error:\n"+ req.status + "\n" +req.statusText;
    }
  }
}

function load(name, div) {
	ahah(name,div);
	return false;
}


// call code with the name of the external file and the id of the div you want to put the content into

<a href="file1.html" onclick="load('file1.html','content');return false;">File 1</a>

Custom Ajax.Request Function

// this is a custom function that simplifies creating an ajax object

function(url, complete, form, params) {
  params = params ? $H(params).toQueryString() : '';
  if (!complete) complete = function(r){};
  if (form)      params   = Form.serialize(form) + '&' + params;
  new Ajax.Request(url, 
    { asynchronous:true,
      evalScripts:true, 
      onComplete: function(r) { complete(r.responseText); },
      parameters:params
    });
}

Increase the number of simultaneous XmlHttpRequests in Firefox

Firefox doesn't do a lot of simultaneous AJAX (or any kind of HTTP) requests. IE caps it at 2, too. This will allow you to test your XHR overload scripts, or just load pages faster in general.

1. Go to "about:config"
2. Find and edit the following
* network.http.pipelining=false
* network.http.pipelining.maxrequests=4
* network.http.proxy.pipeline=false
3. Make the false's true; I set my maxrequests at 20

Simple DIV Toggle Javascript

// description of your code here

function simpletogglediv(whichLayer)
{
	 var theElementStyle = document.getElementById(whichLayer); 

	if(theElementStyle.style.display == "block")
	{ 
		theElementStyle.style.display = "none";  
		 
	}
	else
	{ 
		theElementStyle.style.display = "block"; 
	}
}