The code comes with a BSD-style license so you can basically do with it whatever you want.
Download shortcut: http://18.104.22.168/misc/ht-ajax.tar.gz.
(require 'asdf-install) (asdf-install:install :ht-ajax)
(require 'asdf) (asdf:oos 'asdf:load-op :ht-ajax)
Questions, bug reports, requests, criticism and just plain information that you found this package useful (or useless) are to be sent to email@example.com
In this tutorial we assume that the reader is reasonably familiar with Hunchentoot and AJAX.
So, let's suppose we already have some lisp code working under Hunchentoot and start modifying it to use AJAX. Note that normally we'll (use-package :ht-ajax), so that we won't have to prefix the symbols with ht-ajax:, but here we'll still do it to show clearly which symbols come from the HT-AJAX package.
At first some setup:
(defparameter *ajax-handler-url* "/hunchentoot/some/suitable/uri/ajax-hdlr")
Here we select some URL that will handle our AJAX requests. Later we'll need to arrange for an appropriate handler to be called when we get a request for this URL (and all URLs starting with it). Replace the URL with whatever makes sense for your application
After this we create an instance of so-called ajax-processor that will handle all our AJAX needs. One ajax-processor per application should be enough. We pass the following parameters: :type :lokris to select which backend to use, in this case it's the Lokris library. Also we pass the :server-uri that we've selected and the :js-file-uris that shows where to find the appropriate library file, lokris.js in this case (the URL may be relative to the URL of the page):
(defparameter *ajax-processor* (ht-ajax:make-ajax-processor :type :lokris :server-uri *ajax-handler-url* :js-file-uris "static/lokris.js"))
Now we create the function that we want to be called from the web page:
(ht-ajax:defun-ajax testfunc (command) (*ajax-processor* :method :post) (prin1-to-string (eval (read-from-string command nil))))
(defun testfunc (command) (prin1-to-string (eval (read-from-string command nil)))) (ht-ajax:export-func *ajax-processor* 'testfunc :method :post)
The only thing left to prepare the server side of the things is to create the dispatcher for our *ajax-handler-url* and to add it to Hunchentoot's dispatch table. The specifics of this can vary, but it might include something like:
(create-prefix-dispatcher *ajax-handler-url* (ht-ajax:get-handler *ajax-processor*)
The call to (ht-ajax:get-handler *ajax-processor*) returns the handler that the handler URL needs to be dispatched to.
Now we need to make sure that the dynamic web pages we generate are correctly set up. This means that the result of the call (ht-ajax:generate-prologue *ajax-processor*) needs to be inserted somewhere in the HTML page (right after the <body> tag seems like a good place). Once again how to do this depends on the facilities that are used for HTML generation. For example when using HTML-TEMPLATE we'll have something like the following in our template:
<body> <!-- TMPL_VAR prologue -->
and then pass the output of (ht-ajax:get-handler *ajax-processor*) as the prologue parameter to the fill-and-print-template call.
After that, whatever means for HTML generation we're using, let's put the following HTML somewhere in the page:
This will produce something like:
|no results yet|
The function ajax_testfunc_set_element that we call here was generated for us by HT-AJAX. It takes one required parameter - the id of the element that we want to be set to the result of the remote call. All other parameters will be passed to corresponding exported lisp function, testfunc in this case (watch out for the number of arguments). The resulting string will be assigned to the .innerHTML property of the element with the id 'result'.
This is it. Now to save the files, compile the lisp code, make sure the Hunchentoot server is started and open the web page in the browser. Enter something like (+ 1 42) in the text field and click 'Eval'. If all's well the results of the evaluation will be displayed.
ajax_testfunc_callback(callback_specification, [params for the server's TESTFUNC....])and
ajax_testfunc_set_element(element_id, [params for the server's TESTFUNC....])
Both functions will call the server-side function TESTFUNC, the ajax_testfunc_callback
version will call the provided callback function with the result of the server call
as a single parameter, the ajax_testfunc_set_element version with find the document
element with the id element_id and set it's innerHTML to the result of the
The result of the server call is normally a string and is passed to the callback as-is, unless the Content-Type header was set to application/json which is the official IANA media type. In case of JSON the result is evaluated using "unsafe"  eval call and the resulting object is passed to the callback.
The callback_specification parameter can be used
to specify two kinds of callacks (at the same time). The success callback function will
after a successful interaction with the server and passed the server call result
as a parameter. The error callback function will be called in case of an error
and passed a string with the information about the error.
So the callback_specification can take the following forms:
make-ajax-processor &rest rest &key type &allow-other-keys => new-ajax-processor
Creates an ajax-processor object. Parameters:
TYPE - selects the kind of ajax-processor to use (should be one of:SIMPLE or :LOKRIS, :PROTOTYPE, :YIU or :DOJO) (required).
SERVER-URI - url that the ajax function calls will use (required).
JS-FILE-URIS - a list of URLs on your server of the .js files that the used library requires , such as lokris.js or prototype.js (parameter required for all processors except :SIMPLE). If only one file needs to be included then instead of a list a single string may be passed. Also if this parameter is a string that ends in a forward slash ( #\/ ) then it is assumed to be a directory and the default file names for the processor are appended to it.
AJAX-FUNCTION-PREFIX - the string to be prepended to the generated js functions, (default prefix is "ajax_").
export-func processor funcallable &key method name content-type allow-cache =>|
Makes the function designated by FUNCALLABLE exported (available to call from js) Parameters:
METHOD - :get (default) or :post (:post is not supported under SIMPLE processor).
NAME - export the function under a different name.
CONTENT-TYPE - Value of Content-Type header so set on the reply (default: text/plain).
ALLOW-CACHE - (default nil) if true then HT-AJAX will not call NO-CACHE function and allow to control cache manually.
JSON - (default nil) if true, the function returns a JSON-encoded object that will be decoded on the client and passed to the callback as an object
unexport-func processor symbol-or-name =>|
Removes the previously exported function, should be called with either the name (string) under which it was exported or the symbol designating the function
defun-ajax name params (processor &rest export-args) declaration* statement*
Macro, defining a function exported to AJAX Example: (defun-ajax func1 (arg1 arg2) (*ajax-processor*) (do-stuff))
generate-prologue processor &key use-cache => html-prologue
Generates the necessary HTML+JS to be included in the web page. Provides caching if USE-CACHE is true (default).
get-handler processor => handler
Get the hunchentoot handler for AJAX url. The url that was passed as the SERVER-URI parameter (and all URLs starting with it) should be dispatched to this handler.
At the moment HT-AJAX is known to run on SBCL and Lispworks, but it aims to be portable across all the implementations Hunchentoot runs on. Please report all incompatibilities.
 When not using CREATE-PREFIX-DISPATCHER, note that not only the SERVER-URI itself but also all the URLs starting with it need to be dispatched to the handler in order for "virtual .js files" mechanism to function.
(let ((*string-modifier* #'CL:IDENTITY)) ...template expansion... )
This documentation was prepared with the help of
by Edi Weitz (the code was hacked to run on SBCL).
The initial inspiration for the SIMPLE processor came from Richard Newman's CL-AJAX which is designed for use with Araneida.
;;; Copyright (c) 2007, Ury Marshak