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.