This post is the third post in a series that demonstrate how to create a small RESTful Ajax application leveraging the Dojo Toolkit and Python Werkzeug. This post describes the implementation of a ajax front-end using the dojo toolkit.

In part two of this series we implemented a RESTful backend. In this post we'll utilize that back-end and produce a usable UI with the dojo toolkit. The application is available at letsplantheevent.

Dojo is a comprehensive versatile JavaScript toolkit which offers about anything you can think of when it comes to JavaScript web development. The example application takes advantage of the following components in the toolkit:

RESTing with Dojo

Dojo includes thorough support for interacting with web services. Especially, when it comes to RESTful JSON web services, it requires a minimal amount of client-side code to interact with these, because of the predefined application protocol governed by REST.

In dojo, we interact with a RESTful resources through the JsonRestStore. In the last post, we implemented the concept of tasks as RESTful resources. A transcript of a session with a JavaScript interpreter interacting with those resources can look as follows:

dojo.require("dojox.data.JsonRestStore"); 
tasks = dojox.data.JsonRestStore({target: "/tasks/"});

// create new task, and save with POST
var t = tasks.newItem({name:"a task"});
tasks.save();

// NOTE: save is async. so you need wait here for the save to finished (or use a deferred)
t.__id; // "/tasks/1" <- from the location header in the XHR request.

// edit task and PUT it back.
t.name = "the task";
tasks.changing(t);
tasks.save();

// GET all tasks
tasks.fetch();

// GET item with specific id
tasks.fetchItemByIdentity({identity:t.__id});

// DELETE item
tasks.deleteItem(t);

You can tryout the example by pointing your browser to letsplantheevent, and paste code into your JavaScript console.

Transforming data into information with Dojo

Dojo includes many different layout widgets. The cool thing with these widgets are that they support the different dojo datastores which all have the same uniform interface. That uniform interface makes it possible to seamlessly wire together data and widgets.

For the example, we use the data grid, which enables users to explore and edit data quickly. The grid looks as follows, and is defined programmatically by:

tasks_grid = new dojox.grid.DataGrid({
  name: "tasks",
  structure: tasks_grid_structure, 
  store:tasks}, 'tasks_grid');

tasks_grid.startup();

In the code, the ‘tasks_grid’ refers to the DOM node where the grid is injected - a standard dijit widget pattern. The structure property defines the layout of the grid, manifested in an array of views. In views, we take advantage of an undocumented field: type. type refers to the widget that presents the value. constraint is a field used by our formatter.

  var tasks_grid_structure = [
    { 
      field: 'name',
      editable: true
    },
    {
      field: 'startdate',
      width: '70px',
      editable: true,
      type: dojox.grid.cells.DateTextBox,
      constraint: {formatLength: 'long', selector: "date"},
      formatter: lpte.formatters.datetimeFormatter
    },
    {
      field: 'starttime',
      width: '70px',
      editable: true,
      type: dojox.grid.cells.TimeTextBox,
      constraint: {timePattern: "HH:mm", selector: "time"},
      formatter: lpte.formatters.datetimeFormatter
    },
    // omitted end(time|date) for brevity
    {
      name: 'Actions',
      formatter: function(val, rowIdx, cell){
        var item = this.grid.getItem(rowIdx);
        if(!item.id){
          return "";
        }
        return "tasks.deleteById('" + item.id + "');";
      }
    }
  ];

  tasks_grid = new dojox.grid.DataGrid({
    name: "tasks",
    structure: tasks_grid_structure, 
    store:tasks}, 'tasks_grid');

  tasks_grid.startup();

The actions column shows a delete button when tasks have an id. On press the button calls the function tasks.deleteById, which is a function mixed into the tasks store.

dojo.require('dojox.data.JsonRestStore');

$.tasks = new dojox.data.JsonRestStore({
  target:"/tasks/", 
  deleteById: function(id){
    this.fetchItemByIdentity({identity: id, onItem: function(item){
      tasks.deleteItem(item);
      tasks.save();
    }});
  }
});