Fork me on GitHub
#fulcro
<
2021-07-29
>
genekim02:07:22

@tony.kay Purely FYI — I made the mistake of sending from a RAD report all of props to a mutation, and got this mysterious error:

ERROR [com.fulcrologic.fulcro.networking.http-remote:358] -  Error: Cannot write Component
It was because there was a ton of stuff in there that wasn’t ever meant to be serialized and sent over the network. Here’s a sample below. 🙂 Not sure whether there’s any additional error-checking that could be added, or just mark this up to user error. Thx!!
[{(com.example.ui.trello-mutations/archive-card
           {:trello-card/id "5f376db4cf14b01212f656b8",
            :trello-card/dateLastActivity "2020-08-15T05:20:08.971Z",
            :trello-card/idList "5f30cf009d5f7a57eacf2a07",
            :trello-card/idBoard "5a614ec5f99597ca93ac17c5",
            :trello-card/pos 2048,
            :trello-card/name "c",
            :trello-card/desc "",
            :fulcro.client.primitives/computed
            {:report-instance #unknown [object Object],
             :row-class
             #unknown function com$example$ui$trello_list_report$Card(props__49091__auto__){
                                                                                            var this__49092__auto__ = this;
                                                                                            var temp__5751__auto___120019 = cljs.core.get.cljs$core$IFn$_invoke$arity$2(options__49090__auto___119990,new cljs.core.Keyword(null,"initLocalState","initLocalState",-46503876));                                                                                       if(cljs.core.truth_(temp__5751__auto___120019)){
                                                                                                                                            var init_state__49093__auto___120020 = temp__5751__auto___120019;
...
             :com.fulcrologic.rad.report/idx 0}})
          [* :com.wsscode.pathom.core/errors :tempids]}]

tony.kay02:07:38

The encoding error message could be a lot better.

genekim03:07:07

(I pretty-printed the env to figure out what was going on — I think it was multiple MBs long, and took about 15 seconds to load into the browser console. 😆 That’s when I knew I must be putting garbage into the serializer. 🙂

tony.kay14:07:53

So I'm not sure how you got the error message you got. The line you refer to has new error message stuff on it. I think perhaps you're using an older version of Fulcro itself that doesn't have the imporved error messages?

tony.kay14:07:06

could you verify that? I'm interested in this being easier to understand.

genekim19:07:39

I’m with @U0522TWDA right now — we reproduced the problem with Fulcro 3.5.0-RC3. The error message appears to be the same with 3.5.2. Thinking this through, it’d be great had the error message suggested that during a transact! (maybe it’s name), the data could not be serialized, and it is of the type “Component”. Or something like that. Thanks!

tony.kay20:07:54

But what is the actual content of the error message @U6VPZS1EK? You only showed your print of props

tony.kay20:07:29

I think it refers to a URL, which I've updated in the online book to better explain what is going on. You're right that in debug mode I could warn if the content of a mutation's parameters is not serializable. The problem with that is if it is ONLY a local mutation, you can "get away" with doing that, so it'd have to be deeper in the bowels, which might be harder....I'd have to think about it

tony.kay20:07:52

@U6VPZS1EK @U0522TWDA I've added a new error message to transact! when in dev mode...try it out via GIT SHA version: 274db2654a8e646ccfadd1d4bf7d9aac6d9f7add

❤️ 2
tony.kay20:07:59

(try doing a

(transact! this `[(some-sym {:param (fn [] 52)})])

tony.kay20:07:41

doesn't matter if the mutation is defined if you quote it, it should still show the warning

sheluchin15:07:20

I'm having trouble integrating Crux into my application. I posted some details about it here: https://clojurians.slack.com/archives/CG3AM2F7V/p1627571447444900. Anyone know what I could try to resolve the problem?

tony.kay15:07:56

Just a stab: I'm guessing there is a version problem in your deps. It sounds like the function mem/->nippy-buffer was expected to be there, but was not. Look at what crux.clj aliases to mem, and then figure out which dependency that is and make sure you have the right version.

sheluchin16:07:46

That was just pointing to https://github.com/juxt/crux/blob/master/crux-core/src/crux/memory.clj#L4, so I explicitly added:

pro.juxt.crux/crux-core {:mvn/version "1.17.1"}
               pro.juxt.crux/crux-lmdb {:mvn/version "1.17.1"}
               com.taoensso/nippy      {:mvn/version "3.1.1"}
But then...
[2021-07-29 12:17:07.334 - WARNING] :shadow.cljs.devtools.server.nrepl-impl/init-ns-ex - {:init-ns sheluchin.user}
Note: The following stack trace applies to the reader or compiler, your code was not executed.
CompilerException Syntax error compiling at (clojure/tools/namespace/file.clj:39:26). #:clojure.error{:phase :compile-syntax-check, :line 39
, :column 26, :source "clojure/tools/namespace/file.clj"}
        clojure.lang.Compiler.analyze (Compiler.java:6808)
        clojure.lang.Compiler.analyze (Compiler.java:6745)
        clojure.lang.Compiler$InvokeExpr.parse (Compiler.java:3820)
        clojure.lang.Compiler.analyzeSeq (Compiler.java:7109)
        clojure.lang.Compiler.analyze (Compiler.java:6789)
        clojure.lang.Compiler.access$300 (Compiler.java:38)
        clojure.lang.Compiler$LetExpr$Parser.parse (Compiler.java:6384)
        clojure.lang.Compiler.analyzeSeq (Compiler.java:7107)
        clojure.lang.Compiler.analyze (Compiler.java:6789)
        clojure.lang.Compiler.analyzeSeq (Compiler.java:7095)
        clojure.lang.Compiler.analyze (Compiler.java:6789)
        clojure.lang.Compiler.analyze (Compiler.java:6745)
Caused by:
RuntimeException No such var: parse/deps-from-ns-decl
        clojure.lang.Util.runtimeException (Util.java:221)
        clojure.lang.Compiler.resolveIn (Compiler.java:7388)
        clojure.lang.Compiler.resolve (Compiler.java:7358)
        clojure.lang.Compiler.analyzeSymbol (Compiler.java:7319)
        clojure.lang.Compiler.analyze (Compiler.java:6768)
        clojure.lang.Compiler.analyze (Compiler.java:6745)
[2021-07-29 12:17:07.421 - WARNING] :shadow.cljs.devtools.server.nrepl-impl/init-ns-ex - {:init-ns sheluchin.user}
Note: The following stack trace applies to the reader or compiler, your code was not executed.
CompilerException Syntax error compiling at (sheluchin/user.clj:1:1). #:clojure.error{:phase :compile-syntax-check, :line 1, :column 1, :sou
rce "sheluchin/user.clj"}
        clojure.lang.Compiler.load (Compiler.java:7648)
        clojure.lang.RT.loadResourceScript (RT.java:381)
        clojure.lang.RT.loadResourceScript (RT.java:372)
        clojure.lang.RT.load (RT.java:459)
        clojure.lang.RT.load (RT.java:424)
        clojure.core/load/fn--6839 (core.clj:6126)
        clojure.core/load (core.clj:6125)
        clojure.core/load (core.clj:6109)
        clojure.core/load-one (core.clj:5908)
        clojure.core/load-one (core.clj:5903)
        clojure.core/load-lib/fn--6780 (core.clj:5948)
        clojure.core/load-lib (core.clj:5947)
Caused by:
IllegalAccessError set-refresh-dirs does not exist
        clojure.core/refer (core.clj:4249)
        clojure.core/refer (core.clj:4217)
        clojure.core/apply (core.clj:667)
        clojure.core/load-lib (core.clj:5966)
        clojure.core/load-lib (core.clj:5928)
        clojure.core/apply (core.clj:667)
        clojure.core/load-libs (core.clj:5985)
        clojure.core/load-libs (core.clj:5969)
        clojure.core/apply (core.clj:667)
        clojure.core/require (core.clj:6007)
        clojure.core/require (core.clj:6007)
        sheluchin.user/eval61143/loading--6721--auto----61144 (user.clj:1)

tony.kay16:07:24

missing tools-ns repl

sheluchin16:07:14

I have:

org.clojure/tools.namespace {:mvn/version "0.2.11"}

tony.kay16:07:30

but under an alias you're giving to shadow?

sheluchin16:07:54

:aliases     {:dev {:extra-paths     ["src/dev"]
                     :extra-deps      {org.clojure/clojurescript {:mvn/version "1.10.742"}
                                       thheller/shadow-cljs      {:mvn/version "2.11.23"}
                                       binaryage/devtools        {:mvn/version "0.9.10"}
                                       ; for reloading namespaces at runtime
                                       org.clojure/tools.namespace {:mvn/version "0.2.11"}
                                       cider/piggieback          {:mvn/version "0.4.1"}
                                       fulcrologic/fulcro-spec   {:mvn/version "3.1.4"}
                                       com.fulcrologic/guardrails {:mvn/version "1.1.0"}
                                       ; FIXME: duplicate from top-level :deps. Figure out
                                       ; which one is better to keep
                                       hashp/hashp               {:mvn/version "0.2.0"}}}

tony.kay16:07:20

does your require actually refer that function?

tony.kay16:07:29

try loading your user NS in a plain clj repl

sheluchin16:07:24

It does refer that function:

(ns sheluchin.user
  (:require
    [sheluchin.server.db.mounts :as crux]
    [sheluchin.server :as server :refer [http-server]]
    ; [shadow.cljs.devtools.api :as shadow]
    [clojure.tools.namespace.repl :as tools-ns :refer [set-refresh-dirs refresh]]
    [mount.core :as mount]
    [taoensso.timbre :as log]))
    ; [hashp.core :include-macros true]))

(set-refresh-dirs "src/dev" "src/app" "src/test")

(defn start []
  (mount/start #'sheluchin.server.db.mounts/crux-node
               #'sheluchin.server/http-server))

(defn restart
  "Stop the server, reload all source code, then restart the server.

  See documenation of tools.namespace.repl for more information"
  []
  (log/info "Restarting HTTP server and related services")
  (mount/stop)
  ;; TODO: fix? Not sure, most example use this thing but I keep getting
  ;;       this error when I do:
  ;;       
  ; (refresh :after 'sheluchin.user/start)
  (mount/start))
I'll try in clj.

tony.kay16:07:16

your shadow-cljs.edn file have the :dev alias in it?

sheluchin16:07:35

{:deps {:aliases [:dev]}

sheluchin16:07:15

clj -A:dev
Clojure 1.10.1
(require 'sheluchin.user)
nil
user=>

tony.kay16:07:43

I don't know what iced is doing to you...have you run shadow-cljs server by itself?

tony.kay16:07:26

I don't use that tool, but my guess is that it is messing with classpath in a way you don't expect

tony.kay16:07:18

If iced is running shadow for you, then it also needs to know the deps you want to set. shadow cannot change the classpath if iced is running the jvm

sheluchin16:07:19

Hmm :thinking_face: I'm under the impression that it does manage deps properly because after updating deps.edn and restarting, I get messages like:

$ iced repl :main --force-shadow-cljs --with-cljs -A:dev

OK: CLJS option is enabled.
OK: shadow-cljs project is detected
OK: For shadow-cljs project, start watching instead of starting REPL.
shadow-cljs - config: project/shadow-cljs.edn  cli version: 2.8.64  node: v12.18.0
shadow-cljs - starting via "clojure"
Downloading: org/lwjgl/lwjgl/3.2.3/lwjgl-3.2.3.jar from central
Downloading: com/github/jnr/jnr-a64asm/1.0.0/jnr-a64asm-1.0.0.jar from central
Downloading: org/lwjgl/lwjgl-lmdb/3.2.3/lwjgl-lmdb-3.2.3-natives-macos.jar from central
Downloading: pro/juxt/crux/crux-lmdb/1.17.1/crux-lmdb-1.17.1.jar from central
Downloading: com/github/jnr/jnr-x86asm/1.0.2/jnr-x86asm-1.0.2.jar from central
Downloading: com/github/jnr/jffi/1.2.17/jffi-1.2.17.jar from central
Downloading: org/lwjgl/lwjgl-lmdb/3.2.3/lwjgl-lmdb-3.2.3-natives-linux.jar from central
Downloading: org/lwjgl/lwjgl/3.2.3/lwjgl-3.2.3-natives-linux.jar from central
WARNING: Use of -A with clojure.main is deprecated, use -M instead
shadow-cljs - HTTP server available at 
And I have installed and integrated deps this way.

tony.kay16:07:52

but is that -A:dev sent to shadow or for iced?

tony.kay16:07:37

that's all I know about it, so afraid I cannot help more.

tony.kay16:07:59

I'm just guessing, and don't have any more of those 😛

sheluchin16:07:31

Thanks, I'll look into your suggestions some more.

adamfeldman18:07:27

@UPWHQK562 not sure if you’re working with RAD, but the fulcro-rad-demo has some Crux example code https://github.com/fulcrologic/fulcro-rad-demo#running-an-crux-based-server-alpha-quality

sheluchin18:07:02

@UCHV4JZ7A thanks, I've been using the PR for that as reference https://github.com/fulcrologic/fulcro-rad-demo/pull/24/files I think I have all the main parts copied over, but OFC, I could be missing something. At this point I do think it probably has something to do with my use of vim-iced, but I seem to be following the instructions for that as described here https://github.com/liquidz/vim-iced/issues/350#issuecomment-878606923, but still not getting much luck. The errors are inconsistent, but always similar, so that makes it all the more confusing for a newbie like me. My latest is:

$ iced repl --force-shadow-cljs :main
OK: CLJS option is enabled.
OK: shadow-cljs project is detected
OK: For shadow-cljs project, start watching instead of starting REPL.
shadow-cljs - config: project/shadow-cljs.edn
shadow-cljs - starting via "clojure"
shadow-cljs - HTTP server available at 
shadow-cljs - server version: 2.11.23 running at 
shadow-cljs - nREPL server started on port 9000
shadow-cljs - watching build :main
[:main] Configuring build.
[:main] Compiling ...
GUARDRAILS IS ENABLED. RUNTIME PERFORMANCE WILL BE AFFECTED.
Mode: :runtime  Async? false  Throw? false
Guardrails was enabled because the CLJS Compiler config enabled it
[:main] Build completed. (309 files, 0 compiled, 0 warnings, 15.85s)


Warning: resetting Nippy thaw for custom type with id: :crux.codec/edn-id
Warning: resetting Nippy thaw for custom type with id: :crux.codec/id
[2021-07-29 14:16:43.377 - WARNING] :shadow.cljs.devtools.server.nrepl-impl/init-ns-ex - {:init-ns sheluchin.user}
Note: The following stack trace applies to the reader or compiler, your code was not executed.
CompilerException Syntax error compiling at (mount/core.cljc:1:1). #:clojure.error{:phase :compile-syntax-check, :line 1, :column 1, :source
 "mount/core.cljc"}
        clojure.lang.Compiler.load (Compiler.java:7648)
        clojure.lang.RT.loadResourceScript (RT.java:381)
        clojure.lang.RT.loadResourceScript (RT.java:372)
        clojure.lang.RT.load (RT.java:459)
        clojure.lang.RT.load (RT.java:424)
        clojure.core/load/fn--6839 (core.clj:6126)
        clojure.core/load (core.clj:6125)
        clojure.core/load (core.clj:6109)
        clojure.core/load-one (core.clj:5908)
        clojure.core/load-one (core.clj:5903)
        clojure.core/load-lib/fn--6780 (core.clj:5948)
        clojure.core/load-lib (core.clj:5947)
Caused by:
IllegalAccessError on-error is not public
        clojure.core/refer (core.clj:4249)
        clojure.core/refer (core.clj:4217)
        clojure.core/apply (core.clj:667)
        clojure.core/load-lib (core.clj:5966)
        clojure.core/load-lib (core.clj:5928)
        clojure.core/apply (core.clj:667)
        clojure.core/load-libs (core.clj:5985)
        clojure.core/load-libs (core.clj:5969)
        clojure.core/apply (core.clj:667)
        clojure.core/require (core.clj:6007)
        clojure.core/require (core.clj:6007)
        mount.core/eval71118/loading--6721--auto----71120 (core.cljc:1)

wei17:07:22

hi, I'm starting from the fulcro-template code and trying to understand how to get props into a new router target. let's say I want to add a gallery page that has a collection of images, where do I load image urls? I think I'm confused whether I need a singleton query like [:component/id :gallery] and whether that query needs to be in the parent (TopChrome) or can I just fetch the data from the db in the Gallery component via link query.

wei17:07:16

Here's the segment I'm looking at:

(defsc TopChrome [this {:root/keys [router current-session login]}]
  {:query         [{:root/router (comp/get-query TopRouter)}
                   {:root/current-session (comp/get-query Session)}
                   [::uism/asm-id ::TopRouter]
                   {:root/login (comp/get-query Login)}]
   :ident         (fn [] [:component/id :top-chrome])
   :initial-state {:root/router          {}
                   :root/login           {}
                   :root/current-session {}}}
  (let [current-tab (some-> (dr/current-route this this) first keyword)]
    (div :.ui.container
      (div :.
        (dom/a :.item {:classes [(when (= :main current-tab) "active")]
                       :onClick (fn [] (dr/change-route this ["main"]))} "Main")
        (dom/a :.item {:classes [(when (= :settings current-tab) "active")]
                       :onClick (fn [] (dr/change-route this ["settings"]))} "Settings")
        (div :.
          (ui-login login)))
      (div :.ui.grid
        (div :.ui.row
          (ui-top-router router))))))

wei17:07:42

I originally thought I needed to add the images to the TopChrome query, but it doesn't look like that query corresponds to the items in the menu

wei18:07:06

I know routers use a union query but how does it decide which query to use?

wei18:07:22

Still working toward that critical mass of understanding where I can start to figure things out for myself. I finished @U0522TWDA's excellent fulcro exercises without any problems but this template (and me wanting to add routing) seems to add a lot of new things that I'm having trouble sorting through. Appreciate any help you can lend, will def pay it forward!

tony.kay19:07:44

So, the TopChrome needs to know nothing about the images, because it does not render them

tony.kay19:07:15

I assume you've composed your Gallery target INTO the TopRouter, and that your Gallery has an ident and SOME kind of initial state

tony.kay19:07:21

say [:component/id ::Gallery]. That component also probably has a query like :query [{:gallery/images (comp/get-query Image)}] and initial state maybe :initial-state {:gallery/images []}

tony.kay19:07:34

You'd have a resolver (with some kind of made-up keyword), say :all-images with NO ::pc/input, and an output of something like [:image/url :image/label] (matching whatever the query of Image component needs).

tony.kay19:07:03

that resolver would return something like {:all-images [{:image/url "..." :image/label "..."} ...]}

tony.kay19:07:33

then you just issue a load like: (df/load! app :all-images Image {:target [:component/id ::Gallery :gallery/images]})

tony.kay19:07:58

(dr is not using a union query, BTW, it uses dynamic queries)

tony.kay19:07:24

I'm imagining an Image component:

(defsc Image [this {:image/keys [label url]}]
  {:query [:image/url :image/label]
   :ident :image/url ; will cause the images to normalize by their URL
  }
  (dom/img {:src url}))

tony.kay19:07:54

no initial state needed on that one, since there's nothing to know in advance (they are loaded

tony.kay20:07:43

The take-away is this: Fulcro does have you compose things globally (you end up with a global query and initial state, and you have a normalized db containing all data), BUT, very importantly, Fulcro does not complect that with your ability to reason locally, and refactor freely. Thus, most real concerns in your app will be local to a component. Thus, the Gallery component only has to think about what it is doing (composing in images and asking them to render), and the Image component only has to worry about rendering an image. Loading is always an external concern (and IMO should not be complected with UI lifecycle like :componentDidMount). WHEN you issue a gallery load is probably more related to routing or another user event than rendering. The rendering might coincide with those events, but it isn't the same as them.

tony.kay20:07:22

When you refactor (e.g. move the Gallery to a new router) you don't have to touch the load, Image, or internals of Gallery itself at all: they are tied to the ident which doesn't change

tony.kay20:07:08

So, when you ask about TopChrome when talking about some far-off child concern, it is a red flag 🙂. You should not have to know anyting about that there.

tony.kay20:07:43

The UI State Machine stuff was added to the library after realizing (over and over) that we would like something apart from rendering that can control the lifecycle of some group of components (like your gallery, where the loading, paginating, etc. are complicated concerns having to do with actions done to the Gallery itself).

tony.kay20:07:37

Redux/Reframe has you code these as events that evolve state. Fulcro uses mutations, which are often sufficient (e.g. a show-gallery mutation can change the route and load the images). In fact, I'd say that plain mutations with composition of helpers gets you most of the way there a lot of the time. UISM is handy when things get more complicated. See the source of Fulcro RAD reports or forms, or Dynamic Routers (these all use UISM). These systems could have been written with separate mutations, but the state machine gives a nice reusable grouping of functionality that can be tied to UI separately, and is easier to see the connections between operations that are valid in various states.

wei20:07:03

wow thanks! I got my gallery of images showing! still parsing your replies for the deeper understanding, but your comment that TopChrome should have nothing to do with images got me to focus on the right things, gallery and image component queries. I read on a previous thread that composition was important so I was trying to find some connection between parent and child elements that didn't exist, guess things were more decoupled than I thought 🙂 and thanks for pointing out that dynamic router actually uses dynamic queries and not union queries, I think I mistook it with the TopChrome query (which is a union query I believe), and that added to the confusion

wei20:07:34

still learning state machines (I've read the section in the guide but will probably need to read it a few more times), and I might have a question or two about it down the road.

tony.kay22:07:38

I'm 99% certain you are not using union queries anywhere 😄

Jakub Holý (HolyJak)08:07:21

why did you think it used union query? And the connection from parent to child is there - TopChrome includes the query of the router which includes the queries of its targets...No? A nice way to load data into a router target is its :will-enter with deferred routing.

wei09:07:15

union queries are maps (right?) and i thought the TopChrome query was that, but on second look it's actually a vector of maps. still getting used to the syntax.

👍 2
wei09:07:03

yes the connection is there but it's more implicit in the relationship between the entities and not spelled out, i.e. the parent doesn't have to get all the data for the child which is what i'm used to from other frameworks

wei09:07:48

thanks for the tip on :will-enter , fits my use case

Jakub Holý (HolyJak)15:07:25

any tips on how to clarify the parent-child relationship / composition in the Minimalist Fulcro Tutorial is most appreciated :)

genekim19:07:46

Logging this trick I learned from @tony.kay — sometimes you may want to use a RAD Report in a defsc, where it will have no route, but you need to replicate everything that the report :will-enter would do — 1) df/load! the data, 2) set a parameter, 3) start the UI state machine. Logging this here I so can remember later, and hopefully help others:

(df/load! this [:trello-list/id list-id] tf/SelectedListCards
    {:target [:component/id trello-main-kw :ui/selected-list]})

; this is necessary, because we need to set the :params
; normally, this might be done thru the route
(uism/trigger! this (comp/get-ident rlr/ListCards {}) :event/set-ui-parameters
    {:params {:trello-list/id list-id}})
(uism/trigger! this (comp/get-ident rlr/ListCards {}) :event/run))

❤️ 2
tony.kay20:07:48

Giving a little more context: A report/form in RAD can have controls. Those controls result in data that is co-located with the report/form as ui parameters. Sometimes you want to initialize one or more of these "on entry". Triggering a set-ui-parameters event on the ident of a report is how you'd send one or more values into those controls. Triggering a run event causes the report to load/filter/sort. So, the load! you're showing is not needed. The report will do that part when you run it. NOTE: report/start-report! does these for you (and starts the machine, which is required, if it isn't), so you should probably use that instead. If the report is already started, it will do the steps you're listing...though you are allowed to do those manually if you wish.

❤️ 2