Fork me on GitHub
#clojurescript
<
2021-07-05
>
zendevil.eth08:07:39

Suppose I have a route called “/creators”. This is a reagent app and not a static page, so the javascript does the rendering in the browser and the /creators route sends the boilerplate html to the browser. However, the content to be shown at creators route also depends on values from the database, so not only do I want to send the boilerplate html, but also data from the database. How can I do both simultaneously? Follow up: Suppose that I have other microservices that produce values that need to be rendered in the /creators route, but since those have a higher latency, I would prefer to send what I can as soon as possible and wait for the other microservices to produce the result in the browser when they are done producing the result, kind of how in twitter, the menus load before the tweets do. How will I integrate those microservices with the main server that receives the request? The static response to be sent is:

(layout/render request "home.html")
and the db data to be sent is:
(r/response (get creators- (-> :req :query-params :id)))

p-himik09:07:12

> I would prefer to send what I can as soon as possible Make / and /creators serve the exact same HTML and make only the frontend initiate data loading upon navigating to /creators. If you're also using re-frame, it's rather easy to do with kee-frame's controllers.

zendevil.eth10:07:25

I’m sending sequence of maps to the client:

({":db/id" 17592186045425, ":creator/id" "2hhae235", ":creator/name" "Carla Wonder"} {":db/id" 17592186045426, ":creator/id" "23we35", ":creator/name" "Killer Reapers"} {":db/id" 17592186045427, ":creator/id" "234w345", ":creator/name" "Autonomy Band", ":creator/img-src" "", ":creator/description" "A great band that has rocked the listeners since the 1990s. They released their latest album in 2021 and have only increased their fanfare since their inception with the album Blushes, which was released in 1992 to wide critical acclaim."} {":db/id" 17592186045420, ":creator/id" "23422344", ":creator/name" "Alpatoons"} {":db/id" 17592186045421, ":creator/id" "23ahjqg35", ":creator/name" "Head One Thousand"} {":db/id" 17592186045422, ":creator/id" "23aasg235", ":creator/name" "Purely Black"} {":db/id" 17592186045423, ":creator/id" "asgasg35", ":creator/name" "Headhead"} {":db/id" 17592186045424, ":creator/id" "23agha35", ":creator/name" "Head Fighters"})
However on the client, it gets converted to:
[{::db/id 17592186045425, ::creator/id "2hhae235", ::creator/name "Carla Wonder"} {::db/id 17592186045426, ::creator/id "23we35", ::creator/name "Killer Reapers"} {::db/id 17592186045427, ::creator/id "234w345", ::creator/name "Autonomy Band", ::creator/img-src "", ::creator/description "A great band that has rocked the listeners since the 1990s. They released their latest album in 2021 and have only increased their fanfare since their inception with the album Blushes, which was released in 1992 to wide critical acclaim."} {::db/id 17592186045420, ::creator/id "23422344", ::creator/name "Alpatoons"} {::db/id 17592186045421, ::creator/id "23ahjqg35", ::creator/name "Head One Thousand"} {::db/id 17592186045422, ::creator/id "23aasg235", ::creator/name "Purely Black"} {::db/id 17592186045423, ::creator/id "asgasg35", ::creator/name "Headhead"} {::db/id 17592186045424, ::creator/id "23agha35", ::creator/name "Head Fighters"}]
i.e. the keywords have two colons instead of one. Why is this the case, and how to fix this?

p-himik10:07:23

Impossible to tell for sure without all the code that serializes the data and deserializes it.

p-himik10:07:39

Perhaps, you have something like :keywords? true set on the deserialization side. Calling (keyword ":db/id") will return ::db/id.

zendevil.eth10:07:24

instead of making changes in the server, is it possible to use two colon keyworeds in the client?

p-himik10:07:45

Yes, but don't do it. Three proper ways to tackle it: • Change the backend, if you have control over it - either to use a different format for keys or to use something other than JSON that preserves data type, like Transit • Do not convert data keys into keywords - just use strings. It makes sense because you seem to be sending strings in the first place. Keep keywords keywords and strings strings • Traverse the received data structure and convert all string keys that start with : to a keyword yourself

emccue13:07:22

or, use transit on top of json instead of just json

emccue13:07:44

that will serialize/deserialize keywords correctly

danielneal10:07:48

I’m getting an error “require not defined” when trying to build a single output file using figwheel.main build-once + webpack. The line in the compiled js giving the error is require("react") I’m guessing it’s somehow not bundling the node modules but I don’t know where to start figuring out how to fix it. Any pointers very welcome!! I don’t get the error in normal figwheel operation (with optimizations none)

admarrs10:07:55

Is it possible to get shadow-cljs to release an app with a versioned name using something like lein-git-inject and lein shadow? I've got a full-stack clojure/clojurescript app and am trying to streamline the release process to add a version string in the .js file to overcome browser caching when pushing out a new version.

p-himik10:07:01

There's also #shadow-cljs Shadow-cljs can generate output manifest specifically to tackle that issue: https://shadow-cljs.github.io/docs/UsersGuide.html#BrowserManifest It's not a version, but it's a sufficient mechanism to overcome browser caching.

thheller16:07:31

but yeah the manifest is the intended solution for this when combined with :module-hash-names true

admarrs15:07:37

Thanks. I somehow missed shadow-git-inject! I did try the :build-options {:manifest-name "manifest.json"} approach but then got a Manifest: root element must be a valid JSON object error in the browser because it's wrapped in a [ ]. Have I missed something in the dhadow-cljs config?

p-himik16:07:54

Don't consume the manifest in the browser. Consume it on the backend when you generate index.html to put the right path to the main JS file.

👍 2
zendevil.eth13:07:55

I am mapping a sequence of maps like so across @(rf/subscribe [:creators]), however, I’m still getting the react unique key error in the console, while each creator does have a unique id with (:creator/id creator)

(defn creator-view [creator]
  [:a {:key (:creator/id creator) :href (str "/creator-page?id=" (:creator/id creator)) :style {:cursor :pointer}} (:creator/name creator)])

(defn creators []
  [:div {:style {:display :flex :flex-direction :column}}
   (map (fn [creator]
          (prn "creator id " (:creator/id creator))
          [creator-view creator]) @(rf/subscribe [:creators]))
   ])
react.development.js:221 Warning: Each child in a list should have a unique “key” prop. Check the render method of creators. See https://reactjs.org/link/warning-keys for more information. at cmp (http://localhost:3000/js/cljs-runtime/reagent.impl.component.js:496:43) at cmp (http://localhost:3000/js/cljs-runtime/reagent.impl.component.js:496:43) at div at div at cmp (http://localhost:3000/js/cljs-runtime/reagent.impl.component.js:496:43)

p-himik13:07:57

Your [:a ...] elements have unique keys. React complains about the [creator-view ...] elements. Even if it doesn't result in a separate HTML node for creator-view, it's still a React element.

zendevil.eth13:07:31

so the best bet is wrapping creator-view in div and putting a key there?

hkjels13:07:47

Either use into with your map or add the key as metadata

hkjels13:07:35

(into [:div {:style {:display :flex :flex-direction :column}}] (map (fn [creator]
          (prn "creator id " (:creator/id creator))
          [creator-view creator]) @(rf/subscribe [:creators])))

hkjels13:07:38

something like that

zendevil.eth13:07:29

how does the metadata thing work?

p-himik13:07:16

Reagent reads the metadata and sets the key when creating the corresponding React element.

p-himik13:07:08

And with into above, you don't need :key at all since all the laziness is removed. But I've seen such a method being discouraged because, depending on the circumstances, the performance might suffer.

zendevil.eth13:07:42

how to set the key in the metadata exactly?

p-himik13:07:04

^{:key (:creator/id creator)}
[creator-view creator]
Can be in a single line if you prefer.

p-himik13:07:51

Are you sure :creator/id is always set and is unique across all records? Are you sure it's the creators component and not something else? - ah, right, the error mentions it explicitly. Unless there are multiple components named like this. Does the error disappear if you use the into method above?

danielneal15:07:04

I’ve made a minimal repro of my figwheel.main “require not defined” error, can anyone spot what dumb mistake / misunderstanding I’m making? https://github.com/danielneal/figwheel-webpack

danielneal15:07:43

^ The issue was that webpack wasn’t running, I’m guessing the bundle-cmd is a map of optimisation level to bundle cmd, I’d completely overlooked that I only had :none <bundle-cmd> in there and nothing for :default (or :advanced)

clj839416:07:33

hello, wanted to share my feedback with cljs so far and see how I can improve further. I have gotten used to the syntax and all that, the language itself is well designed and easy to use, in these respects i am having no issues. the issue i am having is in regards to tooling, specifically on the IDE side of things. Im currently using vscode with the calva extension, and can connect to a repl, so can get feedback quickly and the repl works well. but, I currently dont know of any extensions that provide linting, autocompletion for NPM modules, and debugging. I could probably live without these features, but I think they are very useful to have and want to see what's available for vscode that might do these things. Anyone know of any tools for this?

pez16:07:40

Calva should give you linting. Doesn't it?

pez16:07:23

As far as debugging goes, that is not really available afaik. I seldom lack it, though. Especially since I'm often using shadow-cljs and it's excellent inspector.

clj839416:07:06

i see source formatting but don't see linting for anything like missing function name, For example, if i write a function but never use it the editor doesnt catch it

pez17:07:22

It should work. If your code is public I can have a look.

clj839417:07:40

im trying again with a simple hello world example. I have a function

(defn hello-world "hello world")
and am not using it, but the IDE isn't giving me a warning that it isn't being used

clj839417:07:24

maybe its an issue with my machine, I see Initializning Clojure Language Features via clojure-lsp in the bottom left corner of the editor. Maybe that's why im not getting having issues?

clj839417:07:31

I can connect to the repl fine

clj839417:07:50

also, regarding debugging, is there no way to use the source maps with a debugger? im not familiar with shadow-cljs, how does the inspector work with it? Im primarily trying to use clojurescript through node and not in the browser

clj839418:07:43

i did some testing, it seems that when I have a deps.edn file, on my installation calva is not able to initialize the clojure-lsp for proper linting, but without deps.edn I cant connect to the repl

clj839418:07:08

any idea on what may be the issue here?

pez19:07:38

Might be something to ask about in #lsp

borkdude19:07:00

yes, this should work right out of the box

clj839419:07:59

I asked in the #lsp channel. It does appear to be working probably when I don't have a deps.edn file in my workspace. not sure what is causing the issue here

clj839419:07:51

got it working, turns out it was a configuration issue on my end. thanks for the help yall

clj839419:07:04

also amazing stuff with this extension, thanks for all the hard work yall put in on making this

clj839419:07:37

one last thing @U0ETXRFEW is there any way to change the colors of the parens, would like to use a custom scheme if possible

clj839420:07:10

awesome, will definitely check out the customization options! thanks for all the help

pez20:07:48

About source maps. I’ve had some limited luck with using the Chrome debugger, but the source maps seem to be a bit lacking, so it hasn’t been worth the fiddling.

pez20:07:17

shadow-cljs starts a console at http://localhost:9630 where, amongst other things, you find an inspector that will show you things that you tap> from the REPL. And Calva has some commands for making tapping extra convenient. Shadow works great for node work as well as for the browser.

nate sire17:07:58

I am interested in a debugger too... have you tried running node inspector on the transpiled js?

nate sire17:07:31

Calva website says its debugger does not support clojure script

markbastian20:07:51

Hey all, trying to port this fragment from js to cljs: this.map = this.add.tilemap("tilemap"); The RHS is

(ocall this [:add :tilemap] "tilemap")
using https://github.com/binaryage/cljs-oops. To port the set operation I am doing
(aset this "map" (ocall this [:add :tilemap] "tilemap"))
. A few questions: • Is this the “correct” translation of this fragment? • The oset! function in cljs-oops doesn’t work, saying the key “map” is unexpected. I understand the warning, but I wonder if this is a hint that I should be doing something else. Any tips if this isn’t the right way to do it? • If it is right, I see that you can set with string keys or keywords (e.g. :map ). Is there any downside to that approach? Also note that I am getting this from the this-as macro if that matters.

markbastian20:07:02

I can also just store all desired created objects in an atom. This may be more Clojuric, but I also want to understand the js->cljs translation as well.

p-himik20:07:39

aset is most definitely wrong because it's for arrays, not objects. You don't need cljs-oops either. oset! can work if you add ! to the non-existing key: :!map. This would be a 1-to-1 translation from JS to CLJS:

(this-as this
  (let [add (.-add this)
        tilemap (.bind (.-tilemap add) add)]
    (set! this -map (tilemap "tilemap"))))
And if tilemap doesn't need its this to be bound to add, you can remove the .bind part.

p-himik20:07:43

Actually, I'm probably wrong about the need for bind - this should be sufficient:

(this-as ^js this
  (set! this -map (.. this -add (tilemap "tilemap"))))
Notice how I added ^js in this example - it's important for the advanced compilation to not munge -add and -map. I'm not 100% sure that tilemap will be preserved. If not, you need one extra ^js somewhere in that .. form.

🙏 4