Skip to content
trezmann edited this page Aug 31, 2013 · 15 revisions

jQuery is one of the standard JavaScript Ajax Libraries. It ships with Ruby on Rails and will be used by us for various things including AJAX and cleaning up usability and user interfaces. There is a powerfull API Documentation of jQuery available: http://api.jquery.com/

AJAX

Link Detection

https://github.com/stephan-fischer/jQuery-LiveUrl/

AutoCompletion

http://www.jqueryrain.com/2012/03/35-best-ajax-jquery-autocomplete-tutorial-plugin-with-examples/

I'll try to show some of the functionality of AJAX by the means of our autocompletion. First of all I show the code as a whole, then I'll pick out the AJAX part and describe / explain it in greater detail.

 
<html lang="en">

<head>
  <meta charset="utf-8" />
  <title>jQuery UI Autocomplete - Default functionality</title>
  <link rel="stylesheet" href="http://code.jquery.com/ui/1.10.3/themes/smoothness/jquery-ui.css" />
  <script src="http://code.jquery.com/jquery-1.9.1.js"></script>
  <script src="http://code.jquery.com/ui/1.10.3/jquery-ui.js"></script>
<!--  <link rel="stylesheet" href="/resources/demos/style.css" />-->
	<script>
  	$(function() {
	    	$( "#tags" ).autocomplete({
		//http://jqueryui.com/autocomplete/#remote-jsonp
      		source: function( request, response ) {
        		$.ajax({
				url: "/autocompleteServer-0.0.1-SNAPSHOT/suggest",
				dataType: "json", 
				data: 	{
			        	numItems: 7,
						indexName: "generalindex",
			        	term: request.term
					},
		        	success: function( data ){
				    $.each(data, function(key, val) {
					if (key === "suggestionList"){
					    response( val );
					}
					if (key === 
				    });

				}
        		});
      		},
		//http://codeblogging.net/blogs/1/15/
		minLength: 1})
		.data("ui-autocomplete")._renderItem = 
			function(ul, item) {
				var inner_html = '<a><div class = "list_item_container"><div><img src="'+item.image+'"></div>' + item.suggestion  + item.key + '</div></a>';
				return $("<li></li>").data("item.autocomplete", item).append(inner_html).appendTo(ul);				 
			}		
  	});
  

	</script>
</head>


<body> 
<div class="ui-widget">
  <label for="tags">Suche: </label>
  <input id="tags" />
</div>
</body>


</html>

So our Ajax-request is in our autocompletion, it is the part where we define the origin for our search results. When you override the "source" part of our autocompletion, and want to use an external source, e.g. a server as ours, you can use a function for that, with 2 parameters, (request, response) Here comes our AJAX into play, our asynchronous request, while the user is typing a search term.

###Newsfeed Widget

We tried to get a newsfeed widget tutorial to work with a JSON input instead of a xml: http://net.tutsplus.com/tutorials/javascript-ajax/how-to-build-a-widget-to-display-your-buzzing/

The widget was a bit hard to understand, thanks to some weird code that seemed to do nothing at all. Thanks to Rene we streamlined it down to its core and now I try to explain a bit more in detail how it works. First of all, here is our complete js code we need for the widget to work:

jQuery.fn.metalconReader = function(options){
	return this.each(function(){
		var opts = options || {};
		opts.renderTo = this;
		new MetalconReader(opts);
	}); //End each function
}; //End Plugin
 
var MetalconReader = function(options){
    jQuery.extend(this,options || {});
    this.url = "http://141.26.71.115:8080/Graphity-Server-0.1/read?user_id="+this.user+"&poster_id="+this.user+"&num_items=15&own_updates=1";
	//this.url = "https://api.flattr.com/rest/v2/users/"+this.user+"/things.as";
	if(this.user === "") throw "The 'user' property is required";
	if(this.renderTo === "") throw "The 'renderTo' property is required";
	this.read();
}; //End constructor

MetalconReader.prototype = {
	renderTo: "",
	user	: "",
	items	: 10,
	
	read	: function(){
		this.el = jQuery(this.renderTo);
		this.el.append("<div class=\"metal-loading\"></div>");
		jQuery.ajax({
			url		: this.url,
			context	: this, 
		    success	: this.parse,
		}); //End AJAX call
	}, //End read function

	parse	: function(json_data){
		this.el.empty();
		this.data = json_data.items;
		this.render(this.renderTo);
	}, //End parse function

	render	: function(element){
		var html = [];
		var that = this;
		html.push("<ul>");
		for(var i = 0; i < this.items && i < this.data.length;i++){
			var temp_date = that.createDate(this.data[i].published);
			html.push("<li><strong><a href=\""+this.data[i].id+"\">"+that.format(temp_date)+"</a></strong><br><span>"+this.data[i].actor.id+": "+this.data[i].object.message+"</span></li>");
		} //End for
		html.push("</ul>");
		this.el.append(html.join(""));
	}, //End render function
	createDate	: function(str){
		str = str.substring(0,19).replace(/[ZT]/," ").replace(/\-/g,"/");
		return new Date(str);
	}, //End createDate function
	format		: function(date){
		var diff   = (((new Date()).getTime() - date.getTime()) / 1000),
			days   = Math.floor(diff / 86400),
			months = Math.floor(days / 31);

		if (isNaN(days) || days < 0)return date.toString();
		
		if(days == 0){ 
			if(diff < 60)return "Just now";
			if(diff < 120)return "1 minute ago";
			if(diff < 3600)return Math.floor( diff / 60 ) + " minutes ago";
			if(diff < 7200)return "1 hour ago";
			if(diff < 86400)return  Math.floor( diff / 3600 ) + " hours ago";
		}else if(days < 31){
			if(days == 1)return "Yesterday";
			if(days < 7)return days + " days ago";
			if(days < 31)return Math.ceil( days / 7 ) + " weeks ago";
		}else{
			if(months == 1)return "A month ago";
			if(months < 12)return Math.ceil( days / 31 ) + " months ago";
			if(months >=12)return Math.floor( days / 365 ) + " years ago";
		}//End else
	} //End format function	
}; //End Prototype

The script has the following structure. It starts with jQuery.fn.buzzReader = function(options){...} In this part we define how we can use the final widget in our html source code. This code checks if the given options are empty and writes the renderTo part of the options to this, which in that part refers to JQuery. After that we instantiate a new Object of our BuzzReader Class. In our html part, we can call our widget now with:

$(function(){
	$("#metal .reader").metalconReader({
		user	: "sampleUser",
		items	: 5
	});
});
That means render the data you get in your ("metal .reader") DOM object, with the parameters user and items.

Next step is our constructor for our object. It gets called when we make a "new" instance of our object.

var MetalconReader = function(options){
    jQuery.extend(this,options || {});
    this.url = "http://localhost:8080/Graphity-Server-0.1/read?user_id="+this.user+"&poster_id="+this.user+"&num_items=15&own_updates=1";
	if(this.user === "") throw "The 'user' property is required";
	if(this.renderTo === "") throw "The 'renderTo' property is required";
	this.read();
}; //End constructor

In this example we use a server that is located on the same computer as the script,therefore the localhost address. Then we have some error handeling if renderTo or user weren't set before but its not used at the moment. The more important part is that the constructor calls this.read(); when he is created. This calls his own read method, in which our next step follows. In the read part it gets a bit more complicated. In read we call this.el = jQuery(this.renderTo); and this.el.append("<div class=\"metal-loading\"></div>"); This basiclly means that we take the element that called our function in the first place, our $("#metal .reader") object in our html and render our data into that object. And as a first action of this we append a small giv that appears at the start when our widget loads. This gets removed as soon as our server provides data. After that we start a request to our server using AJAX.

jQuery.ajax({
			url		: this.url,  //take the URL you got from the start
			context	: this,   //use the object you had before instead of an JQuery object
		    success	: this.parse,  //when you are done with the ajax, call the parse function of my object
		}); //End AJAX call

The parsing part that follows is relatively simple. We use our element we defined before as our ("metal .reader") DOM object and first of all clear it with this.el.empty(); This means our loading giv we had displayed before disappears since our ajax call was successfull. Since we have a function with our data requested from the server, we need to save it to use it later. And last we call our render function, again to be rendered to our DOM object ("metal .reader")

render	: function(element){
		var html = [];
		var that = this;
		html.push("<ul id=\"newsstream\">");
		if (typeof this.data != 'undefined'){
			for(var i = 0; i < this.items && i < this.data.length;i++){
				var temp_date = that.createDate(this.data[i].published);
				html.push("<li><strong><a href=\""+this.data[i].object.id+"\">"+that.format(temp_date)+"</a></strong><br><span>"+this.data[i].actor.id+": "+this.data[i].object.message+"</span></li>");
			} //End for
		}//End if
		html.push("</ul>");
		this.el.append(html.join(""));
	}, //End render function

In the render part we create one long line of html code that we send to our form in the end. We start by creating an <ul> element and write as many <li> as we want to display in our widget. All in the form <li>date user message </li>. After we are done with that, we close the </ul> we started before, and join all our strings together with join, since we wrote our entries in a array before. The last part is just to format our data in a more readable way.

Activity Stream

Loading Components

Event Bus

User Interface

Transitions

Fading

Drag and drop

Clone this wiki locally