HT-SIMPLE-AJAX - Another Ajax library for Hunchentoot

Abstract

HT-SIMPLE-AJAX is an Ajax library for the HUNCHENTOOT web server. It allows to call ordinary Lisp functions from within an html page using javascript and asynchronous client/server communication.

It is a heavily simplified (150 lines of code) version of HT-AJAX that is compatible with newer versions (>1.1) of HUNCHENTOOT. It was initially developed for GTFL and therefore provides only one type of Ajax processor (which resembles the 'simple' processor of HT-AJAX).

HT-SIMPLE-AJAX comes with a BSD-style license so you can basically do with it whatever you want.

Download shortcut: http://martin-loetzsch.de/ht-simple-ajax/ht-simple-ajax.tar.gz.

Github: https://github.com/martin-loetzsch/ht-simple-ajax

Contents

  1. Download and installation
  2. Example
  3. The HT-SIMPLE-AJAX dictionary
    1. ajax-processor
    2. create-ajax-dispatcher
    3. defun-ajax
    4. generate-prologue
  4. Acknowledgements

Download and installation

HT-SIMPLE-AJAX together with an example and this documentation can be downloaded from http://martin-loetzsch.de/ht-simple-ajax/ht-simple-ajax.tar.gz. The current version is 0.5.

HT-SIMPLE-AJAX depends on the HUNCHENTOOT (version >= 1.2.x) web server, which itself requires quite a number of other libraries.

If you don't want to download all these libraries manually, you can use Quicklisp or ASDF-INSTALL:

(ql:quickload "ht-simple-ajax")
(asdf-install:install 'ht-simple-ajax)

Once everything is installed, HT-SIMPLE-AJAX is compiled and loaded with:

(asdf:operate 'asdf:load-op :ht-simple-ajax)

Example

This is a brief walk-through of ht-simple-ajax. You can try out the whole example in demo.lisp (also contained in the release).

First we create an ajax processor that will handle our function calls:

(defparameter *ajax-processor* 
  (make-instance 'ajax-processor :server-uri "/ajax"))

Now we can define a function that we want to call from a web page. This function will take 'name' as an argument and return a string with a greeting:

(defun-ajax say-hi (name) (*ajax-processor*)
  (concatenate 'string "Hi " name ", nice to meet you."))

We can call this function from Lisp, for example if we want to test it:

HT-SIMPLE-AJAX> (say-hi "Martin")
"Hi Martin, nice to meet you."

Next, we setup and start a hunchentoot web server:

(defparameter *my-server* 
  (start (make-instance 'easy-acceptor :address "localhost" :port 8000)))

We add our ajax processor to the hunchentoot dispatch table:

(setq *dispatch-table* (list 'dispatch-easy-handlers 
                             (create-ajax-dispatcher *ajax-processor*)))

Now we can already call the function from a http client:

$ curl localhost:8000/ajax/SAY-HI?name=Martin
<?xml version="1.0"?>
<response xmlns='http://www.w3.org/1999/xhtml'>Hi Martin, nice to meet you.</response>

Alternatively, you can also paste the url above in a web browser

To conveniently call our function from within javascript, the ajax processor can create a html script element with generated javascript functions for each Lisp function:

HT-SIMPLE-AJAX> (generate-prologue *ajax-processor*)
"<script type='text/javascript'>
//<![CDATA[ 
function ajax_call(func, callback, args) {
  // .. some helper code 
}

function ajax_say_hi (name, callback) {
    ajax_call('SAY-HI', callback, [name]);
}
//]]>
</script>"

So for our example, the javascript function ajax_say_hi was generated. name is the parameter of the Lisp function (if there are multiple parameters, then they will also appear here) and callback is a function that will be asynchronously called when the response comes back from the web server. That function takes 1 argument, which is the xml DOM object of the response.

Finally, we can put everything together and create a page that calls our function. For rendering html, we will use cl-who in this example (note that ht-simple-ajax can be used with any other template/ rendering system):

(define-easy-handler (main-page :uri "/") ()
  (with-html-output-to-string (*standard-output* nil :prologue t)
    (:html :xmlns "http://www.w3.org/1999/xhtml"
     (:head
      (:title "ht-simple-ajax demo")
      (princ (generate-prologue *ajax-processor*))
      (:script :type "text/javascript" "
// will show the greeting in a message box
function callback(response) {
  alert(response.firstChild.firstChild.nodeValue);
}

// calls our Lisp function with the value of the text field
function sayHi() {
  ajax_say_hi(document.getElementById('name').value, callback);
}
"))
     (:body
      (:p "Please enter your name: " 
          (:input :id "name" :type "text"))
      (:p (:a :href "javascript:sayHi()" "Say Hi!"))))))

Direct your web browser to http://localhost:8000 and try it out!

The HT-SIMPLE-AJAX dictionary

You can also directly look at ht-simple-ajax.lisp, it's fairly simple.

[Standard class]
ajax-processor

Maintains a list of lisp function that can be called from a client page.

:server-uri defines the absolute url for handling http ajax requests (default "/ajax").

:content-type defines the http content type header for XML responses (default "text/xml; charset=\"utf-8\"").

:reply-external-format determines the format for the character output stream (default hunchentoot::+utf-8+).

[Function]
create-ajax-dispatcher processor => dispatcher function

Creates a hunchentoot dispatcher function that handles incoming ajax requests. processor is an instance of ajax-processor.

[Macro]
defun-ajax name params (processor) &body body => no values

Declares a defun that can be called from a client page. processor is an instance of ajax-processor.

See example above.

[Function]
generate-prologue processor => string

Creates a <script> ... </script> html element that contains all the client-side javascript code for the ajax communication. Include this script in the <head> </head> element of each html page. processor is an instance of ajax-processor.

Acknowledgements

All credits should go to the original author of HT-AJAX, who unfortunately doesn't maintain that library anymore.

The layout and structure of this page is heavily inspired by (or directly copied from) DOCUMENTATION-TEMPLATE.

Last change: 2013/04/17 23:17:00 by Martin Loetzsch