This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2021-10-27
Channels
- # announcements (13)
- # asami (12)
- # babashka (65)
- # beginners (62)
- # calva (14)
- # cider (8)
- # clara (11)
- # clj-kondo (16)
- # clojure (86)
- # clojure-europe (12)
- # clojure-gamedev (4)
- # clojure-nl (2)
- # clojure-sg (4)
- # clojure-uk (5)
- # clojurescript (206)
- # clojureverse-ops (11)
- # community-development (7)
- # conjure (12)
- # core-async (2)
- # core-logic (13)
- # cursive (49)
- # datalevin (1)
- # datomic (30)
- # deps-new (3)
- # duct (8)
- # events (5)
- # fulcro (10)
- # helix (5)
- # jobs (1)
- # klipse (5)
- # lsp (178)
- # luminus (1)
- # malli (8)
- # meander (3)
- # membrane (13)
- # missionary (1)
- # nrepl (5)
- # other-languages (4)
- # pedestal (4)
- # reitit (3)
- # releases (1)
- # reveal (27)
- # shadow-cljs (89)
- # tools-build (6)
- # tools-deps (11)
- # vim (2)
- # xtdb (64)
anyone knows how I can debug this to see the respons so i can see why this code is not doing what it is supposed to do
(def raw-data (atom nil))
(defn get-coordinates []
(let [zipcode "Hengelo"]
(ajax/GET ""
{:params {"q" zipcode
"appid" api-key}
:handler (fn [resp] (prn resp)(reset! raw-data resp))})))
(comment
get-coordinates
@raw-data
)
in the comment
you are likely missing the (get-coordinates)
? I mean like actually calling the function? Otherwise if you eval that at the REPL you just get the reference
the atom is not updated on recieving this json
{"name" "Hengelo", "local_names" {"nl" "Hengelo", "en" "Hengelo", "ascii" "Hengelo", "ro" "Hengelo", "feature_name" "Hengelo", "it" "Hengelo", "id" "Hengelo", "pl" "Hengelo", "fi" "Hengelo", "fr" "Hengelo", "da" "Hengelo", "de" "Hengelo", "no" "Hengelo"}, "lat" 52.2658, "lon" 6.7931, "country" "NL"}]
[{"name" "Hengelo",
"local_names"
{"nl" "Hengelo",
"en" "Hengelo",
"ascii" "Hengelo",
"ro" "Hengelo",
"feature_name" "Hengelo",
"it" "Hengelo",
"id" "Hengelo",
"pl" "Hengelo",
"fi" "Hengelo",
"fr" "Hengelo",
"da" "Hengelo",
"de" "Hengelo",
"no" "Hengelo"},
"lat" 52.2658,
"lon" 6.7931,
"country" "NL"}]
code so far
(defn handle-response-coordinates[resp]
(let [lat (get-in resp ["lat"])
lon (get-in resp ["lon"])]
(swap! app-state
update-in [:latitude] (constantly lat))
(swap! app-state
update-in [:longtitude] (constantly lon))
(pp/pprint @app-state)
))
(def raw-data (atom nil))
(defn get-coordinates []
(let [zipcode "Hengelo"]
(ajax/GET ""
{:params {"q" zipcode
"appid" api-key}
:handler handle-response-coordinates})))
if you just have a single value in the path use update
. if you just want to set a value don't use update-in
. so here you use assoc or assoc-in
yeah I don't know what the above data means. one is a map the other is a map in a vector
there is also a (pp/pprint @app-state)
but I don't know what your app-state looks like
from this line
{"name" "Hengelo", "local_names" {"nl" "Hengelo", "en" "Hengelo", "ascii" "Hengelo", "ro" "Hengelo", "feature_name" "Hengelo", "it" "Hengelo", "id" "Hengelo", "pl" "Hengelo", "fi" "Hengelo", "fr" "Hengelo", "da" "Hengelo", "de" "Hengelo", "no" "Hengelo"}, "lat" 52.2658, "lon" 6.7931, "country" "NL"}]
(get thing "lat")
or the (get-in thing ["lat"])
you have but same rule here ... don't use get-in
if there is only a single element in the path
as a general tip ALWAYS label your prints. if you just (prn thing)
all over the place in async code you never know which print belongs to what. with (prn [:thing-at-this-point thing])
you at least get a rough idea what the print meant
(defn handle-response-coordinates[resp]
(let [lat (get-in resp ["lat"])
lon (get-in resp ["lon"])]
(swap! app-state
update [:latitude] (constantly lat))
(swap! app-state
update [:longtitude] (constantly lon))
(pp/pprint @app-state)
(get-forecast!)))
(def raw-data (atom nil))
(defn get-coordinates []
(let [zipcode "Hengelo"]
(ajax/GET ""
{:params {"q" zipcode
"appid" api-key}
:handler handle-response-coordinates})))
(comment
(get-coordinates)
@raw-data
)
(defn handle-response-coordinates[resp]
(let [lat (get-in resp ["lat"])
lon (get-in resp ["lon"])]
(swap! app-state
update-in [:latitude] (constantly lat))
(swap! app-state
update-in [:longtitude] (constantly lon))
(pp/pprint @app-state)
))
(defn handle-response-coordinates[resp]
(prn [:resp resp])
(prn [:app-state-before @app-state])
(let [lat (get resp "lat")
lon (get resp "lon")]
(swap! app-state assoc :latitude lat :longitude lon)
(prn [:app-state-after @app-state])))
I would never use print in this situation. I'd always be using tap>
which only accepts a single value
if the app-state
or resp
are too large to print visibly I'd be using tap>
instead of prn
though. (if you are using shadow-cljs that is)
If you don't see any output, then it's probably sent somewhere else and not to the REPL. Maybe your browser's console?
Are you sure your REPL is running in the same browser session and not on, say, NodeJS? I have no idea if I could tell it from your screenshot.
So if you evaluate e.g. just (println "hi")
in your REPL, you don't see that hi
at all, anywhere?
and my app-state is not updated
:resp
[{"name" "Hengelo",
"local_names"
{"nl" "Hengelo",
"en" "Hengelo",
"ascii" "Hengelo",
"ro" "Hengelo",
"feature_name" "Hengelo",
"it" "Hengelo",
"id" "Hengelo",
"pl" "Hengelo",
"fi" "Hengelo",
"fr" "Hengelo",
"da" "Hengelo",
"de" "Hengelo",
"no" "Hengelo"},
"lat" 52.2658,
"lon" 6.7931,
"country" "NL"}]]
[:app-state-before
{:title "WhichWeather",
:latitude 0,
:longtitude 0,
:zip-code "",
:temperatures
{:today {:label "Today", :value nil},
:tomorrow {:label "Tomorrow", :value nil}}}]
[:app-state-after
{:title "WhichWeather",
:latitude nil,
:longtitude 0,
:zip-code "",
:temperatures
{:today {:label "Today", :value nil},
:tomorrow {:label "Tomorrow", :value nil}},
:longitude nil}]
Well, lat is definitely updated 0 -> nil.
But your resp
is not a map as you seemingly assume in that code with get-in
- it's a vector with a map.
Long is definitely updated as well, it's just that in the initial state the key has a typo.
Vectors are associative collections as well. So if you know for sure that you need "lon"
key out of the very first map in a collection that is a vector, just use (get-in resp [0 "lon"])
.
I'd probably do
(defn handle-response-coordinates [results]
(prn [:resp results])
(prn [:app-state-before @app-state])
(let [result (first results)
lat (get result "lat")
lon (get result "lon")]
(swap! app-state assoc :latitude lat :longitude lon)
(prn [:app-state-after @app-state])))
Oh, and you can destructure maps with string keys:
(let [{:strs [lat lon]} (first results)]
...)
when I have this :
(defn handle-response-coordinates[resp]
(pp/pprint [:resp resp])
(pp/pprint [:app-state-before @app-state])
(let [lat (get-in resp [0 "lat"])
lon (get-in resp [0 "long"])]
(swap! app-state assoc :latitude lat :longitude lon)
(pp/pprint [:app-state-after @app-state])))
look at the data, if you see a nil
instead of the value you expect the value did still update
Never use multiple swap!
in a row. Or deref+swap/reset.
Doesn't matter in CLJS but matters a lot in CLJ.
just not with the value you wanted, which likely points to a problem in where you get the data
If it's still CLJS then I don't think there's any other channel than #clojurescript that would be applicable. Just note that if your code is rather large, it's better to upload it as a file (IIRC Slack even prompts you to do that). And if it has multiple files, you should just go ahead and create a public repo.
I have I think solved the challenge of this page : https://www.learn-clojurescript.com/section-1/lesson-8-capstone-weather-forecasting-app/
can I improve things or did I do a good job : https://github.com/RoelofWobben/clojurescript-book-chapter1
Regarding hiding API keys - instead of hard-coding them and then replacing them each time you commit something, just pull them out of some file that's not checked in or out of the environment variables. A common approach is to use a .env
file within the project's root that would also be added to .gitignore
. I bet lein has a plugin or 5 that support pull env vars out of .env
.
Ah, right. Probably not a problem - depends on your CLJS build tool and how it can be configured. I'm only familiar with shadow-cljs, it's rather trivial there.
Another thing - don't leave print statements of any kind in your code. But leaving tap>
statements in the code is fine just because it's basically a no-op if you don't add any taps (yet another advantage of taps over prints).
for the env Im reading how to do so. Still a very beginner in clojure and clojurescript
this looks like a good start https://yobriefca.se/blog/2014/04/29/managing-environment-variables-in-clojure/
Finally, you should fix those key
warnings that the for
statement gives you - you can see them in your browser's JS console.
There are other things I personally would do differently, but overall it's fine.
or use this one : https://github.com/weavejester/environ
There's a distinction between using env vars in Clojure and using them in ClojureScript. CLJS is compiled, so you can either access variables that are available during the compilation via some Clojure tools (your build tool or some macro that you perhaps wrote yourself) or access run-time variables but via indirect means - after all, a web page has no clue about your environment. Such indirect means could be a meta tag in your HTML page of an extra AJAX request, maybe something else.
to solve the key warning I could do
:temperatures {:key 'today' :today {:label "Today"
:value nil}
?No, that wouldn't solve it - you can verify it by just trying it. Read about React keys. Then find the mention of the word "key" in the right context in Reagent documentation (I usually just search the whole repo and filter by markdown files).
Figwheel REPL Controls:
(figwheel.repl/conns) ;; displays the current connections
(figwheel.repl/focus session-name) ;; choose which session name to focus on
In the cljs.user ns, controls can be called without ns ie. (conns) instead of (figwheel.repl/conns)
Docs: (doc function-name-here)
Exit: :cljs/quit
Results: Stored in vars *1, *2, *3, *e holds last exception object
[Rebel readline] Type :repl/help for online help info
Opening URL
Failed to open browser:
No X11 DISPLAY variable was set, but this program performed an operation which requires it.
Define "nothing happens". The page keeps on loading but stays empty? The page finishes loading but stays empty? Something else? What does the Network tab in the FF devtools say? What does the JS console say?
I never said that. :) I'm a Linux user. There are still issues with WSL, it's still in flux, there's still hassle that you need to go through to make some things work. Quite objectively so - e.g. Calva has a separate page (albeit, a short one) on WSL that mentions how you have to install a whole new plugin for VS Code to make it work.
but if comes to this . IM challenged to empty the input box on this project : https://github.com/RoelofWobben/clojure-temp-converter
That's definitely not the way to change the value of an input.
Text content of a DOM node is something between its opening tag and its closing tag. So in <span>hello</span>
, that hello
is the text content. HTML inputs don't have text content, they have a value.
If the book you're reading is any good, it should either mention that fact or explicitly state somewhere at the start that some level of familiarity with HTML is required.
here is the book : https://www.learn-clojurescript.com/
And it didn't say anything about how you should change the value of an input field but at the same time required you to change the value?
maybe it is sauying it here for another html tag
(set! (.- innerHTML messages-feed) "")
Checked out the right chapter - they don't even touch on emptying the input field. So, if you stray from the main narrative of the book, you should definitely expect all sorts of hiccups. When you add some functionality that's not covered by the book but that's nonetheless somewhat easy to formulate, like "clearing an HTML input field" - just look it up online. Although don't search for ClojureScript specifically - CLJS is built on top of JS and most DOM-related things are done via simple JS interop.
last try was this :
(defn empty-input [_])
(set! (.-value (js/document.getElementById "temp")) "")
"No luck" as in doesn't do anything? Or results in an error? Or does something you didn't want it to do?
To be fair, to me the only issue with the book seems to be that they seemingly require you to know some HTML without saying so. Other issues were neither with the book nor with CLJS.
Challenging by itself is fine, as long as succeeding is not a requirement to proceed - it facilitates learning.
I get that. But most of the time, you can lookup solutions for JS and just adapt them for CLJS.
last question of the day Is it easy to add bootstrap and jquery to a clojurescript site
Meaning, it's largely superseded by other things. Many of them are available directly in DOM API, some are via goog
, some others via some other, much smaller, libraries.
I was looking at this layout and site : https://karan-kmr.github.io/temperature-converter/
so for the layout I need to use bootstrap. I did some googeling but get very different outcomes one is to us this : https://github.com/luxbock/bootstrap-cljs
Yeah, jQuery in that project is used to basically just save a few characters of code - not worth it at all. Well, you don't need Bootstrap for anything, including that layout - you can always learn how to build one yourself. Depends on what your learning goals are. I'd say that in this case, the layout is so simple that you don't really have to learn that much.
But if you mostly want to learn CLJS and not CSS then yeah, just copying that particular HTML+CSS along with the dependency on Bootstrap would be reasonable.
this is a known issue I believe
I believe roelof also describes manually opening a browser and typing in the URL, which still doesn't work, until it does for some reason.
well that sounds different
another more clojurescript problem second project is a temp-converter but why is the input-field not empty here Code so far : https://github.com/RoelofWobben/clojure-temp-converter
No full equivalent, at least not built-in. There's a goog function, but it's very limited.
It's a completely different thing. But it can be used in some of the situations where you'd use format
.
If you're using shadow-cljs: https://clojureverse.org/t/modern-js-with-cljs-class-and-template-literals/7450
What's wrong in this translation from js to cljs?
function loop() {
...
requestAnimationFrame(loop);
}
loop()
(declare d)
(defn d []
...
(js/requestAnimationFrame d))
(d)
I want to make a number of web API ajax calls (GETS), and collect all the results into one collection.
And then call a re-frame event when its finished. What should I read about to learn the mechanisms to do this?
i.e. I have a ["foo", "bar", "quax"]
Then I want to do
Get http://..../foo
, http://..../bar
, http://..../quax
and know when they are finished.
Use the effect that sends the requests N times, accumulate the results in some collection, assume everything is done when (= N (count that-collection))
.
so if i write a function that does the request and parameterise it, I can just map that function over my list ?
No. If you gonna to it re-frame way, you should use an effect that sends a request and then emits an event with the response it received. Like this one: https://github.com/day8/re-frame-http-fx
Usually regular interop. If you don't like it, then something like https://github.com/funcool/promesa
I want my library to generate a unique string for each build of an application. Is there a way to do this? My thinking was to have this in a macro so it generates a unique id at build time. I haven’t tested this yet but seems plausible and also not sure if it’s a good idea.
Yes, a macro makes sense for generating random IDs per build. Whether it's a good idea depends on what the ID is for.
True. I’m thinking of using it as a way of encrypting a secret that I put into IndexedDB. Hard to do security in browser on open source code.
Actually just realised a new attack vector where one could quite literally inspect the source when targeting a specific application
If you are using shadow-cljs, it supports md5 hash: https://shadow-cljs.github.io/docs/UsersGuide.html#NameHashing