Pages

Thursday, March 6, 2014

Getting up and Running - ClojureScript + Node-Webkit

To better facilitate my learning of Clojure, I've decided to write a simple desktop application using ClojureScript and Node-Webkit. I haven't seriously touched javascript in years, I've forgotten all of my CSS, and I don't know Clojure. So, this will probably be quite embarrassing for both author and reader. Any help or comments from those with expertise is welcomed and encouraged.

In this post we'll create a simple application with two-way communication between ClojureScript and a simple html page. The ClojureScript will populate a <div> element with some content, listen for click events on an <input> element, and extract the path from a Node-Webkit file chooser element. Nothing sensational, but enough to get us to the point where we have a sandbox for playing around with ClojureScript.


Let's get started:

As a first step, you'll need Leiningen; a Clojure project tool which does all sorts of wonderful things for you like managing package dependencies, building your project, compiling Clojure to ClojureScript, running unit tests, etc.

I've opted to install Leiningen using Homebrew. Once Homebrew is installed, enter the following in a terminal window:

> brew install leiningen

That should work, and you should be presented with a little beer in your terminal. If everything went according to plan, typing:

> lein

at the terminal should bring up usage information for running Leiningen. If you've gotten this far, Leiningen is properly installed.

Next, create an empty directory somewhere, and within that directory create a new project with Leiningen using the following command:

> lein new clojurescript_app

Leiningen will create a new directory named 'clojurescript_app', which contains our Clojure project. The project name 'clojurescript_app' is arbitrarily chosen, but you might want to stick with it to make sure the following code snippets work without modification. Next, cd into the newly created project directory and create two new directories:

> mkdir src/clj
> mkdir src/cljs 

The first directory (/clj) will contain your Clojure code, and the second (/cljs) will contain your ClojureScript code (Clojure code that will eventually be compiled to Javascript). Move the core.clj file from src/clojurescript_app to /clj, and feel free to delete the clojurescript_app folder within /src.

Next, open up your text editor of choice and edit the project.clj file in the root directory of your project. We'll need to enable cljsbuild for our project, and we'll do that by editing the project.clj file to look like this:



If you're doing this in the future, you might want to make sure that the version numbers for Clojure, Clojurescript, and lein-cljsbuild are up to date. The important point here, is that we are telling lein-cljsbuild to take the Clojure code in src/cljs and compile it to a javascript file resources/main.js which will be included by our html in a <script> element.

Now, we need to create an html page to act as the application GUI. Create an index.html file in the /resources directory and fill it with the following content:



We've added three important elements here: A <div> which will be filled with text from ClojureScript, a button which we will listen to from ClojureScript, and a file chooser which will be interrogated from ClojureScript to collect a local file path chosen by the user.

Now it's time to actually add some ClojureScript code. Create a file named main.cljs in the src/cljs folder and include the following code:



At the top of the file, we define a namespace for the file using the ns macro. The name clojurescript_app.core is more or less arbitrarily chosen. The next two lines include the Google Closure libraries for manipulating and listening to dom elements in our index.html. The :as statement gives us a more convenient way to refer to the package, instead of always having to use the fully qualified name.

The next statement in the ClojureScript file is a function call to goog.dom's setTextContent method. The first argument is the element with id fillMeFromClojureScript from our index.html, and the second argument is simply some text to populate the div content with. When we load up our index.html, the div content should be set to "Hello From ClojureScript!", proving that the ClojureScript is correctly compiled and included via the <script> tag in our index.html.

The next statement sets up an event listener on the button element in our html page. On a click event, the js/alert function will be called, displaying "You Clicked!" in a browser alert window. Not exciting, but it proves that we can listen to events in the dom.

The final statement sets up an event listener which will listen for a change event on our fileDialog element and display an alert window with the chosen path whenever the fileDialog is invoked.  Note the syntax for accessing a property of an object in the last line - it's a bit strange looking. The '-value' indicates that we want to access a property, and not a method of the element (as opposed to using 'value').

We're now ready to run our app for the first time. In the same directory as our project.clj file, run the following command in the terminal:

> lein cljsbuild once

With any luck, you should see something like the following:

> Successfully compiled "resources/main.js" in 0.558 seconds.


At this point, we've compiled our Clojure code into javascript, and Leiningen has placed the output javascript file in /resources/main.js. Everything is now in place, and we can run our application by opening the index.html in a web browser. With any luck, you should see something like this:


We can already tell that the ClojureScript is properly included and interacting with the dom, since our function has successfully appended the text "Hello From ClojureScript!" into the target <div>. Clicking the "Click Me!" button should display an alert, proving that event listening is working from ClojureScript. The file chooser however, is a different matter. If we click on the "Choose File" button, we will be unable to see the full path to the locally selected file. You should see something like the following:



Chrome has helpfully obfuscated the path in the interest of our privacy, but when developing some sort of desktop application we would like to be able to retrieve the fully qualified local path. This is where Node-Webkit comes into play. Hop over to https://github.com/rogerwang/node-webkit and download the node-webkit application. Create a new file named package.json in /resources, and fill it with the following:

Now, run the following at the terminal:

> [path_to_node_webkit_app]/Contents/MacOS/node-webkit [path_to_your_resources_dir]

Your app should now open up in a Node-Webkit browser window, and when you click on the "Choose File" button Node-Webkit will helpfully allow you to access the full local path to your selected file:





We now have a little sandbox for playing with ClojureScript, and I would suggest using the Leiningen auto build mode to speed things up a bit:

> lein cljsbuild auto

This will automatically rebuild your ClojureScript project whenever you modify any files in the project.

In the next post we'll look into using ReactJS bindings with Om as opposed to fiddling around with the dom manipulations using goog.dom and goog.events.