Fork me on GitHub

Is it documented anywhere how to create the defsc expansions that I see in the videos? Looks useful =)…


The options map is an open map. You can put anything you want in there


And get it with comp/component-options


not sure what those expansions were, but just mentioning macroexpand-1


I think you may be referring to live templates in intellij:


This is the export of my live templates…includes my defsc thing…you just type defsc by itself and hit tab (before hitting space)…it’ll also show in intellisense for completions.


Yes, sorry, that’s what I meant, and thanks @U0CKQ19AQ! That will be super useful!


Trying to get up and running in cider repl so that I can have access to app.client from emacs. This is so painful 😞 Literally spent a day on this. README focuses on IntelliJ & standalone server management( clj -A:dev -J-Dtrace -J-Dguardrails.enabled=true ). I managed to get it right once, after randomly trying dozens of combinations but couldn't reproduce it. This is literally 3-4-5 commands in the right order for an experienced cider&fulcro user. Can someone help please?


after you manually run clj -A:dev -J-Dtrace -J-Dguardrails.enabled=true you can use cider-connect to attach to that nrepl session from emacs

🙏 4

upd. I'll post as another message how I solved it


I couldn't find a way to connect to it from cider but there is a workaround to start it from emacs using (setq cider-clojure-cli-global-options "-A:dev -J-Dtrace -J-Dguardrails.enabled=true") Anyway, thanks Krisztián! @thenonameguy


yup, .dir-locals.el is better suited for these project specific overrides


yeah, I just wasn't sure I want that always in defaults


for the serverside

🙏 4

if you want to connec to the frontend (clojurescript) part, cider-jack-in-cljs is the command you are looking for

🙏 4

that will invoke shadow-cljs for you and start and connect to the appropriate build, once you have opened the browser

🙏 4

@thenonameguy That worked! Thank you! I guess smth else was missing as well "cider/cider-nrepl" in ~/.lein/profiles.clj

metal 4

Got it working: For frontend:

npx shadow-cljs server
+ connect to it from cider with cider-connect-cljs => localhost:9000 => (shadow/repl :main) For backend: cider-jack-in (setq cider-clojure-cli-global-options "-A:dev -J-Dtrace -J-Dguardrails.enabled=true") cider-jack-in => (start)

👍 4

yup, this sounds correct


Do most components live at the root level of the application? Or should most defmutations have explicit understanding of where in the graph they’re poking and everything that could be affected by them? I’ve started getting a denormalised app state working, but it seems that my defmutations need to know a lot about what needs to get touched? Maybe there’s a helper function I’m missing? EG: I’ve got a picker [:component/id :item-picker :picker/selected-item], it displays the picked item textually and graphically, so the components which do that live on the picker, giving it the query: [:item/id :item/name {:selected-item/text (get-query ShowText)} {:selected-item/img (get-query ShowImg)}] Now my mutation needs to update 3 places, with lots of get-ins/update-ins so all three parts render the selected item? I keep feeling I should be able to just reference what I want, but it’s not really rendering…


Not quite what I need I think? But this is a section that I’ve not read yet, so thanks =)…

Chris O’Donnell23:04:13

Could you explain in more detail what you're trying to accomplish? My impression is that you have a tree of data corresponding to your picker's selected item, and you find it's not ergonomic to have to slice it up into component pieces and individually add them to their proper places in state. That's the problem merge-component is meant to solve.


Sure =)… I’ve got a List of items which are displayed in a list, each item has a selection button associated with it, selecting the item calls a mutation does this:

(action [{:keys [state]}]
  (swap! state assoc-in [:component/id :item-picker :item-picker/selected-item] [:item/id id]))
  (remote [_] true)
I took this from one of the video tutorials (don’t ask me which one, I have honestly no idea >_<…) But basically calling that sends back into the app db the fully realised item. Now I have a component SelectedItem which has two subcomponents ShowText and ShowImg, so it needs the query [:item/id :item/name {:selected-item/text (get-query ShowText)} {:selected-item/img (get-query ShowImg)}]. Now I’m not sure how to point all three of these items to this selected item…


Is that clear?

Chris O’Donnell00:04:54

I think I understand.

Chris O’Donnell00:04:21

Where are the :selected-item/text and :selected-item/img values coming from? Are they attributes of the item?


I mean I could manually lookup everything with gets and then just do a series of updates, but it feels very clunky, something like the merge-component! was what I was expecting, but it didn’t quite work =)…


They’re the joins for the components


they have :item/id :item/name + more specific stuff related to them on them…


After all, these are supposed to be 3 views on the same thing, showing different aspects of it


I’m used to using re-frame where I’d just subscribe at the picker and then pass the specific bits to the children, but that seems to cause wonky behaviour if you don’t pass queries through properly?

Chris O’Donnell00:04:05

It seems like if you already know what the text and image should be for a given item, then they should already be available as properties, and then it would be enough to just change the selector's ident pointer. If there is some computation that needs to be done, then you can either do it when the item is loaded (so you only need to change the ident at selection time) or do it at selection time, in which case you will need to encode that logic into your select-item mutation.

Chris O’Donnell00:04:49

There may be other ways to accomplish this, but those are the ones I'm aware of.


Ok, so basically my issue is that I’m pointing to the wrong thing?


Ok, would it be reasonable then to store the selected item at some other location in the app-db, and then have each component just query that?

Chris O’Donnell00:04:26

Yeah, that's one way to do it. 🙂

Chris O’Donnell00:04:45

If you share the full code snippets with your components, I think that would help clarify the situation; I'm not fully understanding how your components are composed.


Sure, give me a few minutes to clean this up =)…


(defn item-picker-path [k] [:component/id :item-picker k])

(defmutation select-item
  [{:item/keys [id] :as params}]
  (action [{:keys [state]}]
    (swap! state assoc-in (item-picker-path :item-picker/selected-item) [:item/id id]))
  (remote [_env] true))

(defsc ItemText [this {:item/keys [name blurb] :as props}]
  {:query         [:item/id :item/name :item/blurb]
   :ident         (fn [] [:item/id (:item/id props)])}
  (dom/div :.ui.segment
    (dom/p "Name: "  name)
    (dom/p "Blurb: " blurb)))

(def ui-item-text (comp/factory ItemText {:keyfn :item/id}))

(defsc ItemImg [this {:item/keys [id name url] :as props}]
  {:query [:item/id :item/name :item/url]
   :ident (fn [] [:component/id ::item-img])}
  (dom/div :.ui.segment
    (dom/h3 "Image")
    (pr-str props)))

(def ui-item-img (comp/factory ItemImg {:keyfn :item/id}))

(defsc SelectedItem [this {:item/keys [name]
                           :selected-item/keys [text image] :as props}]
  {:query [:item/id :item/name
           {:selected-item/text (comp/get-query ItemText)}
           {:selected-item/image (comp/get-query ItemImg)}]
   :ident (fn [] [:component/id ::selected-item])
   :initial-state {:selected-item/text  {}
                   :selected-item/image {}}}
    (dom/h3 "Selected " name)
    (pr-str props)
    (when text
      (dom/div :.ui.grid
        (dom/div :.eight.wide.column
          (ui-item-text text))
        (dom/div :.eight.wide.column
          (ui-item-img image))))))

(def ui-selected-item (comp/factory SelectedItem))

(defsc ItemList [this props]
  {:query         [{:item-list/all-items [] #_(comp/get-query ItemListEntry)}]
   :initial-state {:item-list/all-items []}
   :ident         (fn [] [:component/id ::item-list])}
    (pr-str "...")))

(def ui-item-list (comp/factory ItemList))

(defsc ItemPicker [this {:item-picker/keys [selected-item item-list] :as props}]
  {:query         [{:item-picker/selected-item (comp/get-query SelectedItem)}
                   {:item-picker/item-list (comp/get-query ItemList)}]
   :ident         (fn [] [:component/id :item-item])
   :initial-state {:item-picker/selected-item {}
                   :item-picker/item-list     {}}}
    (when selected-item
      (dom/div :.ui.segment
        (ui-selected-item selected-item)))
    (dom/div :.ui.segment
      (dom/h3 "Items")
      (ui-item-list item-list))))

(def ui-item-picker (comp/factory ItemPicker))


That took longer than I thought… 😞

Chris O’Donnell01:04:32

I think the simplest thing to do here is to not give ItemImg and ItemText queries and idents; just write them as functions or bare components. Then you can avoid the weird, artificial nesting and query for [:item/id :item/name :item/url :item/blurb] in SelectedItem.


Hmm, I’ll see if that works =)… Thanks!


Sigh, I moved on to issues with UI updating instead, but after a few hours managed to find the section on keyframe renderer, so that’s fixed that at least =)…


Figured it out I think! I just need to use this :>/ in the query… That allows me to create a level of nesting purely for structure =)…


@codonnell that link that you just posted - first example. I'm wondering if there is a typo.. Shouldn't this be :ident [:counter/id :counter/n] instead?

Chris O’Donnell23:04:48

No, the ident is correct. The first :counter/id in the tuple indicates that the component's state should be stored in the :counter/id table. The second indicates that the identity attribute of the component is :counter/id, so the identity value can be found with (:counter/id component-props).


Got it. Thanks!

👍 4