Fork me on GitHub
#clojurescript
<
2021-05-13
>
macrobartfast01:05:12

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]
            [ :as io]))
and my handler is
(defroutes handler
  (GET "/about" [] "<h1>Hello World</h1>")
  ;; (POST "/test" req (prn (read (:body req))))
  (POST "/test" req (:body req))
  (route/not-found "<h1>Page not found</h1>"))
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.

macrobartfast01:05:20

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.

thheller07:05:14

@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

thheller07:05:40

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

macrobartfast08:05:53

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.

macrobartfast08:05:29

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

thheller08:05:19

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

macrobartfast08:05:20

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.

thheller08:05:07

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

macrobartfast08:05:14

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

thheller08:05:15

but you don't have to use multiple servers

macrobartfast08:05:32

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

macrobartfast08:05:59

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

thheller08:05:04

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

macrobartfast08:05:47

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?

macrobartfast08:05:15

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

thheller08:05:18

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

thheller08:05:33

no, I have everything in one project/repo

macrobartfast08:05:06

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

thheller08:05:08

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

macrobartfast08:05:13

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?

thheller09:05:25

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

thheller09:05:49

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

thheller09:05:13

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

macrobartfast19:05:57

: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!

thheller08:05:53

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

meb08:05:58

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?

p-himik08:05:48

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

meb09:05:37

I’m looking at React Flow.

meb09:05:07

Node Red seems pretty heavy.

meb09:05:16

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

p-himik09:05:06

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

Pepijn de Vos09:05:10

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?

thheller09:05:52

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

thheller09:05:35

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 Vos10:05:19

Hm interesting

Pepijn de Vos10:05:52

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.

thheller10:05:27

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 Vos10:05:48

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

thheller10:05:22

no, you can use shadow completely without npm too

thheller10:05:01

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

thheller10:05:21

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

Pepijn de Vos10:05:53

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.

thheller10:05:30

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

thheller10:05:18

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 Vos10:05:01

Yea it's npm, but not on cljsjs

Endre Bakken Stovner13:05:16

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

(defn page []
    (let [_ (-> (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-himik14:05:13

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 Stovner14:05:39

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

p-himik14:05:00

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

p-himik14:05:13

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

🙏 3
p-himik14:05:06

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 Stovner14:05:07

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

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

p-himik14:05:02

It's the same exact mistake as before.

p-himik14:05:19

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

Endre Bakken Stovner14:05:08

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

p-himik14:05:26

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

Endre Bakken Stovner14:05:17

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

thheller15:05:27

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

thheller15:05:10

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

thheller15:05:41

the react lifecycle would be componentDidMount or componentDidUpdate

thheller15:05:07

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

Endre Bakken Stovner15:05:39

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.

thheller15:05:25

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

km15:05:33

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 Vos18:05:01

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

Pepijn de Vos18:05:45

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 ...}]

flowthing19:05:03

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

Pepijn de Vos18:05:46

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

Uncaught TypeError: Cannot call a class as a function

dnolen19:05:37

raw React Native components have to be adapted