clojurescript

ClojureScript, a dialect of Clojure that compiles to JavaScript http://clojurescript.org | Currently at 1.10.879
macrobartfast 2021-05-13T01:10:12.385300Z

I’ve created a project with create-cljs-app, which is awesome. However, now I’ve enabled the server option in shadow-cljs.edn, which allows me to add routes, and I want to send data from the front-end to the back. To send a map, I would have to turn it into a string or JSON, right? Two questions (I think): what format is best to send and receive it in? And, assuming I’m right in that I have to wrap the Ring or  Compojure routes, how and where do I do this in an app created with create-cljs-app? I don’t have a familiar instance to wrap, as create-cljs-app is a little different than what I set up myself. In my shadow-cljs-edn I have:

:dev-http {8080 {:root "public"
                  :handler app.handler/handler}}
and my handler ns requires are:
(ns app.handler
  (:require [crux.api :as crux]
            [compojure.core :refer :all]
            [compojure.route :as route]
            [muuntaja.middleware :as mw]
            ;; [ring.middleware.json :as json]
            [<http://clojure.java.io|clojure.java.io> :as io]))
and my handler is
(defroutes handler
  (GET "/about" [] "&lt;h1&gt;Hello World&lt;/h1&gt;")
  ;; (POST "/test" req (prn (read (:body req))))
  (POST "/test" req (:body req))
  (route/not-found "&lt;h1&gt;Page not found&lt;/h1&gt;"))
which returns the request at /test. I need to be able to send a map from the front end and send it off to Crux. I’m open to total changes in approach… whatever I have here is just from my thrashing along. I’m using cljs-http to send from the front end with a post.

macrobartfast 2021-05-13T01:24:20.386200Z

I realize this could just as easily be a question for #clojure, but as the app is a create-cljs-app one, and involves sending from the front end, I decided to post it here.

thheller 2021-05-13T07:12:14.387400Z

@macrobartfast I stronly recommend developing your CLJ stuff independently of shadow-cljs. do NOT use :dev-http for this, much better to have a custom server you are in full control over. just using clj -m your.server or so to run it. as for transport format I'd recommend using transit-clj+transit-cljs

thheller 2021-05-13T07:12:40.387900Z

I don't like or use compojure so can't answer questions regarding that

macrobartfast 2021-05-13T08:04:53.392300Z

Ah, ok! Running a separate server is my usual desire but I always run into CORS issues developing locally, where I spend 99% of my time. Do you have any strategies for doing that? I’m on OSX/Chrome here. This is a hurdle I need to overcome, for sure, and I would really like to have separate clj.

macrobartfast 2021-05-13T08:06:29.393500Z

I don’t know what my problem is with CORS for dev… it’s one of those simple things I never worked out.

thheller 2021-05-13T08:11:19.394400Z

there are no CORS issues. just have your regular webserver serve the static files shadow-cljs generates. the REPL and hot-reload run over a separate websocket talking directly to the shadow-cljs server

thheller 2021-05-13T08:11:53.394700Z

don't need a :dev-http at all when you have your own server anyways

macrobartfast 2021-05-13T08:15:20.395Z

ah, ok… I think I remember running into issues POSTing and GETing from my front end on localhost:port-1 to the server on localhost:port-2… but I’m not sure. I’ll be switching to separating my clj code independently and I’ll message here again if I run into those issues.

thheller 2021-05-13T08:16:07.395300Z

yes if you run requests between multiple servers that'll have CORS issue

macrobartfast 2021-05-13T08:17:14.395500Z

oh, doh… I totally just understood the matter. gotcha.

thheller 2021-05-13T08:17:15.395700Z

but you don't have to use multiple servers

macrobartfast 2021-05-13T08:17:32.395900Z

right… because it’s serving the static file up.

macrobartfast 2021-05-13T08:17:59.396100Z

that’s what I wasn’t doing right… typically had two servers, for whatever reason.

thheller 2021-05-13T08:18:04.396300Z

yeah, just .js files as far as your server is concerned. the dynamic stuff is handled elsewhere, not a concern for your server.

macrobartfast 2021-05-13T08:18:47.396600Z

last question (if you have time)… how do you usually direct your server to the static compiled site locally… a symlink… an absolute file path… or?

macrobartfast 2021-05-13T08:19:15.396800Z

I assume shadow and your server are in different projects/directories, right?

thheller 2021-05-13T08:19:18.397Z

when you use ring ring.middleware.file or ring.middleware.resource

thheller 2021-05-13T08:19:33.397200Z

no, I have everything in one project/repo

macrobartfast 2021-05-13T08:20:06.397400Z

ah, ok… I didn’t know about the ring.middleware options.

thheller 2021-05-13T08:20:08.397600Z

just running the code separately, code is still together completely. doesn't have to be if you prefer that

meb 2021-05-13T08:34:58.400200Z

Hello. I’m looking for suggestions for a library, set of components or framework to implement a UI to let a user describe a workflow using drag and drop (dnd) of boxes and letting her connect them with arrows/connectors. Any idea?

macrobartfast 2021-05-13T08:39:13.400300Z

I am so sorry to be so dense… when you create your server and your shadow in the same parent/project directory, I’m assuming they are each in their own subdirectories, right?

p-himik 2021-05-13T08:52:48.400700Z

A long time ago, I've added these to my notes: - https://www.totaljs.com/flow/ - https://nodered.org/

meb 2021-05-13T09:17:37.400900Z

I’m looking at React Flow.

meb 2021-05-13T09:18:07.401100Z

Node Red seems pretty heavy.

meb 2021-05-13T09:19:16.401300Z

For info, our UI is built with re-frame and react semantic-ui

p-himik 2021-05-13T09:28:06.401500Z

Node Red is under a permissive license so you can extract whatever you need.

thheller 2021-05-13T09:52:25.403600Z

well, I use a separate namespace yes but everything sits in src/main

thheller 2021-05-13T09:52:49.404200Z

so I'll have a src/main/foo/bar/server.clj and src/main/foo/bar/frontend.cljs or so

Pepijn de Vos 2021-05-13T09:53:10.404600Z

What would be the recommended approach for adding a js dep thats not on cljsjs yet? Can you easily contribute a new pkg to cljsjs or people just use npm?

thheller 2021-05-13T09:53:13.404700Z

that really is completely your choice how you want to organize your code

thheller 2021-05-13T09:58:52.405200Z

shadow-cljs users just use npm, for figwheel (or just cljs.main) you can use :target :bundle and use npm+webpack

thheller 2021-05-13T09:59:35.405900Z

but if you start using npm you'll have to remove all cljsjs dependencies. they don't mix well and you'd quite often end up with duplicated dependencies

Pepijn de Vos 2021-05-13T10:12:19.406900Z

Hm interesting

Pepijn de Vos 2021-05-13T10:15:52.409100Z

are there any significant differences between figwheel and shadow-cljs? I remember figwheel is what was around a couple of years ago. I think I kinda like to avoid webpack and all that, but maybe that's futile once you start to use some JS things.

thheller 2021-05-13T10:17:27.411200Z

well the npm support is a significant difference. otherwise shadow-cljs also supports hot-reload and REPL. as far as CLJS code is concerned they are identical, still just the regular cljs compiler under the hood

Pepijn de Vos 2021-05-13T10:18:48.411800Z

So figwheel is more cljs centered where shadow is more npm centered?

thheller 2021-05-13T10:19:22.412900Z

no, you can use shadow completely without npm too

thheller 2021-05-13T10:20:01.413500Z

I'd say shadow is more of a complete all-in-one tool. covers every CLJS-related build aspect

thheller 2021-05-13T10:20:21.413900Z

figwheel and cljs.main are sort of more build-your-own kind of deal

Pepijn de Vos 2021-05-13T10:22:53.414600Z

One of the things I want to use is Jupyter Lab, which requires a Python backend service, so it'll be a fun challenge to try and rig the development hot reload stuff together with a python server. In that sense build your own sounds like it might handle that better.

thheller 2021-05-13T10:23:30.415Z

both should be fine with that. hot-reload and REPL really aren't coupled to the http server you use

thheller 2021-05-13T10:24:18.415700Z

don't know what the jupyter JS code looks like but if its on npm then shadow (or :target :bundle) should be fine with it

Pepijn de Vos 2021-05-13T10:25:01.416300Z

Yea it's npm, but not on cljsjs

Endre Bakken Stovner 2021-05-13T13:53:16.416500Z

This is supposed to select an element by id and apply a different style to it:

(defn page []
    (let [_ (-&gt; (d3/select "#test")
                (.style "color" "green"))]
    [:div [:h2#test "whoooo"]]))
Problem is, it does not work. There are no errors in the developer console. Do I need to create the HTML before setting the style? Or what might be the reason for it failing? (This is just a simple example to understand a problem in a much bigger function. I know that is not the best way to set the style for an element.)

p-himik 2021-05-13T14:16:13.416700Z

The Hiccup that this function returns turns into a DOM node waaaay down the line. Don't use IDs with React when the API that you need accepts DOM nodes directly - just use React refs.

Endre Bakken Stovner 2021-05-13T14:16:39.416900Z

Thanks! Perhaps that was more of a #beginners question 🙂

Endre Bakken Stovner 2021-05-13T14:18:28.417200Z

I guess this is what you mean? https://gist.github.com/pesterhazy/4d9df2edc303e5706d547aeabe0e17e1

p-himik 2021-05-13T14:24:00.417400Z

You don't need a form-3 component. You can use a form-2 component or reagent.core/with-let.

p-himik 2021-05-13T14:24:13.417600Z

But overall - yes, that's what I meant.

🙏 1
p-himik 2021-05-13T14:25:06.417900Z

Note that when you use a form-2 component or with-let, you should access that node directly in the function that sets the ref. So you won't have any atom that stores the ref - you will just use it immediately.

Endre Bakken Stovner 2021-05-13T14:47:07.418200Z

This still just leaves me with an empty &lt;svg&gt;&lt;/svg&gt;. Do you see anything obviously wrong here or might my problem lie elsewhere?

;; trying to do something like <https://dagrejs.github.io/project/dagre-d3/latest/demo/sentence-tokenization.html>
(defn graph-page []
  (let [graph (r/atom
               (-&gt; (dagre/graphlib.Graph.)
                   (.setGraph (clj-&gt;js {}))
                   (.setDefaultEdgeLabel (fn [] {}))
                   (.setNode "hi" (clj-&gt;js {:label "hi" :width 144 :height 100}))
                   (.setNode "ciao" (clj-&gt;js {:label "ciao" :width 144 :height 100}))
                   (.setEdge "hi" "ciao")))]
    (fn []
      (let [renderer (dagre-d3/render)
            svg (d3/select "svg")
            inner (-&gt; svg
                      (.append "g"))
            _ (renderer inner @graph)]
        [:div
         [:h2#test "Mounting?"]
         [:svg]]))))

p-himik 2021-05-13T14:48:02.418400Z

It's the same exact mistake as before.

p-himik 2021-05-13T14:48:19.418600Z

When you're calling d3/select above, that svg element is not rendered yet.

Endre Bakken Stovner 2021-05-13T14:49:08.418900Z

How do I fix it? Should the outermost function create the hiccup and the innermost modify it? Seems backwards but...

p-himik 2021-05-13T14:50:26.419100Z

As I said the first time - use React refs. The gist you yourself linked uses refs.

Endre Bakken Stovner 2021-05-13T14:52:17.419300Z

I guess I looked up what a https://purelyfunctional.tv/guide/reagent/#form-2 looked like, but not in context of a react-ref 😳

thheller 2021-05-13T15:00:27.419600Z

if you want to mix d3 with react you'll need to use refs

thheller 2021-05-13T15:01:10.419800Z

or you can use select but you'll need to use it in the proper lifecycle stages

Endre Bakken Stovner 2021-05-13T15:07:21.420Z

Thanks! I think this post covers the relevant material: https://presumably.de/reagent-mysteries-part-3-manipulating-the-dom.html

thheller 2021-05-13T15:08:41.420200Z

the react lifecycle would be componentDidMount or componentDidUpdate

thheller 2021-05-13T15:09:07.420400Z

or nowadays the modern variant useEffect https://reactjs.org/docs/hooks-effect.html

Endre Bakken Stovner 2021-05-13T15:21:39.420900Z

Thanks! useEffect looks especially neat. But come to think of it, the data I want to display are from a re-frame sub so by the time I get it from there, the elements will have rendered.

thheller 2021-05-13T15:24:25.421200Z

thats what you want. have react render the DOM element and then d3 enhance it

km 2021-05-13T15:53:33.423900Z

Anyone have experience with a good org-mode parser? https://github.com/200ok-ch/org-parser seems promising, but still https://github.com/200ok-ch/org-parser/issues/30 on links and some other things. Also tried a few https://www.npmjs.com/package/orga https://www.npmjs.com/package/org (via shadow-cljs) but they both failed with the same error: too much recursion

Pepijn de Vos 2021-05-13T18:53:01.425300Z

I'm kinda struggling using a React component in Reagent 😞

Pepijn de Vos 2021-05-13T18:54:45.426800Z

I managed to import the JS library with ["react-plotly.js" :refer [default] :rename {default PlotComponent}] and then somewhere I saw an example where they defined (defn plot [] PlotComponent) and then just used it as an element like [plot {:data ...}]

Pepijn de Vos 2021-05-13T18:56:46.427400Z

But obviously that's not correct and I can;t find the example anymore

Uncaught TypeError: Cannot call a class as a function

Pepijn de Vos 2021-05-13T19:01:16.427800Z

omg yay

flowthing 2021-05-13T19:05:03.428Z

Also, I'm not well-versed in the intricacies of requiring things from the JS world so I'm not sure whether this is actually useful in your case, but if you're using a new enough version of ClojureScript (and possibly shadow-cljs), it might be: https://clojurescript.org/news/news#_library_property_namespaces

dnolen 2021-05-13T19:10:37.428800Z

raw React Native components have to be adapted

macrobartfast 2021-05-13T19:35:57.429500Z

:thumbsup: that is actually an improvement over what I would have done; I didn’t realize I could run the server and shadow off of the same src/main… thanks!