AJAX in Webware

John Dickinson has created three files which, together, provide a fairly simple way to implement some Ajax functionality into Webware servlets. AjaxPage provides a super class to your servlet (but below your site page). All JavaScript necessary is contained in ajax.js and ajax_polling.js. ajax.js provides basic functionality useful for most situations, and ajax_polling.js provides support for long-running requests and out-of-band server to client commands.

AjaxPage

The AjaxPage class creates a function ajax_allowed() which returns a list of function names. These function names refer to Python functions that are able to be called by an Ajax-enabled web page. This is very similar in functionality to webware's actions. Child classes should override ajax_allowed().

AjaxPage also provides the class PyJavascript. This class simply tanslates a Python expression into a string. For example:

foo = PyJavascript('bar')
foo.hello('hi').another.element.func() # evaluates as the string "bar.hello('hi').another.element.func()"

PyJavascript is based in large part on code from Nevow 0.4.1 (nevow.org).

The AjaxPage class uses PyJavascript to set up several useful references to JavaScript functions.

ajax.js and ajax_polling.js

ajax.js provides all the client-side heavy lifting required to get Ajax functionality into a web page. Several pieces of it are based on ideas from Apple developer code (developer.apple.com) and Nevow 0.4.1 (nevow.org).

ajax_polling.js provides functionality that allows the server to process long-running requests from the client and allows the server to send commands to the client without the user on the client side triggering an event.

The Anatomy of an AJAX-enabled page

  1. Client triggers an action. This request is sent to ajax_controller().

  2. ajax_controller() calls the appropriate functions on the server.
    1. Once the server-side function has returned, the results are sent back down the pipe to the client...

    2. ...unless it took longer that TIMEOUT seconds to complete (the timeout is defaulted to 100). If the function take a while to finish, ajax_controller() puts the results into the results bucket that ajax_response() is polling.

  3. ajax_response(), which is being polled every few seconds by the client, sees that there is something for the client in the results bucket, grabs it, and sends it to the cient.

The polling behavior of an ajax-enabled page may be controlled by overriding ajax_clientPollingTimeout. Setting its return value to None prevents the polling from happening at all. Otherwise, this function needs to return the number of seconds to wait until the client polls the server again.

Example

class my_servlet(AjaxPage):
  def ajax_allowed(self):
    return ['check_if_even']

  def check_if_even(self,val):
    is_even = (int(val) % 2) == 0
    if is_even:
      ret = self.setTag('response_id','The value you entered is even!')
    else:
      ret = self.alert('Oh no! The value you entered is not even!')
    return '%s; %s' % (self.setReadonly('even_num',False),ret) # don't forget to unlock the field

  def writeContent(self):
    wl = self.writeln
    wl('<form action="some_page.py" method="post">')

    # will first set the input field to read only, then call check_if_even with the field's value passed in
    ajax_cmd = self.generic_ajax(self.setReadonly('even_num',True),'check_if_even',self.this.value)

    wl('<input name="even_num" id="even_num" type="text" onchange="%s"/>' % ajax_cmd)
    wl('<span id="response_id"></span>') # see check_if_even() for tag name
    wl('</form>')

Downloads:

Robert Forkel has compiled an extended example named AjaxSuggest. This example shows how to implement functionality a la Google suggest. Note: This example will be already included in the Examples context of Webware for Python starting with version 0.9.1.