Fork me on GitHub
#clojurescript
<
2021-10-27
>
roelof10:10:59

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
  )

thheller10:10:51

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

roelof10:10:08

oke, that seems to be the culprit

roelof10:10:49

and next problem

roelof10:10:34

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})))

thheller10:10:46

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

thheller10:10:46

(swap! app-state assoc :latitude lat)

roelof10:10:24

it schould be a single value

roelof10:10:40

"lat" 52.2658

thheller10:10:05

yeah I don't know what the above data means. one is a map the other is a map in a vector

thheller10:10:15

so I do not know what data you are working with at which point

thheller10:10:56

there is also a (pp/pprint @app-state) but I don't know what your app-state looks like

roelof11:10:14

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

roelof11:10:22

I want the lat and lon

thheller11:10:31

(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

thheller11:10:39

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

roelof11:10:03

now a do not see why my app-state is not printed

roelof11:10:07

(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
  )

thheller11:10:15

update is incorrect like that

roelof11:10:00

yep, and update-in also

roelof11:10:16

app-state did not update like I thought it would be

roelof11:10:29

back to the drawing board 😞

roelof11:10:04

(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)
     ))

thheller11:10:25

(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])))

p-himik11:10:49

Why not (prn :resp resp)?

thheller11:10:36

I would never use print in this situation. I'd always be using tap> which only accepts a single value

thheller11:10:39

for prn your could do that

thheller11:10:34

this is what I would do in full debug mode

thheller11:10:16

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)

roelof11:10:30

I tried that in on repl I did not see any output then

p-himik11:10:14

If you don't see any output, then it's probably sent somewhere else and not to the REPL. Maybe your browser's console?

roelof11:10:14

I do not see it

thheller11:10:58

try js/console.log instead of prn

p-himik11:10:02

One other advantage of tap> - you always know where the data will end up. :)

roelof11:10:39

still the same sorry

roelof11:10:16

moment, you talked about full debug mode is that something else then starting a repl

thheller11:10:47

no, just a normal REPL. just meant "me trying to debug something"

roelof11:10:22

oke, still no luck with calva and FF

p-himik11:10:47

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.

roelof11:10:09

I will try a totally fresh repl

roelof11:10:08

still no luck

p-himik11:10:17

So if you evaluate e.g. just (println "hi") in your REPL, you don't see that hi at all, anywhere?

roelof11:10:40

when I use pretty print I see output 🙂

roelof11:10:27

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

p-himik11:10:30

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.

p-himik11:10:42

Long is definitely updated as well, it's just that in the initial state the key has a typo.

thheller11:10:17

yeah now you can see the problems by just looking at the data 🙂

roelof11:10:19

then I have to back to the docs how to get the value out of that

roelof11:10:28

both are updated wrong now

p-himik11:10:16

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"]).

p-himik11:10:58

Or, arguably with a more clear intent: (-> resp (nth 0) (get "lon")).

thheller11:10:24

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])))

p-himik11:10:12

Oh, and you can destructure maps with string keys:

(let [{:strs [lat lon]} (first results)]
  ...)

roelof11:10:18

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])))

roelof11:10:24

only lat is updated

thheller11:10:38

because you have "long"?

roelof11:10:47

can I not better use two swap! to update both ?

thheller11:10:23

look at the data, if you see a nil instead of the value you expect the value did still update

p-himik11:10:33

Never use multiple swap! in a row. Or deref+swap/reset. Doesn't matter in CLJS but matters a lot in CLJ.

roelof11:10:37

finally , it works

thheller11:10:40

just not with the value you wanted, which likely points to a problem in where you get the data

roelof11:10:57

time for lunch and then look why the rest is not working

roelof11:10:07

I can use the same "trick" here

p-himik12:10:43

I'm 95% certain that won't fix your software issue. :)

roelof12:10:01

i see the problem

roelof12:10:15

because of two calls to a api it slow

roelof12:10:29

it takes a few seconds to display the data

roelof12:10:39

some 2 /3 seconds

roelof12:10:02

can I somewhere ask for feedback if the make my api -key invisible ?

p-himik12:10:14

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.

roelof12:10:28

I think I made a public repo

roelof12:10:41

I do not think the file is large

p-himik12:10:35

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.

roelof12:10:49

oke but this project uses clojure-tools . Is that a problem

p-himik12:10:36

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.

p-himik12:10:53

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).

roelof12:10:54

print is deleted

roelof12:10:24

for the env Im reading how to do so. Still a very beginner in clojure and clojurescript

p-himik12:10:42

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.

roelof12:10:51

oke, then I also have to use google

roelof12:10:10

the book does not explain how to solve those errors so far

p-himik12:10:22

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.

roelof12:10:54

pff, a big mess I try to learn 😞

p-himik12:10:38

Web development has a lot of moving parts, yeah.

p-himik12:10:19

But all are totally approachable - just don't jump at everything at once.

roelof12:10:34

I not doing it

roelof12:10:03

im just following this book to learn and get a taste of clojurescript and/or react

roelof12:10:35

not to be a expert. Clojure is for me a hobby ot a proffession

👍 1
roelof12:10:52

maybe time for chapter2

roelof13:10:10

and wait with the key errors till I learn how to solve those

roelof13:10:24

the same wih the env variables

roelof13:10:33

thanks for the feedback

👍 1
roelof13:10:52

chapter2 I learn about events

roelof13:10:32

to solve the key warning I could do

:temperatures {:key 'today' :today {:label "Today"
                                                   :value nil}
?

p-himik13:10:44

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).

roelof13:10:17

oke, on my to-do list

roelof15:10:16

im going crazy

roelof15:10:11

Im seeing this in my prompt ater doing clojure -A:fig:build

roelof15:10:43

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.

roelof15:10:10

and even afyeter I manually open localhost:9500 on my windows machine nothing happens

roelof15:10:19

the code is running in WSL

roelof15:10:17

very wierd . In FF no luck in Chrome it worls

p-himik15:10:33

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?

roelof15:10:27

I see a message that no server is found

roelof15:10:43

but aftter it works in Chrome it also works again in FF

roelof15:10:54

wierd things computers

p-himik15:10:39

WSL is finicky and IMO is still immature to be used reliably.

roelof15:10:36

oke, I find WSL2 reliably. This is the only time I had problems with it

roelof15:10:59

So according to you I can better do it all in Win10?

p-himik15:10:59

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.

roelof15:10:01

yep, but on WSL2 that plugin is almost automatically installed

roelof16:10:39

can you help me one more time witr another clojurescript problem ?

p-himik16:10:25

I can at least take a look. What's the problem?

roelof16:10:16

I posted on the clojurescript channel

roelof16:10:19

but if comes to this . IM challenged to empty the input box on this project : https://github.com/RoelofWobben/clojure-temp-converter

roelof16:10:38

but if I press the button. the input box stays the same

p-himik16:10:23

Why are you using setTextContent?

p-himik16:10:38

On the input field, I mean.

roelof16:10:58

I thought and learned that that is the way to change text

p-himik16:10:27

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.

roelof17:10:20

oke, back the text to learn a better way

p-himik17:10:22

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.

roelof17:10:09

and it is not saying that some familairity of html is required

p-himik17:10:05

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?

roelof17:10:48

maybe it is sauying it here for another html tag

(set! (.- innerHTML messages-feed) "")

p-himik17:10:20

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.

roelof17:10:25

wierd, that they challenge you to do things they have not learned

roelof17:10:34

maybe again the wrong book

roelof17:10:07

last try was this :

(defn empty-input [_])
   (set! (.-value (js/document.getElementById "temp")) "")

roelof17:10:14

but still no luck

p-himik17:10:37

"No luck" as in doesn't do anything? Or results in an error? Or does something you didn't want it to do?

roelof17:10:48

no luck as in the html element is still not empty

p-himik17:10:20

Oh, hold on. Pair up parentheses in your function.

roelof17:10:51

yes. it works

roelof17:10:18

tomorrow I try to make it work that someone can also convert from and to Kelvin

roelof17:10:24

and maybe a better layout

roelof17:10:47

The first two challenges have a very bad looks and way of working

p-himik17:10:21

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.

roelof17:10:55

oke, clear point

roelof17:10:16

and challenging people and not learning how to do so

roelof17:10:47

thanks and a good night

p-himik17:10:52

Challenging by itself is fine, as long as succeeding is not a requirement to proceed - it facilitates learning.

roelof18:10:32

and that is my weak point. I hate it when I do not get it work

roelof18:10:10

Get then the feeling I did not understand what they trying to teach

p-himik18:10:29

I get that. But most of the time, you can lookup solutions for JS and just adapt them for CLJS.

roelof19:10:58

last question of the day Is it easy to add bootstrap and jquery to a clojurescript site

p-himik19:10:30

Yes. But don't use jQuery, it's pretty much outdated by now.

p-himik19:10:38

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.

roelof19:10:01

But I think that what I have learned now I can use goog to do the same

roelof19:10:27

it looks like it only used to updated the converted temp

roelof19:10:57

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

roelof19:10:14

but I have to dive in to see if and how I can make that layout

p-himik19:10:05

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.

p-himik19:10:17

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.

roelof19:10:30

oke, so add a link to bootstrap in the index.html and go ?

p-himik19:10:38

Yep. That's the easiest way to do it.

roelof19:10:10

oke, then find a cdn for it

Alex Miller (Clojure team)15:10:09

this is a known issue I believe

p-himik15:10:39

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.

Alex Miller (Clojure team)15:10:52

well that sounds different

roelof15:10:02

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

Apple15:10:17

What's the equivalent to clj/clojure.core/format in cljs?

p-himik15:10:56

No full equivalent, at least not built-in. There's a goog function, but it's very limited.

Apple15:10:45

how about js template string in cljs?

p-himik15:10:17

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

Apple16:10:28

thank you!

👍 1
Apple16:10:03

What's wrong in this translation from js to cljs?

function loop() {
...
  requestAnimationFrame(loop);
}
loop()
(declare d)
(defn d []
  ...
  (js/requestAnimationFrame d))
(d)

p-himik16:10:54

LGTM, although you don't need that declare.

Apple16:10:32

silly me... thanks ag

👍 1
Stuart20:10:14

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.

p-himik20:10:27

Use the effect that sends the requests N times, accumulate the results in some collection, assume everything is done when (= N (count that-collection)).

Stuart20:10:48

so if i write a function that does the request and parameterise it, I can just map that function over my list ?

Stuart20:10:10

(map (my-get-fn) ["foo" "bar" "quax"]) sort ofd thing?

p-himik20:10:22

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

p-himik20:10:40

Or, if you write your own effect handler, you can absolutely do that (map ...), yes.

p-himik20:10:01

All side effects in re-frame apps happen in effects.

Stuart20:10:06

yeah, the :http-xhrio is what I'm using now. that's how I'm getting my initial list

p-himik20:10:14

So, you use it N times, store the results, count them, and voilà.

Stuart20:10:39

Thanks, I think that's good info to try and figure this out.

Apple21:10:14

what's the pattern for js promise in cljs?

p-himik21:10:54

Usually regular interop. If you don't like it, then something like https://github.com/funcool/promesa

1
☝️ 1
grounded_sage23:10:38

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.

p-himik23:10:02

Yes, a macro makes sense for generating random IDs per build. Whether it's a good idea depends on what the ID is for.

grounded_sage23:10:13

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.

grounded_sage23:10:15

Actually just realised a new attack vector where one could quite literally inspect the source when targeting a specific application

JB Seo00:10:15

If you are using shadow-cljs, it supports md5 hash: https://shadow-cljs.github.io/docs/UsersGuide.html#NameHashing

thheller04:10:17

you absolutely cannot put secrets into a build directly. no matter how people will be able to find it thus making it not a secret.