CloServe A view first web framework in Clojure

Ajax and Comet

Ajax form and Comet example

The "Ajax form" in closerve is in fact implemented with websocket, but we keep the lift:form.ajax tag to have better prtability of HTML code.

Here is an example of using form.ajax and comet to make a very simple chat app.

First, define some data structures for bookkeeping:

(def chat-chans (atom #{}))
(def chat-msgs (atom []))

Then we need a processing fuction for ajax.form which takes user input:

 [context cmd]
 (let [chat-msg (cmd "chatmsg")]
   (if (string? chat-msg)     
       (swap! chat-msgs #(apply vector (drop (- (count %1) 9)
                                                  (conj %1 %2))) chat-msg)
       (send-cmd-to-page (:page-id context) 
                         {:act :reset 
                          (str "#" (:form-id context))})

Distribute the input msg to all comet channels, kind of like the comet actor server in Lift:

(add-watch chat-msgs :watch-chat-msgs
           (fn [key aref old-val new-val]
             (if new-val 
               (doseq [c @chat-chans]
                 (go (>! c (last new-val)))))))

Now the comet actor which read input from its channel and send update to browser:

 [ch req page-id uuid]
 (swap! chat-chans conj ch)
 (loop [msg (<! ch)]
   (when msg 
     (send-cmd-to-page page-id 
                       {:act :append
                        :selector "#msglist"
                        :html (hickory-to-html {:type :element, :attrs nil
                                                :tag :li :content [msg]}) })
     (recur (<! ch))))
 (prn "chat channel is closed")
 (swap! chat-chans disj ch)

The corresponding HTML code:

<ul class="lift:comet?type=chat" id="msglist"></ul>

<form class="input-group lift:form.ajax?callback=ChatInput">
      <input name="chatmsg" type="text" class="form-control">
      <span class="input-group-btn">
        <input value="Go!" class="btn btn-default" type="submit">

Try it here: