Quantcast
Channel: klauskomenda.com » Agent YUI
Viewing all articles
Browse latest Browse all 6

Agent YUI: Mission 5 – Ajax. Shaken, not stirred.

$
0
0

Missions 1 to 4 covered a lot of stuff already and gave the attentive reader a good idea what the YUI is capable of and can do for you in everyday development. Today we will discover how to use a very important utility that is part of YUI and pretty essential these days in a world where Web 2.0 rules the world (there you have your villain): YUI Connection Manager.

Introduction

Bond: Vodka Martini.
Bartender: Shaken or stirred?
Bond: Do I look like I give a damn?

from Casino Royale

Ajax functionality, i.e. loading and displaying new content to the user without having to refresh the page, is the new black these days (well, actually, it is the new black for quite a while already). A website without Ajax these days is like James Bond driving a Toyota (no offense to the Japanese automaker though)—it is just not right (at least a lot of product manager will make you believe that). So how are we going to deal with all that request/response hassle including browser incompatibilities without causing us headaches? We make YUIs Connection Manager do all the hard word for us!

The Mission Statement

What we would like to achieve is the following:

  • We have a page listing our 3 favorite James Bond actors
  • Without JavaScript, clicking on the link “[actor name] appeared in…” will takes us to a new page, showing the actor again, with a table underneath listing movies he appeared in
  • With JavaScript, clicking on that link should keep the user on the same page and load that same table via Ajax from the server and display it below the images of the actors.

Step 1a: The Markup for the Overview Page

As usual, we are not rushing into things, but instead stick to the initial plan of starting with the markup, adding appropriate CSS for styling and then add behavior using JavaScript on top. The markup bits for the list very straightforward:

<h1>Which actor appeared in which (other) movies?</h1>
<ul>
    <li><img src="img/sean.jpg" alt="Sean Connery" /><a href="Sean_Connery/">Sean Connery appeared in&hellip;</a></li>
    <li><img src="img/brosnan.jpg" alt="Pierce Brosnan" /><a href="Pierce_Brosnan/">Pierce Brosnan appeared in&hellip;</a></li>
    <li><img src="img/craig.jpg" alt="Daniel Craig" /><a href="Daniel_Craig/">Daniel Craig appeared in&hellip;</a></li>
</ul>

This was easy and that first step can be viewed on the initial demo page.

Step 1b: The Markup for the Details Page

While we are at it, we can also prepare the markup for the page that gets loaded when a user clicks on one of the “…appeared in…” links:

<h1>Sean Connery appeared in&hellip;</h1>
<div>
    <img src="../img/sean.jpg" alt="Sean Connery" />
</div>
<!-- table will go here -->

Step 1c: Setting up the Server

Before we jump into Step 2, I would like to show you how I set up the server to support our Ajax functionality. I created sub directories for each actor which hold:

So the structure for the directory “Sean_Connery” might look like:

Directory of \Sean_Connery

26.07.2008  21:59    <DIR>          .
26.07.2008  21:59    <DIR>          ..
23.07.2008  19:56             1.734 index.php
23.07.2008  20:46             6.932 table_snippet.php

This is sufficient for this simple example, but in the real world, this would be set up differently. You would probably have one index.php file which, based on a querystring or particular part of the URL retrieves the data from a datasource, e.g. a database, generates the table markup and provides this to the browser. But going into these details goes beyond this tutorial.

How this kind of structure facilitates the Ajax flow will be explained when we get to Step 3.

Step 2: Adding some styling

As usual, we put some CSS into the game to make things a little bit prettier. No rocket science. The result can, again, be viewed on the demo page for this step.

Step 3: Ajax, please

Now, when we click on one of the “…appeared in…” links we get redirected to a new page with the actor image at the top and the data table underneath. But now we want to use Ajax to keep the user on the same page, load the data and display it underneath our list of actors. How are we going to do it?

Before we dive into it, lets have a look at how YAHOO.util.Connect (the YUI Connection Manager) works. This object has a function as a property called asyncRequest:

YAHOO.util.Connect.asyncRequest  ( method , uri , callback , postData )

So what do these parameters do?

  • method: either “GET” or “POST”
  • uri: the URI to your script that you request data from and that returns data to the script in your browser
  • callback: an object that defines which functions should handle the response coming back from the server
  • postData: additional data you want to send when doing a POST request

We are only going to use the first 3 parameters in this example.

At first, we set up a skeleton for our JavaScript module at the bottom of the page and in the init function we get all the anchors on the page.

<!-- Dependency -->
<script src="http://yui.yahooapis.com/2.5.2/build/yahoo/yahoo-min.js"></script>

<!-- Used for Custom Events and event listener bindings -->
<script src="http://yui.yahooapis.com/2.5.2/build/event/event-min.js"></script>

<!-- Source file -->
<script src="http://yui.yahooapis.com/2.5.2/build/connection/connection-min.js"></script>
<script type="text/javascript">
YAHOO.namespace("AgentYUI");

YAHOO.AgentYUI.Mission5 = function () {

    var init = function () {
        // get all links on the page
        var anchors = document.getElementsByTagName("a");
        var len = anchors.length;
    };
    return {
        init: init
    };
}();

YAHOO.AgentYUI.Mission5.init();
</script>

After that, we loop through the anchors array and attach an event handler to every onclick event

// loop through anchors and attach XHR request
for (var i = 0; i < len; i++) {
    YAHOO.util.Event.addListener(anchors[i], "click", function (e) {
        YAHOO.util.Event.preventDefault(e);
        YAHOO.util.Connect.asyncRequest("GET", this.href + "table_snippet.php", { success: requestSuccess, failure: requestFailure });
        })
    };
}

Within the loop, we are first using YAHOO.util.Event to attach a listener. The event handler then first prevents the user from getting redirected to the non-JS page. And after that, we are using the newly introduced YUI Connection Manager. We are doing a get request to this.href + “table_snippet.php”, which will, at runtime, be resolved to e.g. “Sean_Connery/table_snippet.php”. If the Ajax request was successfull, we want to have a requestSuccess function that handles the response. If not, call requestFailure.

Now you can see that having one file, table_snippet.php, which serves the data for both the Ajax and non-Ajax version, is a good idea. It saves us from coding things twice when essentially, the data (and the markup) that we would like to serve are the same.

After that for-loop, we still need to do a tiny, but essential thing: We need to prepare a container where we want to put our HTML table when we get the response back. We do this by putting a div after our unordered list with id=container:

// create container where to put in the returned HTML
var bodyEl = document.getElementsByTagName("body")[0];
responseContainer = document.createElement("div");
bodyEl.appendChild(responseContainer);

So what do the requestSuccess/requestFailure functions look like? They are both amazingly simple:

var requestSuccess = function (o) {
    var response = o.responseText;
    responseContainer.innerHTML = response;
}
    
var requestFailure = function (o) {
    console.log("XHR failed!");   
}

In requestSuccess what happens is that a response object o gets passed in, which contains a lot of additional properties about the request/response (you can do a console.dir(o) if you are interested into what sorts of info is contained in that object). For us, we really only care about responseText because that contains the HTML of the table we want to display. All for us then is left to do is dump the markup we got from the server into our container element using innerHTML.

Two notes regarding this:

  • In the real world, you would probably deal with a response in JSON format more often than HTML fragments. To be able to use the response as a JSON object in your module, you need to eval it first. The syntax for this is:
    var response = eval("(" + o.responseText + ")");

    Update: Strictly speaking, and for security reasons, it is safer to use the YUI JSON Utility to take care of the parsing instead of using “eval”. We won’t go into details here, but Douglas can explain why eval is evil. So the syntax to use, when the JSON utility is included on the page (thanks alfred for pointing that out):

    var response = YAHOO.lang.JSON.parse(o.responseText); 

    After that you can access all the properties in the object that the server provided you in JSON format using the dot notation e.g. response.myProperty.

  • People might argue that innerHTML is bad. It is not. It is a proprietary method invented by Microsoftt but it works in all major browsers today, which is the major thing here. Especially when dealing with HTML fragments in responses like this, it makes life much easier.

Et voilá, we have implemented our Ajax interaction.

Because we are not beginners anymore we can enhance the experience a little bit by using Animation. After that little polishing, have a look at the final result.

Further Reading


Viewing all articles
Browse latest Browse all 6

Latest Images

Trending Articles





Latest Images