TurboGears: 20 Minute Wiki, Page 6

## You want AJAX? We got AJAX!

This part of the tutorial is not technically AJAX. The "X" in AJAX stands for XML. I'm going to use JSON instead. JSON is easy and lightweight and efficient to use for all browsers. To see how easy it is to use JSON, point your browser at http://localhost:8080/pagelist?tg_format=json. There's your pagelist in JSON format.

In standard CherryPy methods, you can return a string containing the rendered page. You can do that with TurboGears as well. But, if you write your code the way we have been in this tutorial, returning dicts instead of strings, you'll get JSON output for free.

This part of the tutorial requires no Python code. We'll just be doing JavaScript using MochiKit. To keep things simple, we're going to change our "view complete page list" link to pull in the pagelist and include it right in the page you're viewing. Our changes will all be in master.kid.

The first thing we need to do is have MochiKit included in all of our pages. It turns out that this is easy, using Kid's py:match feature:

<![CDATA[ <head py:match="item.tag=='{http://www.w3.org/1999/xhtml}head'"> <meta content="text/html; charset=UTF-8" http-equiv="content-type" py:replace="''"/> <title py:replace="''">Your title goes here</title> <meta py:replace="item[:]"/> <script src="/tg_js/MochiKit.js"></script> </head> ]]>

Note that we need to add py:replace="''" to the tags that appear in the other templates. If we didn't do that, we'd have two title tags! We also need to add some tag (any tag) that will get replaced by the contents of the head of the child page. The net result of this new head element is that the script tag that loads MochiKit will find its way onto every page.

Next up, we'll replace our page list link with one that will invoke a JavaScript function. We'll also add an empty container for our pagelist.

<![CDATA[ <p>View the <a href="#" onclick="requestPageList()">complete list of pages.</a></p> <div id="pagelist"> </div> ]]>

Now, we use MochiKit to retrieve the page list in requestPageList, which we'll define in a script tag in the head of the page.

<![CDATA[ <script type="text/javascript"> function requestPageList() { var d = loadJSONDoc("$${std.url('/pagelist', tg_format='json')}"); d.addCallback(showPageList); } ]]>

MochiKit includes a function for doing an asynchronous XMLHttpRequest and converting the result from JSON into a JavaScript object. Handy!

loadJSONDoc returns a "Deferred" object. The idea with a Deferred is that we know that our request for the pagelist will happen some time in the future, but we don't know when. A Deferred is a placeholder that allows us to specify what happens when the result comes in. We have a very simple requirement here: call a function called "showPageList", which we'll write now:

<![CDATA[ function showPageList(result) { var currentpagelist = UL(null, map(row_display, result["pages"])); replaceChildNodes("pagelist", currentpagelist); } ]]>

When loadJSONDoc gets its result, it will pass it along to showPageList. What we end up with in JavaScript is the same dictionary our pagelist method returned in Python!

MochiKit.DOM allows you to create new document elements by nesting tags in your JavaScript. We're creating a UL element, just like we had in our freestanding pagelist page. The first parameter is a mapping of attributes for the element, which is null in this case. The other parameters will all become child nodes. MochiKit's map function works just like Python's. For each element in result["pages"], we're going to call a function called row_display, which I'll come to momentarily. The last part is to replace the contents of our <div> with the id of pagelist with the new DOM elements we've just created.

Let's create our row_display function and close off our script tag:

<![CDATA[ function row_display(pagename) { return LI(null, A({"href" : "$${std.url('/')}" + pagename}, pagename)) } </script> ]]>

Each pagename from the pages list will get passed into row_display. We create an LI element (no attributes), and an A element with an href attribute. The A element also has the pagename as its contents.

Voila! If you go to your front page and click on the page list link, you'll see the page list right there in the page.

## The final files

The final files for this demo are in wiki20.tgz. You'll need to edit dev.cfg to point to the database file that is included in the tarball.

## For further reading...

If you had any problems with this tutorial, or have ideas on how to make it better, please let us know on the mailing list! Documentation patches are happily accepted.

Reference documentation that covers more details is available on the docs page.