Fork me on GitHub
#clojurescript
<
2023-01-29
>
Clojuri0an08:01:24

How to represent an array in clojurescript?

Clojuri0an08:01:59

I'm confused because I don't understand why this works

(defn render-content []
  (println "test rendering")
  (println(. js/document -body))
  (m/render (. js/document -body) (m "h1" "test"
            (m "h1" "my epic header")
            (m "h2" "my epic subheader"))))
Where m-render requires an array

Clojuri0an08:01:13

Anyone know why I can't get this to render?

Clojuri0an08:01:28

(let my-pictures #js {:view (fn [] (        
       (m "main" #js {:class "container"}
         (m "article"
            (m "h1" #js {:role "button"} "My Name")
            (m "h2" "My Pictures")))))})
                             

(defn render-content []
  (println "test rendering")
  (println my-pictures)
  (println(. js/document -body))
  (m/mount (. js/document -body) my-pictures))


(defn init []
  (.addEventListener js/document "DOMContentLoaded" render-content))

thheller08:01:00

note your extra pair of ()

thheller08:01:20

basically you have ((m "main" ...))

thheller08:01:34

which means you are calling the result of (m "main" ...) as a function

thheller08:01:25

and your let is borked

thheller08:01:28

(let [my-pictures #js {:view (fn [] (m "main" #js {:class "container"}
                                      (m "article"
                                        (m "h1" #js {:role "button"} "My Name")
                                        (m "h2" "My Pictures"))))}])

thheller08:01:36

or I guess that was supposed to be a def?

Clojuri0an08:01:55

I'm not sure which I should use

Clojuri0an08:01:03

The js is var

Clojuri0an08:01:09

So I think def probably makes more sense

Clojuri0an08:01:57

that fixed it with def

thheller08:01:59

let would be what you use for JS vars when its only in the function scope

thheller08:01:10

def is for "globals"

thheller08:01:43

(defn render-content []
  (let [my-pictures #js {:view (fn [] (m "main" #js {:class "container"}
                                        (m "article"
                                          (m "h1" #js {:role "button"} "My Name")
                                          (m "h2" "My Pictures"))))}]
    (println "test rendering")
    (println my-pictures)
    (println (. js/document -body))
    (m/mount (. js/document -body) my-pictures)))

thheller08:01:03

or

(def my-pictures #js {:view (fn [] (m "main" #js {:class "container"}
                                     (m "article"
                                       (m "h1" #js {:role "button"} "My Name")
                                       (m "h2" "My Pictures"))))})

(defn render-content []
  (println "test rendering")
  (println my-pictures)
  (println (. js/document -body))
  (m/mount (. js/document -body) my-pictures))

Clojuri0an08:01:52

that makes sense ty, do you recommend a specific scope over the other?

Clojuri0an08:01:25

also any stuff you recommend for data structures?

thheller08:01:22

if render-content is the only place where this is referenced use let

thheller08:01:29

if you need it elsewhere you def

thheller08:01:55

if its only constructed once and takes no other parameters like it currently does maybe use def

Clojuri0an08:01:01

TypeError: fexpr__12320.call is not a function
at line
(let my-pictures #js {:view (fn [] (
Seems I'm calling the function incorrectly

Clojuri0an08:01:31

fixed was minor syntax problems

mesota10:01:20

Hello, I have a dropdown with search input. I am using re-frame. When the search input is in focus, the dropdown shows. When the input loses focus, the dropdown hides. When I click on the dropdown items, the item is supposed to be selected and the dropdown should hide. However, the onblur of the input element “hijacks” the onclick event of the dropdown item. I.e, the latter, and thus the item selection, never happens. How can I fix this? Thanks!

p-himik10:01:12

Impossible to tell without having the code or the documentation for that dropdown component.

p-himik10:01:20

As a kind of hand-wavy response - don't hide the dropdown on blur immediately, give the other event a chance to be handled.

mesota10:01:45

I thought the code would be long to post here. But here goes the clunky code. defn geo-zone-selector [] (let [state (r/atom {})] ;; you can include state (r/create-class {:component-did-mount (fn [] (reset! state (assoc @state :drop-down-visible false))) ;; ... other methods go here ;; name your component for inclusion in error messages :display-name "complex-component" ;; note the keyword for this method :reagent-render (fn [] (let [geozones @(rf/subscribe [:geozones]) country (:country geozones) gettext (fn [e] (-> e .-target .-value)), getdatavalue (fn [e] (-> e .-target .-dataset .-value)), getdatafname (fn [e] (-> e .-target .-dataset .-fname)), getlinktext (fn [e] (-> e .-target .-text)), spaces (fn [n] (str (apply str (repeat n "\u00A0")))), change-geozone-input (fn [e] (reset! state {assoc state :geozone-input-value (gettext e)})) select-geozone (fn [e] (reset! state {:drop-down-visible false :geozone-input-value (string/trim (getlinktext e)) :selected-geozone {:name (string/trim (getlinktext e)) :code (getdatavalue e)}}))] [:div.field [:label.label "Geographic Zone"] [:div.control [:div {:class "dropdown is-active"} [:div {:class "dropdown-trigger"} [:div {:class "field"} [:p {:class "control is-expanded has-icons-right"} ;; on focus lost, alert ;; on focus, show dropdown [:input#geo-zone {:class "input", :type "search", :placeholder "Geographic Zone" :on-focus #(reset! state (assoc @state :drop-down-visible true)) ;; :on-blur #(reset! state (assoc @state :drop-down-visible false)) :on-hange change-geozone-input :value (:geozone-input-value @state)}] [:span {:class "icon is-small is-right"} [:i {:class "fas fa-search"}]]]]] [:div {:class "dropdown-menu", :id "dropdown-menu", :role "menu" :style {:display (if (:drop-down-visible @state) "block" "none")}} [:div.dropdown-content {:style {:max-height "250px", :overflow-y "scroll"}} [:div [:a {:class "dropdown-item level-1-dropdown" :on-click select-geozone :data-fname (:name country) :data-value (:code country)} (str (spaces 0) (:name country))] (for [p (:provinces country)] [:div [:a {:class "dropdown-item level-1-dropdown" :on-click select-geozone :data-fname (:name p) :data-value (:code p)} (str (spaces 5) (:name p))] (for [sgz (:districts p)] [:a {:class "dropdown-item" :on-click select-geozone :data-fname (:name sgz) :data-value (:code sgz)} (str (spaces 10) (:name sgz))])])]]]]]]))}))

p-himik10:01:15

Yeah, long code should go into an attachment or at least in a triple-backtick block.

p-himik10:01:30

Well, my hand-wavy response seems like it's your best bet - just wrap that :on-blur in a js/requestAnimationFrame, that should work.

p-himik10:01:26

An arguably better approach that would also allow having focus within a dropdown is closing the dropdown only when focus leaves the whole component.

mesota10:01:17

Interesting! I’m leaning towards the second solution. I’ll try them both. Thanks a lot!

👍 2
mesota10:01:26

It worked with the delay. For some reason, the on-blur is not propagating down to the elements. I’m sure it’s some rookie mistake somewhere. Thanks a lot!

👍 2
Clojuri0an11:01:14

I'm trying to represent the following javascript object in clojurescript

m.route(document.body, "/home", {
    "/home": Home, 
})

Clojuri0an11:01:24

Here is my attempt but it returns a map literal error

Clojuri0an11:01:44

(def router[] (m/route (. js/document -body) "/home" #js { ("/home" home-page) }))

Clojuri0an11:01:17

The map literal starting with ("/home" home-page) on line 32 column 15 contains 1 form(s). Map literals must contain an even number of forms.

Clojuri0an11:01:14

I think this means I need to add another key value pair

🧵 2
Clojuri0an11:01:05

Changing the router to (def router (m/route (. js/document -body) "/" #js { ("/home" home-page) ("/pictures" my-pictures) }))

🧵 2
thheller11:01:46

maps are {"/home" home-page} not {("/home" "homepage")}

thheller11:01:55

get rid of the extra () again

Clojuri0an11:01:13

returns JavaScript literal keys must be strings or unqualified keywords

Clojuri0an11:01:58

Refactoring to separate out the function from the object into

🧵 2
Clojuri0an11:01:30

(def router-default ( "/" #js { ("/home" home-page) ("/pictures" my-pictures) })) (defn route[router] (m/route (. js/document -body) router))

Clojuri0an11:01:10

(defn render-content [] (println "test rendering") (println my-pictures) (println(. js/document -body)) (route router-default ))

Clojuri0an11:01:51

same error of JavaScript literal keys must be strings or unqualified keywords, removing #js from the map allows the page to render and returns the following console error fexpr__12363.call is not a function at the object router-default

🧵 2
Clojuri0an11:01:47

I think maybe I'm not supposed to have (def router-default ( "/" but rather (def router-default "/" (?

Clojuri0an11:01:20

Fixing the extra ()s from function defns and key syntax of {} not {()} fixed all errors but still not rendering components, troubleshooting now

🧵 2
thheller11:01:08

you should really use an editor that can format the code for you

thheller11:01:16

makes most syntax issues very obvious

Clojuri0an11:01:48

What to use for emacs?

Clojuri0an12:01:57

clojure-mode with font-lock?

Clojuri0an12:01:13

I think that's just for syntax highlighting not linting

thheller12:01:07

sorry, don't use emacs so don't know

Clojuri0an12:01:14

I'll try flycheck-clj-kondo

Clojuri0an13:01:57

ended up just using (m/route (. js/document -body) "/" #js {"/" home-page "/pictures" my-pictures})) which just works

🧵 2
Clojuri0an13:01:30

any way to extract out "/" #js {"/" home-page "pictures" my-pictures} into an object default-router? Tried a few times and failed, not being recognized correctly as an object

🧵 2
p-himik13:01:50

You really need to start properly formatting your code here. The way you write it right now makes no sense - you wrote 2 values, one is a string "/" and the other is a JS literal, #js {...}. You can't assign them both to a single var, if that's what you want.

p-himik13:01:21

The closest alternative is to either extract them into their own respective vars or to wrap them in a vector, store it in some var, and use it with that m/route by using apply.

Clojuri0an14:01:43

Idk what your first paragraph is trying to communicate but the last clause was what I was looking for

Clojuri0an14:01:46

If you would explain what you mean by proper formatting it could be helpful to me

p-himik14:01:14

I mean code formatting in Slack messages, so that code stands out from English text.

p-himik14:01:43

And there's more to it than just that button in the WYSIWYG Slack message editor. (Oh, now it has a second button - well, that covers 90% then.)

Clojuri0an14:01:26

I'll check if I can find any tutorials on how to format code on slack, I agree with you that the formatting I have been using for code blocks has made me uncomfortable. I'm new to using slack

p-himik14:01:15

There are 2 buttons - one for in-line code, the other is for multiline code blocks. You can do the same with just the backtick - inline singular for the former, on their own line around the code block and triple for the latter. The rest 10% is code attachments - they also support syntax highlighting and are collapsible.

Clojuri0an15:01:07

I'll try out code attachments, I was annoyed that I didn't have any syntax highlighting

Clojuri0an16:01:23

My webapp project is getting bigger. I'm wanting to add more .cljs files than just app.cljs. I'm confused about several things. Firstly, what the recommended practice is for using multiple files (e.g., when one ought to create a new file instead of using the existing one). Secondly, how it is possible to work within an existing namespace in a separate file -- IIRC, ns http://myproject.frontend.app is expecting that it is used with the myproject/frontend/app.cljs path). Thirdly, when one should create a new namespace. E.g., I have http://myproject.frontend.app as my namespace for app.cljs. My assumption is that I should probably have more specific namespaces than this, but as I am a noob I do not know what the recommended practice is

thheller16:01:44

1) whatever makes sense to you. some people group by "components", some by "type", some by "topic". it really is up to you

thheller16:01:48

2) not supported

thheller16:01:46

3) http://myproject.frontend.app is fine. some people use .core, some .app, some .main. whatever makes sense to you