d3 JavaScript translation to ClojureScript

323 Views Asked by At

I want to translate the following JavaScript into ClojureScript:

var myScale = d3.scaleLinear()
  .domain([0, 10])
  .range([0, 600]);

After creating this function you ought to be able to call it with a number:

myScale(3);   // returns 180 

My attempt is as follows:

(:require ["d3-scale" :refer (scaleLinear)])

(let [f (-> scaleLinear
            (. (domain (clj->js [0 10])))
            (. (range (clj->js [0 600]))))]
      (f 3))

Gives:

react-dom.development.js:16545 Uncaught TypeError: module$node_modules$d3_scale$dist$d3_scale.scaleLinear.domain is not a function

2

There are 2 best solutions below

0
On BEST ANSWER

scaleLinear is a function that can be called from d3. It is not something that ought to be constructed. So this is a better answer:

(-> d3
    (.scaleLinear)
    (.domain #js [min-y max-y])
    (.range #js [1 0]))

I had trouble with the requires, so here are the relevant ones:

["d3" :as d3]
["d3-scale" :refer (scaleLinear)]

This is using shadow-cljs rather than cljsjs.

The trouble with requiring "d3" is that you pull the whole library in. So here's another approach:

(-> (scaleLinear)
    (.domain #js [min-y max-y])
    (.range #js [1 0]))

It seems that whenever you are requiring something you can use it directly as it was required, and forget the interop dot.

Also (:require ["d3" :as d3]) means that you pull d3 into the local scope. js/d3 is the global scope, so just use d3 if that's what you have required. i.e. js is another 'interop' thing you can forget about.

On the other hand domain and range were not required, so interop rules need to be observed.

In conclusion - the main problem solved is that scaleLinear is a function and as such actually needs to be called!

This question was asked at the beginning of an exploration into working with d3 through React Vis - see https://medium.com/@cjmurphy/using-react-vis-from-clojurescript-787d02281f7c

0
On

With some trial and error I did get it to work. These are all equivalent:

(-> (new scaleLinear)
    (.domain (clj->js [0 10]))
    (.range (clj->js [0 600])))

(-> (new scaleLinear)
    (. (domain (clj->js [0 10])))
    (. (range (clj->js [0 600]))))

(-> (scaleLinear.)
    (.domain #js [0 10])
    (.range #js [0 600]))