Fork me on GitHub
#clojurescript
<
2020-11-02
>
Ronny Li01:11:57

Hi I'm running into a circular dependency with reitit because my view is linking to another page, which requires the route but the route depends on the view. I imagine this is a common issue that beginners run into but does anyone have a simple code example I could follow for breaking the loop? Here's what I currently have:

;; component

(ns
  app.components
  (:require [reitit.frontend.easy :as rife]))

(defn card []
      [:div {:on-click #(rife/push-state ::card-details)}
       "Foo"])

(defn card-details []
    [:div "Bar"])

;; ================
(ns app.navbar
  (:require
    [app.components :as c]
    [reitit.frontend :as rif]
    [reitit.frontend.easy :as rife]))

(def routes
  [["/card"
    {:name ::card
     :view c/card}]

   ["/card-details"
    {:name ::card-details
     :view c/card-details}]])

p-himik06:11:45

Your views do not require the routes - they require the keyword. Just use the full keyword name, without aliases, and you're good to go.

p-himik06:11:08

In your particular code above, the only mistake that I can see is that you're using ::card-details in both NSs.

p-himik06:11:47

Since :: gets substituted with the current NS, they end up being completely different keywords.

p-himik06:11:31

So you should either spell out the full namespace in one of the usages or just switch to simple keywords without namespaces (assuming reitit allows you to do that).

Ronny Li14:11:03

thank you so much! I was wondering what the :: convention meant...

Rahul P.03:11:09

Hey all, I'm new to cljs and writing a search function for my users table. I am having difficulties accessing the value of my input element in my core.cljs

[:card_index_dividers:
    [:h4 "Search User: "]
    [:input {:id "searchKey"
	         :type "text"}]
    [:button {:id "searchButton"
              :onClick (fn [e]
                         (getUserByName "TODO Pass User name here"))}  "Search"]]
Can anyone help me or refer any specific guide or project?

phronmophobic03:11:33

I would look into clojurescript interop with js. here's a primer I found that looks reasonable, https://lwhorton.github.io/2018/10/20/clojurescript-interop-with-javascript.html I don't have a cljs repl available, but I think the following should print the user name:

[:card_index_dividers:
 [:h4 "Search User: "]
 [:input {:id "searchKey"
	  :type "text"}]
 [:button.searchButton {:onClick (fn [e]
                                   (let [input (js/document.getElementById "searchKey")]
                                     (prn (.-value input))))}  "Search"]]

👍 3
Ritchie Young03:11:17

Even simpler:

[:card_index_dividers:
    [:h4 "Search User: "]
    [:input {:id "searchKey"
	         :type "text"}]
    [:button {:id "searchButton"
              :onClick (fn [e]
                         (getUserByName (-> e .-target .-value))}  "Search"]]

phronmophobic04:11:18

wouldn't target be the button in this case?

Rahul P.04:11:02

@U7RJTCH6J It gave me reading null value with your solution

core.cljs:280 Uncaught TypeError: Cannot read property 'value' of null
    at onClick (core.cljs:280)
    at Object.global.invokeGuardedCallback (ReactErrorUtils.js:71)
    at executeDispatch (EventPluginUtils.js:86)
    at Object.executeDispatchesInOrder (EventPluginUtils.js:109)
    at executeDispatchesAndRelease (EventPluginHub.js:44)
    at executeDispatchesAndReleaseTopLevel (EventPluginHub.js:55)
    at Array.forEach (<anonymous>)

phronmophobic04:11:22

what happens if you type document.getElementById ("searchKey") into the js console?

phronmophobic04:11:29

is the input not having it's id set for some reason?

Rahul P.04:11:19

My bad. The username I was passing had some white spaces. All Good now.

Rahul P.04:11:48

Thanks for the help.

Ritchie Young06:11:41

Ah, you’re right @U7RJTCH6J. My bad.

tugh08:11:31

i'm trying to set a var from another ns but i got no luck. here is what i'm trying:

(ns chatbot.flow.conversational.core)

(defn init []
  (set! 'chatbot.flow.views/test123 123))
here is the error from shadowcljs build logs:
--------------------------------------------------------------------------------
  11 | 
  12 | (defn init
  13 |   []
  14 |   (set! 'chatbot.flow.views/test123 123))
---------^----------------------------------------------------------------------
null
set! target must be a field or a symbol naming a var at line 14 chatbot/flow/conversational/core.cljs
init fn will be called after the module is loaded. i'm trying to hide the details of which components are in use. there will be different modules like conversational, form, etc. can someone point out what i'm missing? why i'm getting the above error even though i'm passing a symbol to set! ?

tugh09:11:20

btw i'm not sure if this is the correct way of setting vars from different namespaces. if there are other approaches i'll be glad to know. thanks everyone 🙂

thheller09:11:42

you cannot "cheat" creating circular dependencies this way

thheller09:11:09

your example is a bit too abstract to provide alternatives but basically you need one ns that can be shared anywhere that has a reference to an atom or so

thheller09:11:31

I typically have a (ns my.app.env) (defonce my-global-state-ref (atom {})) which all other namespace can use to coordinate

tugh09:11:45

thanks @thheller atoms would be ugly for my situation b/c i will set reagent view functions and i dont want to deref every time i want to use a view function. let me explain it in more detail: my re-frame app has 3 modules. core, conversational , form . core contains the logic and other parts just contain components. i simply want to render different components based on flow type. both conversational and form components will have the exact same names but will render different html. for example, both will have a component named user-input . the only difference of these views are the produced html. core module fetches the flow data from an API and loads the required module with shadow.loader/load. after loading the required module i don't want the core to know about which components are in use so i thought binding them to another namespace chatbot.flow.views can solve my problem. hope this gives you more insight about what i'm trying to achieve.

thheller09:11:51

my use of atom does not mean that you actually have to use an atom

thheller09:11:09

you can use (def a "foo") and in your module (set! env/a "bar") if you really have to

tugh09:11:12

so, do you mean that i should def it first in chatbot.flow.views and then bind(using set!) whatever i want from another namespace? is this why i'm getting the above error?

thheller09:11:41

the error you are getting is that set! does not accept a quoted symbol

thheller09:11:32

you can use set! if the namespace is properly required and aliased. doing that is most likely a code smell and something you should rarely do.

tugh09:11:40

but this will break my code splitting. i will require chatbot.flow.views on all three modules

thheller09:11:35

I'm not sure how you would build an app in core if that doesn't know what is happening

thheller09:11:30

I would strongly recommend not using set! and instead have some of your state data reflect what it is supposed to do

👍 3
👀 2
tugh09:11:48

i will implement the events, subs, and other details in `core` and component modules will use them. i basically want to decouple rendering details from core

mac12:11:56

I am getting "Error: Require clojure.test.check and clojure.test.check.properties before calling check." when trying to use cljs.spec.test.alpha/check but I have required [clojure.test.check] and [clojure.test.check.properties] already. What might I be doing wrong. I am using shadow-cljs.

thheller12:11:06

@mac make sure to require the CLJS variants and CLJ variants in the correct places. can't tell more from the error description but you are mixing clojure.test and cljs.spec which is confusing

mac12:11:39

@thheller I am requiring the following in a clsj namespace, is that wrong? If so what should I be requiring?

[clojure.test.check]
   [clojure.test.check.properties]
   [cljs.spec.alpha :as sa]
   [cljs.spec.test.alpha
    :refer-macros [instrument check]]

mac12:11:12

cljs.spec.test.alpha checks for the presence of clojure.test.check and clojure.test.check.properties

thheller12:11:16

sorry I can't answer that. the context of WHERE you are using this matters.

mac13:11:57

@thheller I am using it in a cljs file. Is there further info regarding where I can provide?

thheller13:11:16

the full stacktrace for example

mac13:11:57

@thheller There is no stacktrace, just the exception.

thheller13:11:45

there always is a stacktrace 😛

thheller13:11:02

when do you get the error? REPL?

mac13:11:44

When I call cljs.spec.test.alpha/check

thheller13:11:53

HOW are you calling it?

thheller13:11:14

I do not know what you are doing so please provide more context than just the error 😛

thheller13:11:44

you are calling it from the REPL? you are calling it in a function when compiling?

thheller13:11:02

do you get the error in the shadow-cljs compile log?

mac13:11:06

I am calling it from emacs using Cider. There is no error in the compile log. I have shadow-cljs watch running and everything compiles.

mac13:11:30

This is the ful code.

(ns test 
 (:require
   [clojure.test.check]
   [clojure.test.check.properties]
   [cljs.spec.alpha :as sa]
   [cljs.spec.test.alpha
    :refer-macros [instrument check]]
   ))

(defn foo [])
(sa/fdef foo :args (sa/cat))
(instrument `foo)

(check `foo)

thheller13:11:31

works fine for me?

thheller13:11:35

test=> (check `foo)
[{:spec #object[cljs.spec.alpha.t_cljs$spec$alpha77660], :clojure.spec.test.check/ret {:result true, :pass? true, :num-tests 1000, :time-elapsed-ms 137, :seed 1604322614082}, :sym test/foo}]

thheller13:11:36

just did all the forms in shadow-cljs node-repl

thheller13:11:44

can't comment on the emacs/cider bits much. I'm guessing it is maybe mixing CLJ and CLJS modes?

mac13:11:29

I get the below doing the same:

shadow-cljs node-repl
shadow-cljs - config: /home/mac/projects/hypercontracts/shadow-cljs.edn
shadow-cljs - connected to server

cljs.user=> shadow-cljs - #12 ready!

(ns test 
 (:require
   [clojure.test.check]
   [clojure.test.check.properties]
   [cljs.spec.alpha :as sa]
   [cljs.spec.test.alpha
    :refer-macros [instrument check]]
   ))
nil
test=> (defn foo [])
#'test/foo
test=> (sa/fdef foo :args (sa/cat))
test/foo
test=> (instrument `foo)
[test/foo]
test=> (check `foo)
#object[Error Error: Require clojure.test.check and clojure.test.check.properties before calling check.]
test=> 

thheller13:11:55

odd. which version is this?

thheller13:11:48

I have [org.clojure/test.check "1.1.0"]

mac13:11:11

@thheller this is output from shadow-cljs info in the project:

=== Version
jar:            2.11.5
cli:            2.11.5
deps:           1.3.2
config-version: 2.11.5

thheller13:11:44

no I meant the dependency version

thheller13:11:55

shadow-cljs version is fine

mac13:11:07

@thheller I have the same version of org.clojure/test.check

thheller13:11:54

hmm not a clue. works fine on an empty test project.

thheller13:11:15

did you try restarting the shadow-cljs process? maybe its just in some weird state.

mac13:11:15

@thheller I can try again.

mac13:11:43

@thheller Made no difference.

mac13:11:53

@thheller I just spun up a new project and it works like it did for you. So I guess something in our current project is interfering somehow. But how to find what.

thheller13:11:10

first instinct is always dependency conflicts

thheller13:11:34

can't think of anything else making this fail

mac13:11:06

@thheller Yeah, I looked at shadow-cljs info. There is a primary dependency on [org.clojure/spec.alpha "0.2.176"] but that is not in shadow-cljs.edn.