|
| 1 | +Ajax is surprisingly easy to use with the Arc language. This article shows how to implement Ajax-based autocompletion and dynamic page updates using |
| 2 | +the <a href="http://wiki.script.aculo.us/scriptaculous/">script.aculo.us</a> and <a href="http://prototypejs.org/">Prototype</a> JavaScript frameworks. These frameworks allow Ajax applications to be implemented with just a few lines of additional code. |
| 3 | +The hard work is done by the script.aculo.us and Prototype libraries. All the Arc code needs to do is provide the list of autocompletion candidates, and the dynamic page content. |
| 4 | + |
| 5 | +<h3>The example</h3> |
| 6 | +This example implements a simple Ajax input autocompleter and dynamic page update. For the autocompleter, as soon as you enter a few letters into the input, it provides a list of matching terms. As soon as you select a term, the page is dynamically updated with associated data, as in the following screenshot: |
| 7 | +<br> |
| 8 | +<img style="border:1px solid #000000;" src="/ajax/ajax.gif" alt="screenshot"> |
| 9 | +<br> |
| 10 | +For the purposes of the demonstration, the content is information on the |
| 11 | +countries of the world, obtained from the <a |
| 12 | +href="https://www.cia.gov/library/publications/the-world-factbook/">CIA World |
| 13 | +Factbook</a>. |
| 14 | +<p> |
| 15 | +To summarize how it all works: |
| 16 | +When you enter characters in the input field, the script.aculo.us |
| 17 | +autocompleter JavaScript sends the charaters to the Arc server, which returns the |
| 18 | +autocomplete suggestions. The autocompleter JavaScript displays the |
| 19 | +suggestions on the page. |
| 20 | +When you select a country, the updater JavaScript does three separate requests |
| 21 | +to the Arc server, to request the population, area, and capital. When the |
| 22 | +responses arrive, they are inserted into the page. |
| 23 | +<p> |
| 24 | +In more detail, the autocompletion is performed by <a href="http://wiki.script.aculo.us/scriptaculous/show/Ajax.Autocompleter">Ajax.Autocompleter</a>, and the dynamic updating is performed by <a href="http://www.prototypejs.org/api/ajax/updater">Ajax.Updater</a>. The control flow is: |
| 25 | +<ul> |
| 26 | +<li> |
| 27 | +The <code>Ajax.Autocompleter</code> associated with the <code>autocomplete</code> input and <code>autocomplete_choices</code> div sends inputs to <code>/auto_complete?prefix=<i>chars</i></code>. |
| 28 | +<li>The Arc handler sends back a formatted list of autocomplete candidates, e.g. |
| 29 | +<code> |
| 30 | +<ul><li>Iran</li><li>Iraq</li><li>Ireland</li></ul></code>. |
| 31 | +<li>The <code>Ajax.Autocompleter</code> displays the candidates in the <code>autocomplete</code> div. |
| 32 | +<li>When an autocompletion is selected, the <code>update</code> JavaScript function starts three <code>Ajax.Updater</code> instances, which send an Ajax request to URL <code>/getcontents?field=population&name=<i>value</i>, and similarly for area and capital in parallel. |
| 33 | +<li>The Arc handler for <code>getcontents</code> returns the desired contents. |
| 34 | +<li><code>Ajax.Updater</code> puts the contents into the <code>population</code>, <code>area</code>, and <code>capital</code> divs. |
| 35 | +</ul> |
| 36 | +<h3>Running the example</h3> |
| 37 | +First set up the necessary files. |
| 38 | +<ul> |
| 39 | +<li>Download <a href="/ajax/ajax.zip">ajax.zip</a> and uncompress it into the |
| 40 | +same directory that Arc runs in. This zip file provides |
| 41 | +<code>ajax.html</code>, <code>ajax.arc</code>, <code>data.arc</code>, and <code>autocomplete.css</code>. |
| 42 | +<li> Download the script.aculo.us library files from <a href="http://script.aculo.us/downloads">script.aculo.us</a>. Copy the <code>.js</code> files from <code>lib</code> and <code>src</code> into the same directory that Arc runs in. |
| 43 | +</ul> |
| 44 | +Next, start up the Arc interpreter, |
| 45 | +load the <code>ajax.arc</code> file, and start the web server. |
| 46 | +<pre class="arc"> |
| 47 | +arc> (load "ajax.arc") |
| 48 | +arc> (thread (serve 8080)) |
| 49 | +arc> ready to serve port 8080 |
| 50 | +</pre> |
| 51 | +Then go to <a href="http://localhost:8080/ajax.html">http://localhost:8080/ajax.html</a>. (Unfortunately I don't have a live demo on a public machine. If anyone sets one up, let me know and I'll add a link here.) |
| 52 | +<p> |
| 53 | +Start typing in the box, and you should see a dropdown with autocompleted choices. Click one, and the code will be displayed below asynchronously. |
| 54 | + |
| 55 | +<h3>The Arc code</h3> |
| 56 | +The Arc server code is in <a href="/ajax/ajax.arc"><code>ajax.arc</code></a>. Two Arc handlers are implemented to provide the client-side Ajax support. The first, <code>auto_complete</code> receives the current input contents and returns a list of up to 10 autocompletion candidates formatted in HTML. |
| 57 | +The second handler, <code>getcontents</code> returns the dynamic content for the page, from the <code>database</code> table. Note that the handlers do nothing particularly special to make them "Ajax"; they are standard Arc web handlers based on <code>defop</code> and can be accessed directly through the browser. See <a href="http://arcfn.com/doc/srv.html">Arc Web Server</a> for more information on web serving with Arc. |
| 58 | + |
| 59 | +<pre class="code"> |
| 60 | +(defop auto_complete req |
| 61 | + (let prefix (downcase (arg req "prefix")) |
| 62 | + (prn (to-html-list (cut (startselts prefix keylist) 0 10))))) |
| 63 | + |
| 64 | +(defop getcontents req |
| 65 | + (with (field (arg req "field") name (arg req "name")) |
| 66 | + (if (is field "population") (prn ((database name) 1)) |
| 67 | + (is field "area") (prn ((database name) 2)) |
| 68 | + (is field "capital") (prn ((database name) 3))))) |
| 69 | +</pre> |
| 70 | +The handlers use a couple helpers; <code>startselts</code> returns the elements that match the autocomplete prefix and <code>to-html-list</code> wraps the elements in HTML list tags. |
| 71 | +<pre class="code"> |
| 72 | +; Returns a list of elements that start with prefix |
| 73 | +(def startselts (prefix seq) (rem [no (begins (downcase _) prefix)] seq)) |
| 74 | + |
| 75 | +; Wraps elts in HTML list tags |
| 76 | +(def to-html-list (elts) (tostring |
| 77 | + (prall elts "<ul><li>" "</li><li>") |
| 78 | + (pr "</li></ul>"))) |
| 79 | +</pre> |
| 80 | +The actual content is obtained from <a href="/ajax/data.arc"><code>data.arc</code></a>, which contains the information on each country as a list of lists. |
| 81 | +<pre class="code"> |
| 82 | +("United States" "301,139,947" "9,826,630" "Washington, DC") |
| 83 | +</pre> |
| 84 | +Some simple code converts <code>data.arc</code> into a table called <code>database</code> indexed by country for easy lookup, and generates a sorted list of countries for use in autocompletion: |
| 85 | +<pre class="code"> |
| 86 | +(= database (table)) |
| 87 | +(w/infile inf "data.arc" |
| 88 | + (let datalist (sread inf nil) |
| 89 | + (each elt datalist |
| 90 | + (= (database (elt 0)) elt)))) |
| 91 | +(= keylist (mergesort < (keys database))) |
| 92 | +</pre> |
| 93 | +<p> |
| 94 | +For some reason, the default Arc web server <code>srv.arc</code> doesn't support <code>.js</code> files. The <code>ajax.arc</code> file modifies the server slightly to support these files: |
| 95 | +<pre class="code"> |
| 96 | +(= (srv-header* 'text/javascript) |
| 97 | +"HTTP/1.0 200 OK |
| 98 | +Content-Type: text/javascript; charset=UTF-8 |
| 99 | +Connection: close") |
| 100 | + |
| 101 | +(def static-filetype (sym) |
| 102 | + (let fname (string sym) |
| 103 | + (and (~find #\/ fname) |
| 104 | + (case (last (check (tokens fname #\.) ~single)) |
| 105 | + "gif" 'gif |
| 106 | + "jpg" 'jpg |
| 107 | + "css" 'text/css |
| 108 | + "txt" 'text/html |
| 109 | + "html" 'text/html |
| 110 | + "js" 'text/javascript |
| 111 | + )))) |
| 112 | +</pre> |
| 113 | +<h3>Debugging</h3> |
| 114 | +Several things can go wrong when trying to run the example. If the initial page doesn't load at all, something is probably wrong with the Arc server. If no autocompletion happens, the JavaScript may not be loading, or the Arc server may have problems. If the autocompletion shows up as a bulleted list, the CSS file is probably not loading. |
| 115 | +<p> |
| 116 | +The Arc code can be debugged in several ways. The first is to access the handlers directly. The autocomplete handler <a href="http://localhost:8080/auto_complete?prefix=a">http://localhost:8080/auto_complete?prefix=a</a> should return a bullet list of autocomplete suggestions. The contents handler: <a href="http://localhost:8080/getcontents?field=area&name=Algeria">http://localhost:8080/getcontents?field=area&name=Algeria</a> should return the dynamic contents value. |
| 117 | +<p> |
| 118 | +The Arc code can also be debugged by strategically inserting print statements such as: |
| 119 | +<pre class="code"> |
| 120 | +(write req (stderr)) |
| 121 | +</pre> |
| 122 | +This will display the request on the Arc console. |
| 123 | +<p> |
| 124 | +To debug Ajax, you can use Firefox plugins |
| 125 | +<a href="http://livehttpheaders.mozdev.org/">Live HTTP Headers</a> and |
| 126 | +<a href="http://www.getfirebug.com/">Firebug</a>. Live HTTP Headers lets you see the headers between the browser and the server, and is very helpful to determine if something is failing. Firebug allows much more in-depth JavaScript debugging. |
| 127 | + The browser's JavaScript console is also useful to see JavaScript errors. |
| 128 | +<p> |
| 129 | +This article has just scratched the surface of script.aculo.us and Ajax. More information is available at the <a href="http://script.aculo.us/">script.aculo.us</a> website or in <a href="http://www.amazon.com/exec/obidos/ASIN/1934356018/rightocom">books</a>. |
0 commit comments