Fork me on GitHub
#clojurescript
<
2021-07-20
>
Oliver George01:07:16

Quick sanity check. I'm having trouble requiring a node module where default is name mangled to default$ then doesn't resolve correctly. This...

(ns interop.masked-input
  (:require [react-text-mask :as MaskedInput]
            [reagent.core :as r]))

(def masked-input (r/adapt-react-class MaskedInput/default))

Oliver George01:07:56

Turns into

// Compiled by ClojureScript 1.10.773 {:target :nodejs}
goog.provide('interop.masked_input');
goog.require('cljs.core');
goog.require('reagent.core');
interop.masked_input.node$module$react_text_mask = require('react-text-mask');
interop.masked_input.masked_input = reagent.core.adapt_react_class.call(null,interop.masked_input.node$module$react_text_mask.default$);

Oliver George01:07:21

with cljsrn I happily set :language-out to something very recent. is that the safe/correct approach for web browser targets?

Oliver George02:07:39

I think this indicates I can feel good about compatibility with :language-out :es6 https://caniuse.com/?search=es6

xlfe02:07:33

hmm. wild guess here but would the following work?

(ns interop.masked-input
  (:require [react-text-mask :default MaskedInput]
            [reagent.core :as r]))
(def masked-input (r/adapt-react-class MaskedInput))

zackteo04:07:09

Does anyone know how to translate this into clojurescript ...

<MapConsumer>
  {(map) => {
    console.log('map center:', map.getCenter())
    return null
  }}
</MapConsumer>
I only know I can do
[:> MapConsumer]
but i'm not sure how to do the arrow function and where it should be

zackteo04:07:51

Okay seems like it is ...

[:> MapConsumer
    (fn [map] (do (js/console.log map)
                  nil))]

lsenjov04:07:59

You can remove the do

lsenjov04:07:06

fn has an implicit body

lsenjov04:07:13

But otherwise looks right

lsenjov04:07:24

Errrr actually hold on

lsenjov04:07:29

What exactly is MapConsumer?

lsenjov04:07:23

The function is correct, but if it's reagent land you're looking at something like [MapConsumer {} (fn ...)]

lsenjov04:07:29

Not sure what it looks like in raw cljs land

zackteo04:07:42

Honestly, that's something I'm asking myself now. Leaflet react doesn't have amazing documentation https://react-leaflet.js.org/docs/api-map/#mapconsumer but using leaflet directly and creating my own reagent components is too tough for me for now

zackteo04:07:31

Yeah I don't need the {} it is assumed to be empty

lsenjov04:07:57

Oh that's what it is, you may want to look into.... one tic

lsenjov04:07:24

Specifically reagent/adapt-react-class

zackteo04:07:04

@lsenjov just so that I don't get stuck if I need this ... do you happen to know how to the equivalent for ^

lsenjov04:07:26

There's a big rabbit hole here of what useMap is doing and what it's setting

lsenjov04:07:19

Wait, is map just a global?

lsenjov04:07:03

(fn my-comp [] (js/console.log (.getCenter (js/useMap))))
(fn my-map-comp [] [(adapt-react-class MapContainer) {:center [50.5 30.5] :zoom 13} [my-comp]])

lsenjov04:07:30

console.log returns null anyway, so we shouldn't need to do any let bindings in there

lsenjov04:07:53

Oh, I forgot the extra bit of text in console.log, but you can add that in yourself

lsenjov05:07:11

Note again that this is reagent land, I'm not sure how this works in react cljs or raw cljs

zackteo05:07:54

Yeah am using reagent. I think the [my-comp] doesn't seem to work

zackteo05:07:59

:thinking_face:

jaju05:07:53

I can only guess - but do you see any errors about my-comp not being a valid component? It doesn’t have any visual elements in it, and I’d guess reagent barfs at that. I don’t know the purpose of my-comp, but a crude attempt could be to wrap it in a [:div… and display: none it and figure out the logic part?

zackteo05:07:46

@jaju @lsenjov okay will try that. I think this isn't as important for now. Will look into it again if I encounter it again :x but thank for your help!!

👍 4
olaf05:07:23

Hey, I build the app with shadow-cljs release app , filling the release/public dir with js files [app.js, utils.js] and no html files. So for run the static app I added a page.html importing css and js files in order or dependency. Everything is loaded without console error, but the page is blank. What I'm missing?

jaju05:07:40

Stupid question - did you call the init -equivalent JS function in the HTML? Or, is it called by default in your main namespace, so as the initialize the app?

thheller05:07:01

@eliascotto94 what is in your html used for the development builds?

olaf06:07:34

@thheller I don't use any html for the build. Is created in Clojurescript when the user go on the route (reitit-ring/router [["/" {:get {:handler index-handler}}]]) index-handler is a hiccup component

(def mount-target
  [:div#app
   [:h2 "Welcome to 7GUIs"]
   [:p "please wait while shadow-cljs is waking up ..."]
   [:p "(Check the js console for hints if nothing exciting happens.)"]])

(defn head []
  [:head
   [:meta {:charset "utf-8"}]
   [:meta {:name "viewport"
           :content "width=device-width, initial-scale=1"}]
   (include-css (if (env :dev) "/css/site.css" "/css/site.min.css"))])

(defn loading-page []
  (html5
   (head)
   [:body {:class "body-container"}
    mount-target
    (include-js "/js/utils.js")
    (include-js "/js/app.js")
    [:script "sevenguis.core.init_BANG_()"]]))

(defn index-handler
  [_request]
  {:status 200
   :headers {"Content-Type" "text/html"}
   :body (loading-page)})
I'm using this template https://github.com/reagent-project/reagent-template

thheller06:07:45

loading-page is your html and this emits a <script> tag calling your init function. so thats missing from your page.html

olaf06:07:01

Yep, I missed that. In any case now says sevenguis is not defined.

thheller06:07:43

if you want to do it correctly you remove that script tag entirely and in your build config use :init-fn to call it for you. if you prefer to keep the script tag you need to ^:export your init! function. so (defn ^:export init! [] ...)

olaf07:07:52

:modules {:utils {:entries [sevenguis.utils]}
          :app   {:entries [sevenguis.core]
                  :depends-on #{:utils}
                  :init-fn sevenguis.core/init!}}
but I get an error
An error occurred when calling (sevenguis.core/init!)
Uncaught Error: No matching clause: 

thheller07:07:00

well you are using a router. so I suspect that it doesn't have a proper route? what is the rest of the stacktrace?

thheller07:07:34

the template you used seems to expect some kind of server? if you just have a static html file things might be different

olaf07:07:08

An error occurred when calling (sevenguis.core/init!) app.js:979:357
Uncaught Error: No matching clause: 
    QD file:///Users/elia/dev/elias94.github.io/js/app.js:580
    VD file:///Users/elia/dev/elias94.github.io/js/app.js:581
    AA file:///Users/elia/dev/elias94.github.io/js/app.js:433
    VD file:///Users/elia/dev/elias94.github.io/js/app.js:581
    <anonymous> file:///Users/elia/dev/elias94.github.io/js/app.js:979
    <anonymous> file:///Users/elia/dev/elias94.github.io/js/app.js:980
app.js:580:376
I don't know about the route because if i run shadow-cljs watch app works correctly. So I suspect it expect a server, which I don't need because is a static app. I will remove all the unnecessary stuff from the template or switch to a new one. Thanks in the meantime, I love this community!

thheller07:07:07

you can try shadow-cljs release app --debug to get a better idea whats actually wrong

thheller07:07:32

my guess would be that you have a router and a case somewhere that usually expects a "/" or so route which now doesn't exist

olaf07:07:35

An error occurred when calling (sevenguis.core/init!) append.js:2:57
Uncaught Error: No matching clause:  core.cljs:856:2
    $sevenguis$core$page_for$$ core.cljs:856
    $sevenguis$core$init_BANG_$$/< core.cljs:887
    $accountant$core$dispatch_current_BANG_$$ core.cljs:169
    $sevenguis$core$init_BANG_$$ core.cljs:893
    <anonymous> append.js:2
    <anonymous> app.js:14733
cljs/core
(defn page-for [route]
  (case route
    :index #'home-page))

(defn current-page []
  (fn []
    (let [page (:current-page (session/get :route))]
      [:div
       [page]
       [:footer
        [:p]]])))

(defn mount-root []
  (rdom/render [current-page] (.getElementById js/document "app")))

(defn init! []
  (accountant/configure-navigation!
   {:nav-handler
    (fn [path]
      (let [match (reitit/match-by-path router path)
            current-page (:name (:data  match))
            route-params (:path-params match)]
        (session/put! :route {:current-page (page-for current-page)
                              :route-params route-params})
        (clerk/navigate-page! path)))
    :path-exists?
    (fn [path]
      (boolean (reitit/match-by-path router path)))})
  (accountant/dispatch-current!)
  (mount-root))
clj/handler
(def app
  (reitit-ring/ring-handler
   (reitit-ring/router
    [["/" {:get {:handler index-handler}}]])
   (reitit-ring/routes
    (reitit-ring/create-resource-handler {:path "/" :root "/public"})
    (reitit-ring/create-default-handler))
   {:middleware middleware}))

thheller07:07:00

as I said. see the case route. that doesn't have a default case so it fails since you are not getting :index

thheller07:07:34

can't see your router so not sure what you actually get, server side parts don't matter here

🙌 2
Elias Elfarri07:07:41

I have an issue with Re-frame's event handler reg-event-db

Elias Elfarri07:07:23

i am trying to, by clicking a button, to add extra information into an array in the appdb. But what is currently happening is that the data that is stored in the key is being replaced

Elias Elfarri07:07:55

how do i make sure that the data is added to the array?

Elias Elfarri08:07:21

code:

(reg-event-db
  :fetch-data
  (fn [db [_ map]]
    (let [{:keys [a]} map]
    (assoc-in db [:data :a] a))))

Elias Elfarri08:07:47

and currently in the appdb :data :a holds [1,2,3]

Elias Elfarri08:07:55

and i am trying to add 4,5,6

Elias Elfarri08:07:04

so it becomes [1,2,3,4,5,6]

p-himik08:07:31

For future reference, there's #re-frame But in this particular case, your question is actually about core Clojure API. You're using assoc-in that is designed to replace the value. You should use something else, like a combination of update-in and into.

zackteo08:07:46

May I know how to access properties of a javascript object in clojurescript?

p-himik08:07:03

Web search query: clojurescript javascript interop. Thoroughly documented.

zackteo08:07:17

I did, am quite confused why I can't further access it using -property (.. layer -feature -geometry -properties)

p-himik08:07:00

One small thing that's not that well documented but still is important - when you define a symbol that names a JS object, add ^js in front of it, be it in a function signature, in a def, in a let - all the same.

p-himik08:07:53

I have no idea why you can't access -property - maybe it's not there?

cljs.user=> (def x #js {:a #js {:b 1}})
#'cljs.user/x
cljs.user=> (.. x -a -b)
1

zackteo08:07:05

I can access up to -property but although I can console.log out the javascript object if I will get something like `Cannot infer target type in expression (. (. (. (. layer -feature) -geometry) -properties) -gid)` when I add the -gid part

zackteo08:07:15

I can only stringify it for now

p-himik08:07:17

`Cannot infer target type in expression` is a message from the compiler. Add ^js in front of layer.

zackteo08:07:10

aget works

zackteo08:07:39

How do I add ^js exactly?

p-himik08:07:05

Do not use aget - it's for arrays only.

p-himik08:07:11

Just replace layer above with ^js layer.

zackteo08:07:44

okay it works! so what was the issue actually? o:

p-himik08:07:42

The article above and the others that it links to provide an explanation that's much better and more detailed than the one I could come up with. ;)

zackteo08:07:16

okay! 🙂 thank you

zackteo08:07:37

@U2FRKM4TW for some reason, using aget allows me to get the value and not the conventional method. Seems like the clojurescript aget docs tell me to use goog.object/get

p-himik08:07:50

As I said - simply do not use aget for anything that's not a JS array. goog.object/get is a less convenient alternative to the direct interop with ^js.

zackteo08:07:53

Right o: cause my issue was that when I console.log, with (.. ^js layer -feature -geometry -properties -lu-desc) I get undefined but (goog.object/get (.. ^js layer -feature -geometry -properties) "lu-desc") gives me "BUSINESS 2"

p-himik08:07:57

Oh, that is interesting - the compiler doesn't like dashes in property names, it seems.

zackteo08:07:14

yeah ... i thought that might be the case .... ><

p-himik08:07:50

Yeah, -lu-desc gets converted to lu_desc. Alright, this right here is a good use case for goog.object/get then.

zackteo09:07:52

was just trying to keep to the standard of using dashes for keyword names :x

p-himik09:07:33

Oh, so layer is a data structure that you control? Why use a JS object at all then?

zackteo09:07:24

I pass my geojson data into leaflet first and then I'm accessing the :properties of data in the popups

p-himik09:07:39

Ah, so you control the properties, I see. Maybe it (or at least a field within it with a simple name) could be set to a CLJS object. But if you're fine with interop or goog.object/get, then there's no need.

👍 2
Jason12:07:29

Curious, does anyone not like hiccup in reagent? If so why? I’m trying to decide if I’ll go down that route or not for a hobby project

lilactown14:07:32

I like the dx of editing hiccup but it does have a small performance impact, and can be tricky to parse

Richard Bowen13:07:02

Hey. How do you import a font from ClojureScript? In JavaScript it's import "@fontsource/roboto", which imports css styles what's the ClojureScript equivalent? I'm using material-ui.

p-himik14:07:33

Just include the necessary CSS statement in a relevant CSS file. It's not CLJS- or JS-specific.

Richard Bowen14:07:16

Ah, indeed. Will give it a go.

Richard Bowen14:07:49

No CSS files are defined in the project though, it's all managed from ClojureScript.

p-himik14:07:10

You can add it into your index.html via <style> - no need for a separate file.

Richard Bowen14:07:58

I could but I have the font family defined in a theme file for configuring the material-ui theme.

p-himik14:07:47

How do you define the theme and use it in CLJS?

Richard Bowen14:07:11

With material-ui, you can define a theme object that you define to customize the theme.

p-himik15:07:12

Alright, so it just mentioned the font by name? Then simply put that <style> above the main script tag.

Richard Bowen15:07:04

body { 
    font-family: "Roboto", sans-serif;
}
?

p-himik15:07:22

Ah, it's a self-hosted font. And I think import "@fontsource/roboto"; inlines that font in your JS bundle. So it's more involved than just adding a tag. Your CSS above is an instruction to use the font, but it doesn't say where to get it from.

p-himik15:07:55

Three options that I know of: • Make your backend serve that font and specify it with @font-face in your CSS • Inline the fonts yourself via a data URL inside @font-face: https://gist.github.com/minosiants/9891317 • Use something like webpack to do it for you One other option is to get the font from a CDN. It's like the first option above, but without hosting anything yourself.

Richard Bowen15:07:34

Thanks, The CDN option works but I wanted to go with self hosting.

dnolen14:07:05

@olivergeorge should be ok - but note there's a better way now

Oliver George00:07:19

Thanks David. I guess you mean the new approach to using https://clojurescript.org/guides/webpack but I might missing something. (The new webpack is https://ask.clojure.org/index.php/9602/recommended-webpack-config-for-clojurescript-bundle-target)

dnolen15:07:33

@rbowen I'm pretty sure that is not a JavaScript feature? that's a feature of build tooling

dnolen15:07:54

such things are not possible directly in ClojureScript and that's unlikely to change

dnolen15:07:28

however this problem was already considered some time ago - the :bundle target allows you to write these patterns outside of your ClojureScript

dnolen15:07:51

meaning we punted the problem to where it belongs - not in the language but in tooling

Richard Bowen15:07:32

So the tooling would be shadow-cljs? It should be able to handle that correct? I came across :ignore-asset-requires true|false but it seems to have no effect when set to false, assuming it is true by default.

dnolen15:07:31

I don't know if shadow-cljs supports such things directly

thheller15:07:18

no support for css no. the ignore is only for npm packages that might want to require css directly which would otherwise break

Richard Bowen15:07:40

Okay, got it, thanks.

beders17:07:16

Can someone point me in the right direction with regards to symbol resolution in CLJS? This will work in CLJ but not CLJS:

(resolve (:boo (clojure.edn/read-string (pr-str {:boo 'inc}))))
=> #'clojure.core/inc
In CLJS you’ll get an exception:
clojure.lang.ExceptionInfo : 
    java.lang.AssertionError : Assert failed: Argument to resolve must be a quoted symbol

beders18:07:12

i.e. I’d like to read some EDN and then resolve any symbol in there

dpsutton18:07:49

clojurescript cannot do any runtime evaluation. the repl has some limited at dev time

beders18:07:07

One would think it should work since ns-publics seems to work. Not sure if it survives :advanced compilation

thheller18:07:17

@beders all those things are macros. so they all work at compile time in a static context. none of them take any arguments that resolve at runtime since that is technically impossible with :advanced

👍 2
beders18:07:21

thanks Thomas, looks like I need to roll my own limited symbol resolution

thheller18:07:54

if you ^:export all the things that you want resolvable that still works and you can look up the things by name after. vars don't really exist though so you just get the thing not a var like you would in CLJ

beders18:07:56

trying not to pollute. So maybe something using ns-publics or a hand-rolled mapping. All things I need to resolve are static.

beders18:07:34

thanks for the explanation @thheller You are the MVP of CLJS!

kozmicluis19:07:07

does anyone know how I can print the value of an atom straight in the chrome console?

p-himik19:07:29

(js/console.log @my-atom)

dnolen19:07:58

@luishendrix92 ClojureScript has deterministic munging

dnolen19:07:25

w/ Dev tools autocompletion it's pretty easy to use anything in ClojureScript directly as long as you're not using advanced compilation

dnolen19:07:56

console.log(cljs.core.deref(your_ns.core.your_atom)) or whatever