Hi. What’s the recommended way to do logging in clojurescript?
Seems https://github.com/lambdaisland/glogi is a good choice.
https://github.com/ptaoussanis/timbre also really good
When working with Reagent + Google Maps API, I've created a "Polyline" form 2 component, saving a reference of the shape (google.maps.Polyline) in an atom within the component. When Figwheel re-renders after a change, the lines disapear on the Map, but I can confirm they still exist. Does anyone have any advice for working with shapes in google maps w/ Reagent? I want to generate Polylines for each item in an array stored in my global app state. I want to then select one of them and have it styled slightly differently (stroke color).
Yep. Google Maps is highly stateful and dealing with stateful js components is a bit painful. However this strategy has worked for me well https://github.com/day8/re-frame/blob/master/docs/Using-Stateful-JS-Components.md
Google maps happens to be an example there. 🙂
I think this has something to do with defonce
, since I'm storing state in a component w/ let
, it is lost when Fighweel reloads. I need to figure out a way to retain a reference to each shape drawn on the map that will persist.
Are there any alternatives to lumo which could support nodejs libraries?
I'm looking for something that I can embedded in AWS Lambda runtime.
embed what? just use a random node library or cljs-eval?
@thheller I would like to have the same capabilities as lumo
for Clojurescript or ts-node
for Typescript. I would like to just pack Clojurescript sources and node_modules in zip and run shadow-cljs bootstrapped-run entrypoint.cljs instead of compiling Clojurescript-> Javascript beforehand.
I don't know what you mean by "same capabilities". why use bootstrapped if you can just precompile it normally?
For AWS Lambda it's convenient to have a bootstrapped Clojurescript, so you could change sources and see immediate change on Lambda.
How does the code look like?
I mean, it’s easier to help if you post relevant chunks of code here 🙂
(defn polyline
"Creates a Polyline on the Map"
[path gmap]
(let [ref (r/atom nil)]
(fn [_ _ is-selected]
; if the polyline hasn't been created, create it
(when (nil? @ref)
(reset! ref (js/google.maps.Polyline.
(clj->js {:path path
:geodesic true
:strokeColor "black"
:strokeWeight 6
:map gmap})))
)
; if the polyline has been created and toggled
; set the appropriate strokeColor
(when (some? @ref)
(if (and is-selected (some? @ref))
(.setOptions @ref (clj->js {:strokeColor "blue"}))
(.setOptions @ref (clj->js {:strokeColor "black"})))
)
nil
)))
In my App component, we iterate over each item that would render a Polyline:
(doall (for [activity (:activities @app-state)]
^{:key (util/get-activity-time activity)}
[maptools/activity
activity
(:gmap @app-state)
(util/is-selected? (:selected-activity @app-state) activity)
]))]]))
How are you rendering the google map? Does that happen inside or outside React render tree? My guess is that on figwheel reload your whole google map gets initialised again
shadow-cljs? https://shadow-cljs.github.io/docs/UsersGuide.html#node-repl https://shadow-cljs.github.io/docs/UsersGuide.html#target-node
(assuming you’re not using some react wrapper for google maps)
Yeah, I think that's the issue as well. I have a component for the Google Map, which looks like this:
(defn google-map [app-state set-error]
(let [api-key (subs (-> js/document .-location .-search) 1)
center (clj->js {"lat" 40.730610
"lng" -73.935242})
loader (google.maps.plugins.loader.Loader.
(clj->js {:apiKey api-key
:version "weekly"}))]
(fn []
(.addEventListener
js/window
"DOMContentLoaded"
(-> (.load loader)
(.then (fn []
(swap! app-state assoc :gmap (google.maps.Map.
(. js/document (getElementById "map"))
(clj->js
{:center center
:zoom 8
:fullscreenControl false
:clickableIcons false
:disableDoubleClickZoom true})))))
(.catch #(set-error "Unable to load Google Maps"))))
[:div {:id "map"}])))
I can confirm that it re-renders each time I save a change in the project directory.
Vanilla CLJS can. So Shadow-CLJS / FIgwheel-main too: • https://figwheel.org/docs/nodejs.html • https://clojurescript.org/guides/quick-start#running-clojurescript-on-node.js
Dymanic import import('foo').then(module => console.log(module));
seems now supported by the last Google Closure compiler:
https://github.com/google/closure-compiler/issues/2770#issuecomment-834744931
the dynamic+dynamic import style isn't supported by the closure-compiler and likely will never be, webpack also has issues with it.
so import(someVariable).then ...
will always be a problem for bundlers when they are supposed to bundle stuff
it is fine to do at runtime when you don't expect the bundler to bundler stuff but instead import already bundled stuff like code-split modules
I had an example for truly dynamic import here https://clojureverse.org/t/generating-es-modules-browser-deno/6116
webpack has a partial interpreter to figure out some dynamic imports but it is fairly limited and usually requires a bunch of build configuration
@thheller Your example tackles truly dynamic imports whereas cljs.loader/load
requires for the imported thing to be a predefined module.
Are there some other differences that are important?
you'd use the dynamic-import instead of cljs.loader (in case of an :esm
build). so no cljs.loader included at all, just purely build-in import
the import variant that can be rewritten I have not tried yet. not sure what the closure-compiler turns it into or how it needs to be configured
Thanks both for these replies and useful informations...very interresting. 👍
You can already do the same thing in CLJS with code splitting.
Is it something interresting for CLJS in the futur?
I’m having an issue with eval
in Clojurescript. My use case is that I’m importing a bunch of rules as s-expressions and executing them against the current state of the app. I have a function called present?
that is kind of like (complement nil?)
but for JS input fields, i.e. it checks to see if a field’s value is ""
or js/NaN
, that kind of thing. When I eval
(present? 1)
, I get true. When I eval (or (present? 1))
, I get true. When I eval (or (present? 1) true)
I get nil
.
Here is a gist with the minimal steps to get there: https://gist.github.com/joshuamiller/20773273a234948013358f91e74c0392
Does anyone have any ideas what I might be doing wrong?
Thanks. Rather, I saw the use to import another file dynamically like in these examples: https://inertiajs.com/pages#default-layouts But hey, this goes a bit against namespaces so I don't think so...
At the moment, the only solution I have found is to list the pages manually: https://github.com/prestancedesign/reagent-inertia-reitit-integrant-fullstack/blob/main/front/src/reagent/inertia.cljs#L112-L121
The JS example can be adapted just fine to work with namespaces.
At least, it seems to me so.
Just as an idea - you can try supplying a custom logging js-eval
to see what actually gets evaluated. Maybe it it will shed some light.
Good idea, I’ll give that a shot.
My or
with branches seems to not be returning anything, here’s the source it generates:
var or__43529__auto___44 = cljs.js.get_fn( 97 ).call(null,(1));
if(cljs.core.truth_(or__43529__auto___44)){
} else {
}
A statement is something purely imperative - it has no value. An expression is anything that has a value. In CLJ(S) world, everything is an expression. In JS world, sometimes something has to be a statement to work.
That makes sense in retrospect. I have played around with the code each context generates and you can see the difference.
At the same time, it's interesting how (present? 1)
still returns a value when using :statement
.
I guess that when it’s not embedded in a conditional, it doesn’t matter whether it’s side-effecting or whatever, so it just calls it and you get what JS gives you.
Yeah, but it should not have a return
there if it's a statement. Otherwise, it could break some things.
But I've never dealt with eval
before, so I can be completely wrong.