Posted by Tom on 2006-12-05

OK, next up we’re going to see what Hobo brings to the Ajax table. In a nutshell – the ability to refresh fragments of a page without pushing them out into separate partial files. To see how that works, let’s knock up a quick demo application.

We’re going to build on the todo-list demo app from an earlier post, so if you want to follow along you should start with that post. Just to recap, the app is trivially simple, consisting of a TodoList model which has_many :tasks and a Task model which belongs_to :todo_list. We created a controller for to-do lists with just a show action, and we implemented a DRYML view for that action.

We’re going to add an ajaxified “New Task” form on that same page. Let’s do the back-end first, so that it’s possible to create tasks. We’ll start by being good modern Rails citizens, and switch to RESTful routing. Add this line to routes.rb:

map.resources :todo_lists, :tasks

Now create our controller

$ ./script/generate controller tasks

app/controllers/tasks_controller.rb

Now we’ll add a simple ajax form to todo_lists/show.dryml. We’ll use the familiar remote_form_tag helper. We’ll use raw HTML for the form controls, just to be clear about exactly what’s going on, What we won’t do, for now, is deal with refreshing the page.

That should work as is, although you’ll have to manually reload the page to see any new tasks. Let’s fix that, Hobo style.

With DRYML, any fragment of the page can be marked as a part. A part can be rendered separately from the rest of the page, just like a partial. To create a part, just give any element a part_id. For this demo we’ll add it to the ul_for tag.

The important bits to note are the <span> with the same ID as our part, and the JavaScript snippet at the end. The JavaScript was generated by Hobo to keep track of which model objects are displayed in which parts.

We can ask for a part to be updated, simply by adding a few parameters to the request. Hobo provides tags to make this blissfully easy, and we’ll have a look at those shortly. For now though, just to show there’s no magic going on, we’ll add them by hand using hidden fields.

The parameters we need are:

part_page: The path of the current page template, e.g. “todo_lists/show”. (Future development: maybe we can do away with this and use the HTTP referrer instead)

render[][part]: The name of the part we’d like to refresh. e.g. task_list

render[][object]: The “DOM ID” of the “context object” for that part. e.g. todo_list_1

app/controllers/tasks_controller.rb

The hobo_ajax_response method needs a page context. As you can see we’re passing in the object just created.

You should now have a working ajax “New Task” feature.

The code might work but it’s pretty ugly (heh). Let’s clean it up using the appropriate Hobo tags. The tags we need come from a tag library that’s provided with Hobo – Hobo Rapid. Hobo Rapid is a very general purpose tag library that makes it extremely quick and easy to do the bread-and-butter stuff: links, forms, ajax…

We’ll look at Hobo Rapid in more detail in another post. For now we’ll see how to pretty-up our ajax demo.

The <create_form> tag can be read as: Include a form which will first create an object in the collection “tasks” of the current context (the TodoList), and then update the “task_list” part. Note that you don’t need to say anything about how to update that part. Hobo knows.

<booming-voice>AND NOW [drum-roll] THE GRAND FINALE…</booming-voice>

How easy is it to update multiple parts in one go? For example, suppose the page had a count of the number of tasks – that would need updating too. We’ll use another handy little tag from Hobo Rapid: <count> (Note this tag doesn’t have any special ajax support. We could have used a regular ERB scriptlet and the ajax would still work). And for bonus marks, we’ll DRY up all those attr='tasks' using a <with> tag, which just changes the context.