Quiescent and Highcharts


How to glue them together

I recently rewrote the front-end of a side-project from JavaScript and React to ClojureScript and Quiescent. In that effort I needed to chart some data. I could not find a ClojureScript charting library that I was happy with, so I decided to keep using HighCharts for the moment.

Add Highcharts to project

If you want to use a JavaScript library in a ClojureScript project, the easiest way is to go with CLJSJS. However, not every library is packaged up by CLJSJS. So in order to use Highcharts you have to put a little more effort into it.

Here is how you include it in your Leiningen project.clj

 :compiler {
   ;; Highrise standalone-framework makes Highrise work without jQuery
   :foreign-libs [{:file "lib/standalone-framework.src.js" 
                   :provides ["Standalone"]}
                  {:file "lib/highcharts.src.js" 
                   :provides ["Highcharts"] 
                   :requires ["Standalone"]}]}}]}

Render a graph

To render a graph component with Quiescent, you have to use one of the React lifecycle hooks that Quiescent provides. In addition, you have to tell Highcharts which DOM-element to use for the graph. I banged my head against the wall trying to get the graph to render by passing a DOM element string id to Highcarts. The trick was passing an actual DOM element reference instead.

(q/defcomponent MyGraph

  ;; Quiescent will call the :on-render function every time the DOM is updated. 
  ;; It is equivalent to 'componentDidMount' and 'componentDidUpdate' in React 
  :on-render (fn [dom-node component-value]
              ;; Instantiate a new chart by targeting the dom-node
              (new js/Highcharts.Chart
                   ;; Deep-convert the clojure data structure into a JavaScript 
                   ;; object that Highcharts.Chart takes as input                  
                     ;; Need to pass the dom-node reference to :renderTo
                     {:chart {:type    "column"                       
                              :renderTo dom-node}
                      ;; Just some bogus data
                      :series [{:name "Monkey",
                                :data [1, 0, 4]}, 
                               {:name "Giraffe",
                                :data [5, 7, 3]}]})))
  ;; This div will be the target of the graphs :renderTo  
  (d/div {}))

You can find a complete example in my Cashflow repo.