A few days ago I started learning ClojureScript. I wrote a trivial “hello world” application just to get ClojureScript to compile and execute, and later added some basic jQuery support with jayq.
The time has come to make things a little bit more interesting and add Backbone.js to the mix. I’ve never done ClojureScript or Backbone before, so I’m learning them at the same time with an interesting learning curve.
Anyway, I managed to rewrite the first two examples from Backbone docs to pure CLJS. I made some minor modifications like triggering events on button click and changing main background instead of sidebar.
Here’s my page source (with Hiccup):
(hp/html5 [:head] [:body [:button#clickable-event "Click to trigger an alert from basic Backbone event"] [:button#clickable-color "Click to change background color"] (hp/include-js "http://code.jquery.com/jquery-1.8.2.min.js" "http://underscorejs.org/underscore.js" "http://backbonejs.org/backbone.js" "js/cljs.js") ])
As you can see, it renders a very basic page with two buttons and includes a few JS libraries.
And here’s the CLJS file mixing jQuery and Backbone:
(ns hello-clojurescript (:use [jayq.core :only [$]]) (:require [jayq.core :as jq])) ; ALERT ON CLICK ; Rewrite of http://backbonejs.org/#Events (def o {}) (.extend js/_ o Backbone.Events) (.on o "alert" (fn [msg] (js/alert msg))) (jq/bind ($ "#clickable-event") :click (fn [e] (.trigger o "alert" "Hello Backbone!"))) ; MODEL WITH COLOR CHOOSER ; Inspired by http://backbonejs.org/#Model but without sidebar (def MyModel (.extend Backbone.Model (js-obj "promptColor" (fn [] (let [ css-color (js/prompt "Please enter a CSS color:")] (this-as this (.set this (js-obj "color" css-color)))))))) (def my-model (MyModel.)) (.on my-model "change:color" (fn [model color] (jq/css ($ "body") {:background color}))) (jq/bind ($ "#clickable-color") :click (fn [e] (.promptColor my-model)))
There’s a number of new things (to me) and nonobvious pitfalls. View this side-by-side with Backbone demos, and note:
- To invoke
_.extend(o, Backbone.Events)
, do(.extend js/_ o Backbone.Events)
. ClojureScript will correctly transform(.extend js/_ ...)
to_.extend(...)
, and it will copyBackbone.Events
as is (no quoting necessary) - To distinguish between objects and functions defined elsewhere and in CLJS, always prefix the former with
js/name
. Works foralert
, underscore etc. - I had an issue with passing objects (as maps) directly to calls like
Backbone.Model.extend()
. Tried things like{:promptColor fn}
and{"promptColor" fn}
to no avail. I finally discovered(js-obj)
and it did the trick, but it’s pretty cumbersome. I wonder if there’s a better way. - You need some extra work to use
this
. It has to be bound to a Clojure symbol withthis-as
macro. - On a slightly related note, I really begin to love jayq. In this example I use bare Backbone directly and struggle, and really appreciate jayq bridging the gap to jQuery. I wonder if there is a CLJS wrapper for Backbone.
All in all, it’s an interesting exercise. Just the right learning curve – stimulating, but not discouraging, regularly providing visible feedback.
As usually, complete source is at GitHub. I created a new repository for it, to keep “hello ClojureScript” as small as possible. This new demo probably will grow as I learn more Backbone.
If you write a few macros you can make the code much prettier. I wrote some macros that for instance automatically bind the this attribute, converts clojure maps to JS objects and destructures the model attributes, see:
https://github.com/carneades/carneades/blob/master/src/PolicyModellingTool/src-cljs/catb/views/sct/claim_editor.cljs#L58
See https://github.com/carneades/carneades/tree/master/src/PolicyModellingTool/src-cljs/catb/backbone for the code.
You should look at http://rhysbrettbowen.github.com/PlastronJS/ it is a google closure backbone look alike with some interesting ideas.
@pa – thanks. I thought about wrapping a Backbone wrapper similar to what jayq does for jQuery. I know that could be a lot better with macros, I’ve been wondering about taking it one step further and adding a clojure feel to it (more funtional, hide the OO stuff).
@Tyler Tallman – I took a glimpse, but part of this exercise was to learn Backbone itself (for the sake of it). You know, just because it’s so popular and there’s no harm learning something like that.
As somebody just starting to learn LISP and wondering I can apply these new concepts to my every day work, this is really encouraging. I’m glad Clojurescript is so viable for web development and will definitely be giving it a try sometime. Thanks for posting!