How often, if ever, would you make a nested comp/get-query
call?
In every app... I'm not sure you're asking what you think you're asking
I guess so
@tony.kay Hmmm... in that case do I create n versions of AccountQuery, for the n different views an Account is represented as? Otherwise, if I only create just one all-encompassing AccountQuery that has all the 20 things an account has, then wouldn't I be asking too much in a simple Badge?
@tony.kay and also, when would I get non-normalized account if would never do (load! ... Badge)
? The query of Badge
will only be used for pulling data from the client db, isn't it?
(defsc UiPhraseLocation [this {:phrase-location/keys [from to id] :as props
:ui/keys [extraction-phrase]}]
{:use-hooks? true}
(let [[open? set-open] (react/useState)
{:keys [source i]} (comp/get-computed props)
phrase (subs source from to)
loading? (get-in props [df/marker-table [:fetching id]])]
($ ListItem
{:style #js {:display "grid"
:gridTemplate "'top-bar' 50px
'ana' auto / 1fr"
:padding "0px"
:background (if (odd? i)
"rgba(220, 240, 220, 0.31)"
"rgba(200, 230, 200, 0.31)")}
:key (str id)}
($ ButtonBase
{:onClick #(do (set-open (not open?))
(when-not (or extraction-phrase loading?)
(df/load! this [:phrase/phrase phrase]
ExtractionPhrase {:target [:phrase-location/id id :ui/extraction-phrase]
:marker [:fetching id]
:parallel? true})))
:style #js {:gridArea "top-bar"
:height "50px"
:justifyContent "space-between"
:background (if (odd? i)
"rgba(220, 240, 220, 0.31)"
"rgba(200, 230, 200, 0.31)")}}
($ Typography {:variant "h5" :color "secondary"
:style #js {:textOverflow "ellipsis"
:overflow "hidden"
:whiteSpace "nowrap"
:width "80%"
:position "absolute"
:left "15px"}} phrase)
($ :div {:style {:position "absolute" :right "15px" :top "15px"}}
(if open? ($ ExpandLess) ($ ExpandMore))))
($ Collapse
{:style #js {:gridArea "ana"}
:in open? :unmountOnExit true :timeout "auto"}
(if loading?
($ LinearProgress {})
(ui-extraction-phrase extraction-phrase))
#_(for [[i [form short long]] (map-indexed vector forms)]
($ Typography {:variant "h6" :color "secondary" :key (str i " - " form " - " i)
:style #js {:alignSelf "center"}}
(str form " - " short " - " long )))))))
(form/defsc-form PhraseLocation [this {:phrase-location/keys [from to id] :as props}]
{::form/attributes [extr/phrase-loc-from
extr/phrase-loc-to
extr/phrase-loc-id]
::form/query-inclusion [{:ui/extraction-phrase (comp/get-query ExtractionPhrase)}
[df/marker-table '_]]
::form/id extr/phrase-loc-id
:use-hooks? true})
(def ui-phrase-location (comp/factory UiPhraseLocation {:keyfn :phrase-location/id}))
(form/defsc-form Extraction [this {:extraction/keys [phrase-locations source]}]
{::form/id extr/id
::form/attributes [extr/source extr/phrase-locations extr/id]
::form/subforms {:extraction/phrase-locations
{::form/ui PhraseLocation}}
:use-hooks? true}
(map-indexed (fn [i phrase-location]
(comp/with-parent-context this
(ui-phrase-location (comp/computed phrase-location {:i i :source source}))))
phrase-locations))
use-hooks?
seems not to work on defsc-form
so I made a different component for that. The issue seemed to stem from the map-indexed
I thought maybe the Collapse
was suspect, but the render of UiPhraseLocation didn't seem to matter.
not sure if it matters but the Extraction
gets rendered with a comp/factory -> ui-extraction
like so:
(when (:material/extraction selected-material)
(ui-extraction (:material/extraction selected-material)))
In every app... I'm not sure you're asking what you think you're asking
map-indexed is lazy, and since it is at the top of the body it isn’t getting forced. I bet if you put it in a div
it’ll be fine. The DOM elements all forcechildren, but defsc does not. That’s technically an oversight on my part, but I guess no one has tried it quite that way
Yes, you write different components with different queries, but using the same Ident. That’s most of the point, remember? Being able to ask for what you need, when you need it?
@tony.kay Hello! I went on a walk this evening and I was pondering about Fulcro. I got reminded that when I first started playing with Fulcro I found the idea of normalising data via component queries neat, and it made perfect sense for examples I could come up with (e.g. a Todo component mapping to a Todo entity, etc), but it wasn’t obvious to me (and still isn’t) that the model generalised (e.g. that I wouldn’t have a component corresponding to two distinct data nodes, etc). Have you found this to be a limitation or a problem in practice?
Could you clarify? Are you saying that if you have some {:todo/id 1 :todo/title "..."}
on the screen you wouldn't expect fulcro to be capable of drawing it on the screen twice?
@hmaurer no. The model generalizes in the following ways:
• Two different things that need to display in a hetero list (e.g. a timeline feed with news/images/etc): Union queries
• A component that has a mix of data from various places in the real back-end db: Keywords are namespaced. Query resolution can combine disparate things into a view, and save logic can pick them back apart. Anything that can displayed this way must by its very nature be to-one in relation (otherwise you’d have to have an edge to traverse, which would lead to children with different IDs). Thus, merging data implies that you can “merge IDs”…e.g. :account/id
and :primary-address/id
can co-exist in a single component even though more than on ID corresponds to elements on that UI. Save, of course, has to know that fields need what IDs. RAD solves that by having you declare what identities (a keyworkd like :account/id
) identifies the entity(ies) on which that fact can be saved or read.
@tony.kay thanks for writing this up. Follow-up question, then: how would you deal with, say, a “CompareVehicles” component which lets you pick two vehicles (entities in the DB) and compare them (imagine a side-by-side table).
a container with either a single to-many join (with 2 elements) or two edges that I made up :left-car
:right-car
[{:left-car (comp/get-query Car)} {:right-car (comp/get-query Car)}]
or [{:cars-to-compare (comp/get-query Car)}]
the latter supports n-compare screen
1💯I am getting following message twice (same Router) on startup since I updated to 3.2.16:
WARN [com.fulcrologic.fulcro.ui-state-machines:215] - #error {:message "", :data {}} Attempt to get an ASM path [:com.fulcrologic.fulcro.ui-state-machines/local-storage :path-segment] for a state machine that is not in Fulcro state. ASM ID: :decide.ui.root/PageRouter
Fulcro 3.2.15 works fine.
3.2.17 breaks even more after that…
Do I have to initialize my routers? I remember that it wasn’t necessary anymore sometime ago.“breaks” or gives warning?
With .16 only these warnings and the page renders like expected.
With .17 the page does not render.
I deleted the .shadow-cljs folder and restarted shadow-cljs after each change
I don’t have much time today. I’ll take a closer look at this later.
OK, that helps. I did change that function. I see where the crash is happening, and I can protect that code, but the old version was just better at tolerating the overall problem, which I suspect is either a mis-use (or mis-documentation) of the dynamic routers themselves. Try 3.2.18-SNAPSHOT when you have a moment.
That sequence in your main app from line 60 to 57 to 35…what is that sequence? The crash you’re seeing is because something in the app wasn’t yet initialized. I think the new version of that function was just being less tolerant of being called when things weren’t initialized. Hopefully the snapshot version I pushed fixes that, but I should update the dyn routing chapter and be more explicit about how to initialize it properly.
in 18-SNAPSHOT there are only the two warnings from .16
Line 35 is a call to dr/current-route
The component used as the parameter is a router-target for my root level router (called PageRouter
)
Following code does not help with the issue in .17 & .18-SNAPSHOT
(defn ^:export init []
(log/info "Application starting.")
(app/set-root! SPA root/Root {:initialize-state? true})
(dr/initialize! SPA)
(app/mount! SPA root/Root "decide" {:initialize-state? false}))
It changes nothing. Same warnings / errors.The warnings are coming from a dr/change-route!
called in client-did-mount
after my HTML5 routing has started.
Well not directly called. the dr/change-route
is in the callback for pushy
There is sort of a race condition between initial state (which has no state machines in it) and any attempt to find the “current route”. That is what initialize!
is about: it tries to make sure the machines are started. Ideally, the real initialization is to do a sequence like this:
(app/set-root! SPA Root {:initialize-state? true})
(dr/initialize! SPA)
(app/mount! SPA Root "app" {:initialize-state? false})
that will prevent the UI from looking for the current route before there is one…ideally you’d also change-route!
before the mount as well
I need to update the book…it is possible my recent cleanups show more warnings than before, but should not have “broken” anything
Could someone give me a quick sanity check?: I've roughly the following attributes/entities:
(defattr category-id :category/id :uuid {ao/identitiy? true})
(defattr element-id :element/id :uuid {ao/identity? true})
(defattr content-id :content/id :uuid {ao/identity? true ao/identities #{:category/id :element/id})
So I've go a bunch of different entities and want to provide a "global" content-id
, so I can model edges with metadata, that can connect all the entities in my system.
But how can I make sure both, my entity-ids and the content-id
are created when saving a new instance via a RAD form?
(Or maybe what I'm trying to do is a stupid idea to begin with?)In .16 i just got the message from above. In .17 it is followed by a lot of red on my console something about deref of nil in current-route. No render anymore. I am currently not on my computer. I will look for details tomorrow.