Writing AngularJS with ClojureScript and Purnam

I’ve got a project that I’ve been using to learn Clojure.  For the front end of this project, I wanted to use ClojureScript and AngularJS so I could share code between and my server and client (and have another avenue for learning Clojure), and also because Angular is totally awesome. Hunting around for details on how to integrate AngularJS with ClojureScript, I did find a few articles, but eventually I came across the library Purnam, and I knew I had a winner. Purnam is a few things wrapped up into one:

  1. A very nice extra layer for JavaScript interoperability above and beyond what ClojureScript gives you out of the box
  2. Both a Jasmine and Midje style test framework, with integration into the great Karma test runner
  3. ClojureScript implementations of AngularJS directives, controllers, services, etcetera which greatly reduce the amount of boilerplate code you would otherwise need
  4. ClojureScript implementations that make testing of AngularJS directives, controllers, services, etcetera a breeze to set up under the above test framework.
  5. Fantastic documentation.

I could go on in detail on each of these topics, but it’s covered very well in the documentation. Instead I’ll expand on how easy it was to get up and running with a simple AngularJS module and controller configuration, and also testing it with Jasmine and Karma to give you a taste of what Purnam can do for you.

To create an AngularJS controller, we can define it like so (I’ve already defined my module “chaperone.app” in chaperone.ng.core):

This shows off a few things:

  • def.controller : This is the Purnam macro for defining an AngularJS controller. Here I’ve created the controller AdminUserControl in the module chaperone.app.
  • You will notice the use of !. This is a Purnam construct that allows you to set a value to a Javascript property, but allows you to define that property with a dot syntax, as opposed to having to use something like (set! (.-init $scope) (fn [] ... ))  or (aset $scope "init" (fn [] ...))  which, you may not prefer.  Purnam has quite a few constructs that allow you to use dot notation with JavaScript interoperability, above and beyond this one.  I personally prefer the Purnam syntax, but you can always choose not to use it.

One thing I discovered very quickly, I needed to make sure I required chaperone.ng.core which contains my Angular module definition, in the above controller’s namespace, even though it is not actually used in the code.  This was so that the Angular module definition would show up before the controller definition in the final JavaScript output. Otherwise, Angular would throw an error because it could not find the module, as it had yet to be defined.

Purnam also makes it easy to run AngularJS unit tests. Here is a simple test I wrote to test a $scope value that should have been set after I ran the init function on my AdminUserCtrl controller

As you can see, Purnam takes away a lot of the usual Jasmine + AngularJS boilerplate code, and you end up with a nice, clean way to write AngularJS tests. Purnam also implicitly injects into your tests it’s ability to interpret dot notation on JavasScript objects for you, which is handy if you want to use it.

Purnam also has capabilities to test out Services, Directives and other aspects of the AngularJS ecosystem as well, though it’s describe.ng macro, which gives you full control over what Angular elements are created through Angular’s dependency injection capabilities.

Finally, Purnam integrates into Karma, which lets you run your tests in almost any javascript environment, be it NodeJS or inside a Chrome web browser.

Configuring Karma is as simple as running the standard karma init command, which asks you a series of questions about what test framework you want (Jasmine) and what platform you want to test on (Chrome for me), and results in a karma.conf.js file in the root of your directory.

One thing I found quickly when setting up my karma.conf.js file was that it was very necessary to specify which file you want included, and in what order. For example:

In my initial setup I had simply used a glob of test/*.js, which caused me all sorts of grief as angular-mocks.js needed to be loaded after angular.min.js (for example), but the resultant file list didn’t come out that way, and I got all sorts of very strange errors. Specifying exactly which files I needed to be using and in the correct order fixed all those issues for me.

I’m really enjoying working with the combination of ClojureScript and AngularJS, and Purnam gives me the glue to hook it all up together without getting in my way at all. Hopefully that gives you enough of a taste of Purnam to get your interest piqued, and you’ll take it for a spin as well!

Leave a Comment