A while ago I began to play with ClojureScript and tried to get it to work with popular frameworks. I played with a few of them, most recently with Knockout.js. This post sums up those efforts and my not-so-optimistic view on ClojureScript.

I tried “bare” jQuery. It was pretty smooth.

I tried Backbone.js. I got it to work on a simple example, though one reader on Twitter rightfully commented that ClojureScript was hideous. Yes, that Backbone example is hideous. Later on I tried to do something less trivial. Eventually I fled in horror, thanks to impedance mismatch between heavily OO Backbone and non-OO ClojureScript sauced with my ignorance in CLJS (and Backbone).

I also gave Angular.js a shot. It started really smooth, because Angular proudly states that it doesn’t rely on object-oriented programming so much. It was great. Right to the moment when I started arguing with the compiler renaming my variables, soon followed by discovery that Angular and Closure are no go.

So, the time has come to another experiment – this time Knockout.js. I followed the official tutorial and here is what I eventually came up with.

The Page

The complete page in Hiccup looks like this. Nothing particularly exciting here.

(defn render-body [] (hp/html5 [:head] [:body [:p "First name: " [:strong {:data-bind "text: firstName"} "todo"]] [:p "Last name: " [:strong {:data-bind "text: lastName"} "todo"]] [:p "Full name: " [:strong {:data-bind "text: fullName"} "todo"]] [:p "First name: " [:input {:data-bind "value: firstName"}]] [:p "Last name: " [:input {:data-bind "value: lastName"}]] [:button {:data-bind "click: capitalizeLastName"} "Go caps"] (hp/include-js "//ajax.aspnetcdn.com/ajax/knockout/knockout-2.1.0.js" "js/cljs.js") (hp/include-css "css/todo.css") ]))

ClojureScript

The most interesting part is the ClojureScript code. Here’s one way to do it:

(ns hello-clojurescript) (defn app-view-model [] (this-as this (set! (.-firstName this) (.observable js/ko "Bert")) (set! (.-lastName this) (.observable js/ko "Bertington")) (set! (.-fullName this) (.computed js/ko (fn [] (str (.firstName this) " " (.lastName this))) this)) (set! (.-capitalizeLastName this) (fn [] (.lastName this (-> this .lastName .toUpperCase))))) nil ) (.applyBindings js/ko (app-view-model.))

Yes, I do need to explicitly return “nil” there. Otherwise it returns this.fullName = ... , and that breaks KO.

It works, but it’s hard to defend it in comparison to the JS equivalent:

function AppViewModel() { this.firstName = ko.observable("Bert"); this.lastName = ko.observable("Bertington"); this.fullName = ko.computed(function() { return this.firstName() + " " + this.lastName(); }, this); this.capitalizeLastName = function() { var currentVal = this.lastName(); this.lastName(currentVal.toUpperCase()); }; } ko.applyBindings(new AppViewModel());

Complete code can be found at my GitHub repository.

Better Way – Macro

This code can be made a lot better with a custom macro, as the one presented at StackOverflow:

(defvar name_model first_name (observable "My") last_name (observable "Name") name (computed (fn [] (str (. this first_name) " " (. this last_name))))) (. js/ko (applyBindings name_model));

Now, that would be something!

… except for that defining macros in ClojureScript is harder than in plain Clojure, to the point that I haven’t gotten it to work yet.

Conclusions on ClojureScript

I spent quite a few hours poking at ClojureScript, and I have mixed feelings.