This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2021-09-10
Channels
- # announcements (16)
- # aws (1)
- # babashka (2)
- # beginners (33)
- # biff (2)
- # clj-kondo (13)
- # cljs-dev (1)
- # cljsrn (3)
- # clojars (6)
- # clojure (198)
- # clojure-australia (3)
- # clojure-europe (41)
- # clojure-france (3)
- # clojure-nl (2)
- # clojure-spec (7)
- # clojure-uk (12)
- # clojurescript (57)
- # clojureverse-ops (1)
- # code-reviews (3)
- # community-development (2)
- # conjure (10)
- # data-science (1)
- # datomic (15)
- # depstar (2)
- # docker (2)
- # etaoin (1)
- # events (1)
- # exercism (5)
- # fulcro (23)
- # helix (23)
- # introduce-yourself (4)
- # jobs (6)
- # kaocha (1)
- # lsp (11)
- # meander (107)
- # off-topic (8)
- # pathom (3)
- # polylith (33)
- # re-frame (23)
- # reagent (7)
- # reitit (28)
- # remote-jobs (3)
- # sci (1)
- # shadow-cljs (2)
- # specter (5)
- # sql (38)
- # tools-deps (72)
- # web-security (9)
- # xtdb (32)
Hi I’m trying to convert this part into cljs but I don’t know how the interop would work:
client
.query({
query: gql`
query GetRates {
rates(currency: "USD") {
currency
}
}
`
})
.then(result => console.log(result));
this part:
gql`
query GetRates {
rates(currency: "USD") {
currency
}
}
`
this thread might interest you https://clojurians.slack.com/archives/C0E1SN0NM/p1631619249227400
also I don’t understand what ^ this means in js even
is gql getting concatenated to the backtick string or what?
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals#tagged_templates
You can turn it into a call to the gql
function.
https://clojureverse.org/t/modern-js-with-cljs-class-and-template-literals/7450 might help
for some reason I’m requiring this package but that’s causing an error:
["@apollo/client" :refer [ApolloClient InMemoryCache ApolloProvider useQuery gql]]
error:
An error occurred when loading humboi.app.js
env.evalLoad @ app.js:1551
(anonymous) @ app.js:3123
app.js:1552 TypeError: humboi.events.common_navigate is not a function
at humboi$core$navigate_BANG_ (core.cljs:234)
at reitit$frontend$easy$start_BANG__$_rfe_on_navigate (easy.cljs:39)
at Object.eval [as reitit$frontend$history$History$_on_navigate$arity$2] (history.cljs:128)
at Object.reitit$frontend$history$_on_navigate [as _on_navigate] (history.cljs:12)
at Object.eval [as reitit$frontend$history$History$_init$arity$1] (history.cljs:125)
at Object.reitit$frontend$history$_init [as _init] (history.cljs:10)
at Function.eval [as cljs$core$IFn$_invoke$arity$3] (history.cljs:170)
at Object.reitit$frontend$easy$start_BANG_ [as start_BANG_] (easy.cljs:36)
at Object.humboi$core$start_router_BANG_ [as start_router_BANG_] (core.cljs:543)
at Object.humboi$core$init_BANG_ [as init_BANG_] (core.cljs:560)
env.evalLoad @ app.js:1552
(anonymous) @ app.js:3123
the error doesn’t even relate to including the package
Just as before - set your browser to break on both caught and uncaught exceptions and see what the triggered breakpoints show you.
Hello all,
const query = async ({data}) => {await supabase.from('items').insert([data])}
Does someone know how I could translate this little JS snippet in CLJS? I know that await does not really exist in CLJS, I've checked the library promesa as well but did not get how I could translate the said snippet.
Thanks in advance for the help 🙂 !
Something that you await on in JS simply returns a promise. You can interop with that promise by using its API. The above snippet should end up something like this in CLJS:
(defn query [^js obj]
(let [data (.-data obj)]
(-> supabase (.from "items") (.insert #js [data]))))
Since insert
already returns a promise, all is fine.Now heres the problem, insert does not exactly returns a promise but a promiselike object... the behavior I want is triggered in JS by awaiting for the object. Otherwise it's just a normal object.
Really strange thing but in JS this works,
const query = async ({data}) => {await supabase.from('items').insert([data])}
but this does not work,
const query = ({data}) => {supabase.from('items').insert([data])}
I've tried to check to source code, it's written in typescript. Maybe using await is going to transform that object in promise? All of this is really strange, I'm not so sure this library is correctly written
Thanks for your answer !
I have to say that as well I'm not a JS/TS expert, I started this year and I want to use CLJS now so I don't spend my time writing JS and focus on writing CLJS which is more efficient
When I say this doesn't work I mean that the data is not inserted in the database
Wait, actually it wasn't supposed to work in the first place. You have braces in the body - it means that without an explicit return
statement, the return value will be undefined
.
=> x = () => {1}
() => {1}
=> x()
undefined
I'm trying it ASAP thanks. I thought it was ok because I don't care about the return value of the function only the side effect
Yes, the thing is that I know that there is a better async paradigm in CLJS (core.async) but when dealing with interop I've got no other choices. So I should mimic the same thing in CLJS, essentially creating a promise that awaits for insert
?
I think I could do it with the CLJS library promesa
Thanks for the very clear explanations !
> a better async paradigm
It's more powerful, but it doesn't mean that you should always stick to it when presented with a choice.
> I've got no other choices
Actually, you can resolve promises in a go
block - there's <p!
macro in cljs.core.async.interop
. But again - think really hard before you decide to use it. If you can use interop without entangling your code and submerging yourself into a callback hell, then definitely go with that option.
> So I should mimic the same thing in CLJS, essentially creating a promise
Why? .insert
already gives you a promise (or promise-like object, doesn't matter). Just return and use it as is. My example above should work.
I would also recommend against using any extra libraries, be it core.async
or promesa
- at least, not before you grok the basics.
You are completely right, thanks a lot @U2FRKM4TW I will look into it ! 🙂 And yes I'm still starting out, my dev workflow is still not the best so I'm a bit slow. I think you are right about keeping things simple for now. Thanks a lot and have a good day !
I tried your snippet but I have the problem I was thinking about. Here is the CLJS code,
(defn query [data] (-> sb/supabase (.from "test") (.insert #js [data])))
(set! (.. js/window -query) (query #js {:int 5})) ;; execute query and make the result available to browser
After executing this code I go to the browser console (makes more sense to use it for debug in this case than CLJS repl) and as you can see the call returned me a PostgrestFilterBuilder
that just contains the data of my request but that did not execute yet.
As you can see on the attached screenshot, the real insertion of data happens when I await for the object (so the object returned by the application of the query function)
Does that mean that I indeed need to somehow create a promise that awaits for this object ? It seems really fucked up, I don't know if the devs of this library just made things wrong or if the await/async paradigm is wrong from the beginning lol.
Any idea @U2FRKM4TW ? Thanks a lot> Does that mean that I indeed need to somehow create a promise that awaits for this object ?
No. You await a promise, not some objects.
Calling query
returns a promise - you have to await that promise to insert the data and get the result back, whatever the result is.
You can do it like this:
(-> (query #js {:int 5})
(.then (fn [result] (set! js/window -result result))))
Also, the way you're making it available to the browser should only be used in the browser and is only marginally helpful.
If you want for the result to be accessible from JS, you can simply export the relevant CLJS function and call it directly from JS.
If you want to just print the value, then replace (set! ...)
with (js/console.log result)
. If you want to be able to access that object in the JS console, right click on its printed representation and "Store object as global variable".Yup sorry I used the wrong word I meant awaiting for this promise. Make completely sense, for sure the set would need to await for the query... But I still find the promise paradigm very confusing I've been a Software Engineer for a lot of time and still have problems to grasp the concept. Now it's getting better tough thanks to you ! Thanks as well for the advices you provided, I have just one question. Can I have a empty then ? If I don't want to do anything with the result but just want something to wait for it, how would you do that? Thanks a lot everything is getting clearer now 🙏
> Can I have a empty then ?
With regular promises, you don't have to await them because they will start their work when the JS's event loop allows.
With promise-like objects you can't know when the work starts, so you gotta call .then
. An "empty then" would be just (.then promise (fn [_]))
.
Better yet, read up on JS Promise API and understand why you need to use .catch
as well. If that promise-like object starts the work when .catch
is called without .then
then do that.
Thanks a lot @U2FRKM4TW I think I don't have any more questions, back to code 🎉
Why can't I run (eval (read-string "(+ 1 2)"))
in ClojureScript? Is it because the ClojureScript compiler isn't self hosted?
I would expect being able to run something like
(def a 1)
(eval (read-string "(+ a 2)"))
=> 3
like I can do in Clojure. Is there a way to do the same in ClojureScript?Yes, you can self-host it. But AFAIK the process is rather involved and will drastically increase the bundle size, if you care about that.
For most of the cases you can use a perfect tool called SCI by the best magician I know. cc @U04V15CAJ
@U04V15CAJ Do you know if I could use sci with access to the full JS/cljs context in the browser, to ex. be able to run:
(let [input (js/window.prompt "insert-cljs-code-here> ")]
(eval input))
;
; insert-cljs-code-here> (js/alert "Hello")
; => ... triggers a browser alert
;
Yes, you can. This is documented in the README, you can also check nbb’s or scittle’s source. It’s recommended to use the newest SCI from git master.
@U0H9BVB98 So the option is {:classes {'js goog/global :allow :all}}
(I'm finally home so I can type this ;))
@U04V15CAJ Cool, thanks for the help!
I've created a smallish project and it seems like it's not able to pick up the cljs.core
ns, do you know what I'm doing wrong here?
# index.html
<head>
<script src="/js/main.js"></script>
</head>
# shadow-cljs.edn
{:source-paths ["src"]
:dependencies[[borkdude/sci "0.2.6"]]
:dev-http {3000 "public"}
:builds {:dev {:target :browser
:output-dir "public/js/"
:modules {:main {:init-fn my-ns.main/-main}}}}}
# my-ns/main.cljs
(ns my-ns.main
(:require [sci.core :as sci]))
(defn -main [& _]
; This prints "Hello" in browser console
(sci/eval-string "(println \"Hello\")" {:namespaces {'clojure.core {'println println}}})
; ... and so does this
(sci/eval-string "(js/console.log \"Hello\")" {:classes {'js goog/global :allow :all}})
; This outputs a crash in browser console
(sci/eval-string "(println \"Hello\")" {:classes {'js goog/global :allow :all}}))
@U0H9BVB98 What is the question?
The println
example: this is kind of expected, since println is a side effect which isn't allowed by default, I would just override it like you did in the first example for now. I'm currently working on making this easier
I am trying to use react-select with multi true in reagent. But when I am clicking on any item from dropdown it is not getting displayed. Can anybody help ?
(defn select-input [options data] (let [value (r/atom []] [:div.field [:label.label label] [:div.control [:div.select [:> js/Select { :class "form-input" :value @value :options test-options :multi true :placeholder "Select..." :on-change (fn [event] (println event) (doseq [ev event] (swap! value conj (:value (js->clj ev :keywordize-keys true))) ) ) }] ]]]))
When you change the contents of value
, the component is re-rendered.
Re-rendering calls select-input
.
It throws away the old value
and creates a new one with the default value []
.
Replace let
with reagent.core/with-let
(change the namespace according to your imports).
Thanks it worked but it is showing multiple times. So I need to just use (reset! value event)
Has anyone done anything with an electron app using native messaging to an extension in clojurescript?