Login | Register   
LinkedIn
Google+
Twitter
RSS Feed
Download our iPhone app
TODAY'S HEADLINES  |   ARTICLE ARCHIVE  |   FORUMS  |   TIP BANK
Browse DevX
Sign up for e-mail newsletters from DevX


advertisement
 

Speed Up Your AJAX-based Apps with JSON : Page 3

Find out how to improve your Web application performance by leveraging AJAX and JSON. In particular, you'll see the advantages of using JSON over XML as a lightweight JavaScript data-interchange format.


advertisement
The Observer Pattern
The Observer Pattern defines a one-to-many dependency between objects so that when one object (the Observee) changes state, all its dependents (the Observers) are notified and updated automatically. Most literature refers to the Observee as the Subject. For this article I've used the term "Observee" to emphasize the role of this object—it's the object responsible for downloading the news. The Observers are the objects interested in receiving the news.

The sample application implements the Observer using a JavaScript class called "Marquee," while the Subject (from now on I'll refer to it as Observee) is the "ContentRetriever" class. Note that, Marquee and ContentRetriever both represent the concrete classes of Figure 3. They do not inherit from abstract classes because JavaScript doesn't strictly define abstract classes, a subject which—even if we could "simulate" abstract classes—goes beyond the scope of this article. Marquee's responsibility is to display the information within a marquee element. ContentRetriever, on the other hand, takes care of downloading the news in JSON format, using AJAX calls.

ContentRetriever use a helper class, HtmlBuilder, to parse the retrieved JSON text and build the HTML code to be displayed within a marquee HTML element. This helper class isolates the presentation logic. If you wanted to change the news format you would need to modify only the logic in HtmlBuilder. The sample application uses a CSS stylesheet to define the fonts, colors and so on, which you can modify it according to your taste.

Here's the ContentRetriever class constructor:

function ContentRetriever(jsonDataRetriever, observerObjectsArray) { // JSON data retrieved and parsed this.jsonData = ""; // URL of the Server-side component responsible // for retrieving the JSON data this.jsonDataRetriever = jsonDataRetriever; // AJAX object this.ajaxObj = createHttpRequest(); // array of observers this.observerObjectsArray = observerObjectsArray; }

The first parameter is a URL that points to the server-side component responsible for retrieving the news. The second parameter is an array of objects (observers) interested in being notified when the information gets retrieved by ContentRetriever (the observee). After all, this is the essence of the Observer Pattern: something doing a job and others just sitting around waiting to be notified when it's done! The function createHttpRequest creates an XMLHttpRequest object, taking browser differences into account. This function is defined within the ajax.js file that you can find in the downloadable code for this article. The file demo.htm contains the JavaScript code that builds and displays the ticker:

<script type="text/javascript"> var marquee = new Marquee("newsMarquee", 2, true); var contentRetriever = new ContentRetriever( "./tickerDataRetriever.php", [marquee]); //get the content contentRetriever.getContent(); </script>

The preceding code creates an instance of the Marquee class, which I'll discuss shortly, and then builds a ContentRetriever object, passing in both the URL of the PHP script responsible for retrieving the data and an array of observer objects—in this case just one.

Here's the code of the Marquee constructor:



function Marquee(elemId, scrollamount, stopOnMouseOver) { this.elemId = elemId; this.scrollamount = scrollamount; this.stopOnMouseOver = stopOnMouseOver; this.dataArray = []; this.errorMessage = "No news available"; }

The Marquee constructor takes three parameters:

  • elemId—This is the id of the div element apt to display the news.
  • scrollamount—The scrollamount attribute of the marquee HTML element. It indicates the scrolling speed.
  • stopOnMouseOver—A Boolean indicating whether news scrolling should be stopped when the user hovers the mouse over a news item.
The dataArray property is the array containing the news items retrieved and parsed, and the errorMessage property is an error message to display if the news download fails.

The method that actually retrieves the news is getContent(), in the ContentRetriever class. Here's the code:

ContentRetriever.prototype.getContent = function() { if (this.ajaxObj) { var instance = this; this.ajaxObj.open('GET', this.jsonDataRetriever, true); //callback function this.ajaxObj.onreadystatechange = function(){instance.processDataRetrieved();}; this.ajaxObj.send(null); } };

First getContent() checks whether the XMLHttpRequest object was created correctly. Then it prepares an AJAX call using the HTTP GET method, the URL for the PHP news-retrieval script, and a Boolean specifying whether the call should be asynchronous (the third parameter (true) of the open() call). Because the call is asynchronous you must define a callback function that will execute when the call completes; in this case, that's processDataRetrieved. Lastly it sends the AJAX call without any other data (using null as the argument of send()).

The server-side script that retrieves the actual data—the file tickerDataRetriever.php—is very simple:

<?php //news folder $NEWS_FOLDER = "news"; //news file $filename = "news.txt"; //name of the news file $localFile = $NEWS_FOLDER . "/" . $filename; //return the content as a JSON string print(file_get_contents($localFile)); ?>

For simplicity's sake, the news in JSON format was hard-coded within a text file. Of course, in a real world application, the information would be retrieved from a database, an RSS feed or whatever you might choose for dynamic content. In this case, the PHP script just retrieves the JSON text and passes it back to ContentRetriever. This is the content of news.txt:

{ "items" : [ { "title" : "This is the first item's title", "link" : "http://www.alessandrolacava.com", "description" : "This is the description of the \"first\" news. The link takes you to my Web site" }, { "title" : "Second item's title", "link" : "http://www.json.org", "description" : "You can appreciate how easily you can parse JSON text. This link takes you to the JSON Web site" }, { "title" : "This is the third item's title.", "link" : "http://www.devx.com", "description" : "The link for this item takes you to the DevX Web site" } ] }

As you can see, the file content is just a JSON string representing an array of objects. Each object represents a news item and has three properties: title, link and description. Notice the escaped double quotes (\") in the first item's description value. You must escape embedded double quotes; otherwise the parser will interpret them as the end of the field value. At the end of the article you'll see how to automate this process when retrieving dynamic content.

The following function processes the retrieved content (the JSON text):

ContentRetriever.prototype.processDataRetrieved = function() { //request completed if (this.ajaxObj.readyState == 4) { //request succeeded if (this.ajaxObj.status==200) { //check whether the response is empty if(this.ajaxObj.responseText != null && this.ajaxObj.responseText.length > 0) { var htmlBuilder = new HtmlBuilder(this.ajaxObj.responseText); //parse the JSON data this.jsonData = htmlBuilder.getHtml(); } else { //return a zero-length array if an error occurs this.jsonData = []; } // notify observers this.notify(); } } };

The preceding code first checks if the request completed successfully. Then it instantiates an HtmlBuilder object, passing in the JSON text. The HtmlBuilder's getHtml method parses the JSON string and builds the HTML code to be displayed within the marquee. When it's done, it calls the notify method:

//notify all observers ContentRetriever.prototype.notify = function() { //call the observers' callback function, passing in the resulting array for(var i = 0; i < this.observerObjectsArray.length; i++) { this.observerObjectsArray[i].update(this.jsonData); } };

The notify method calls the update method of all the subscribed observers—in this case just one, the marquee.

The HtmlBuilder getHtml function does the transformation work of creating HTML from the returned JSON code:

//build the HTML code and return it as a JavaScript array of items HtmlBuilder.prototype.getHtml = function() { var ret = []; //decode the JSON text var obj = this.decodeJson(); //get the array var items = obj.items; //build the HTML code using the array for(var i = 0; i < items.length; i++) { var item = ""; //build the title item += this.buildTitle(items[i]); item += "<br />"; //build the description item += this.buildDescription(items[i]); item += "<br />"; ret.push(item); } return ret; };

This function parses the JSON text and creates a set of HTML strings that it returns to ContentRetriever as a JavaScript array. The actual parsing of the JSON string occurs within the decodeJson function:

HtmlBuilder.prototype.decodeJson = function() { return eval("(" + this.jsonString + ")"); };

As you can see it takes just one line of code to parse the whole JSON data! Isn't that great? If you think it's not then, as an exercise, represent the same news using XML and parse it using JavaScript. I bet you'll change your mind at once!

The rest of getHtml just formats the news conveniently and creates an array of news items. This array is the parameter passed to the update method of Marquee that displays the news within a marquee HTML element.



Comment and Contribute

 

 

 

 

 


(Maximum characters: 1200). You have 1200 characters left.

 

 

Sitemap