This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2018-05-29
Channels
- # aleph (4)
- # architecture (12)
- # bangalore-clj (1)
- # beginners (87)
- # boot (3)
- # cider (19)
- # cljs-dev (84)
- # clojars (10)
- # clojure (79)
- # clojure-italy (7)
- # clojure-nl (19)
- # clojure-russia (10)
- # clojure-spec (9)
- # clojure-uk (55)
- # clojurescript (64)
- # core-async (7)
- # core-typed (4)
- # cursive (7)
- # data-science (2)
- # datomic (8)
- # devcards (6)
- # docs (1)
- # duct (5)
- # fulcro (117)
- # graphql (1)
- # instaparse (1)
- # leiningen (13)
- # lumo (103)
- # nyc (3)
- # off-topic (54)
- # om (9)
- # onyx (1)
- # pedestal (6)
- # planck (3)
- # portkey (7)
- # re-frame (26)
- # reagent (20)
- # ring-swagger (14)
- # shadow-cljs (164)
- # sql (11)
- # tools-deps (25)
- # yada (1)
What's the best way to pre-populate the database with normalised data on startup? Can you use the :shared
entry to :reconciler-options
? From the docs it looks like data there isn't normalised into the database
@oliver.mooney What's your use-case ? If you have the data on the server when rendering the html you can use intial-state
https://github.com/fulcrologic/fulcro-template/blob/master/src/main/fulcro_template/client.cljs#L20
@claudiu that looks interesting. I'm trying to pre-populate the client db with the last few things the user worked on as that's the most common user behaviour, and it'd save a few network trips. I've no SSR in place yet
How do you refer to a ident query in a component's props? You can't destructure the ident as a key in a {:keys []}
vector
@oliver.mooney (fp/get-ident Component props)
, but most of the time you should not need it
I think it's something i'm missing - it's not something I'm actually trying or needing to do, I'm just testing my understanding of how the props used in the component's render call, and the fulcro query, ident and initial-status all relate to each other. My test case is showing a summary of a select. I guess it's easier if I just show some code...
(defsc OptGroup
[this props]
{:ident [:optgroup/by-id :optgroup/id]
:query [:optgroup/id :optgroup/caption :optgroup/options]})
(def select-table :bs4-select/by-id)
(defn change-select-value* [st id value]
(assoc-in st [select-table id :select/value] value))
(defmutation change-select-value [{:keys [id value]}]
(action [{:keys [state]}] (swap! state change-select-value* id value)))
(defsc BS4Select
[this {:keys [select/id select/caption select/value select/options]}]
{:ident [select-table :select/id]
:query [:select/id :select/caption :select/value
{:select/options (prim/get-query OptGroup)}]}
(labeled-input
{:onChange #(prim/transact! this
`[(change-select-value {:id ~id
:value ~(.. % -target -value)})])
:type "select"
:select-options options
:value value}
caption))
(def ui-select (prim/factory BS4Select {:keyfn :select/id}))
(defsc SelectRoot
[this {:keys [] :as props}]
{:query [{[:bs4-select/by-id :select-test] (prim/get-query BS4Select)}]}
(let [select-data (get props [:bs4-select/by-id :select-test])]
(dom/div
(dom/div "")
(ui-select select-data))))
I think worth keeping in mind the different between normalised vs denormalised DB
So in the select root I was hoping to pull some prop from the select data (like the current choice) and display it as a summary - it works first time but doesn't update when the select is changed to a different value.
@wilkerlucio yeah I was thinking it must be something to do with that, but thought there should be a way to dive into the :query in the render's props to pull it out at the same level
@oliver.mooney ok, one thing that helps is that you most of the time want the component which the state is going to be modified to be the one who triggers the transaction
in your case, the option
is triggering the transaction, but the select
is the one who needs the state to be changed
this is usually better solved by sending a callback function from your select to your option, and call it there, so the select
is the one who actually calls transact!
what matters who calls the transaction is that when you (transact! this ...)
, there will be a key called :ref
in your environment, which is the ident
of the component who did the transaction, you can leverage that to create generic transactions
sorry, I misread your code, your tx is gonig into the right place š
let me try to simplify the code you posted, and tell me if that makes sense
(defmutation change-select-value [{:keys [value]}]
(action [{:keys [state ref]}]
(swap! state update-in ref assoc :selected/value value)))
(defsc BS4Select
[this {:keys [select/id select/caption select/value select/options]}]
{:ident [:bs4-select/by-id :select/id]
:query [:select/id :select/caption :select/value
{:select/options (prim/get-query OptGroup)}]}
(labeled-input
{:onChange #(prim/transact! this
`[(change-select-value {:value ~(.. % -target -value)})])
:type "select"
:select-options options
:value value}
caption))
just the mutation part
check that using ref
we can avoid having to know about the ident key of the element, this will target the correct data
that is in fact so common, that you Fulcro has a simpler way to do it:
:onChange #(mutations/set-value! this :select/value (.. % -target -value))
and if you are using dom/select
, just be aware your values must be strings
and about the normalised/denormalised, the database saved on your reconciler is a the normalised (which is the one who looks like graph, with idents and things...), when you are transacting, this is the one you get
but during rendering, you get the denormalised (also called "tree"), which is just the map you expect to be passing on React components
if you need to change formats, there are some handy functions in fulcro, check for prim/db->tree
and prim/tree->db
, but most of the time you have the right format in hand š
for the initial state, you send the denormalised, and fulcro will normalise it, unless you send the :initial-state
as an atom, then you should normalise it yourself
So the render call has the denormalised version of whatever you had in the query, regardless if the query had idents or links or whatever else?
no, the denormalised is constructed from your query
so if you omit fields on your query, those are going to be missing in your props
imagine like the normalised is like the database, and you run your component queries against it, and it will only return what you expressed there
yeah that makes sense - and if I log the result of the (get-props...)
call in my SelectRoot component above I see the denormalised data
I'd been assuming that you had to have the keys of that get-props result in your render props for the component to update when they changed
it's (fp/props)
, get-props
is something else, but I don't remember what it is
sorry yes, I mistyped - I meant the (get props [:bs4-select/by-id :select-test])
part
hahaha, no problem š
your props will be whatever your query mentions (unless you break the chains and tumper on it yourself on the way)
and you should avoid that, if you need to send extra things (that aren't part of the db), use computed
Is computed only for things that need to be "computed" for each child of a parent, or grandparent etc? I was trying to work out if it was only event handlers that varied by child in practice
most of the time is for event handlers
but I had some exceptions, sometimes I also use to send extra customization for the component, like override styles
thanks for all of the explaining! Things are clearer now, I'll play with it a bit more
@oliver.mooney Iād add this: computed is for any data being passed from parent to child that did not come from the childās query. That includes callbacks and anything else the parent ācomputedā for the child. It has to do with rendering optimization: Fulcro can re-render a child that has an ident, but it can only supply data that the child asks for (not data that the parent previously passed). To make it work, Fulcro remembers the computed stuff for these optimized renders from the prior parent render. If youāre using keyframe rendering mode, for example, then you donāt need it (but your UI will likely be a lot slower).
good to know @tony.kay, thanks - so if switching to the keyframe rendering mode gets callbacks etc working you've a signal that computed is needed too
say I have a Blog component like in section 9.9.2 Filling in the Subgraph of the book
how do I set params to the sub query :blog/comments
using df/load
so I can put it in started-callback
?
@oliver.mooney I guess you could use that as a debugging step š The rule is simple, though: if it isnāt in the query, it belongs in computed.
@myguidingstar (load :blog/comments Comment {:params { ... } :target [...]})
, or do you mean something else???
technically it should send something like this to server: {[:blog/by-id 1] [:db/id {(:blog/comments {:offset 10 :limit 10}) [:db/id :comment/body]}]}
hmm, I didn't think of :target
. Thanks
@tony.kay the issue here is that we want to parameterize some part of the sub-query
the :params
on load
only goes into the join root of the query
My original thought was that these āinitial loadsā have reasonable ādefaultsā. Thereās really no need to keep loading Blog 1 with different parameters of children. You load the Blog and it defaults to ānoā or ā10" commentsā¦then you update the stream of comments
you can also pass the parameters to the blog join and pass those parameters down to children
do you know if using load-field
, the params will go on that field?
or they will go on the ident join?
I guess so too
since it's just a sugar for the regular load
I wonder if we could do something like: :subquery-params {[:blog/comments] {:foo "bar"}}
I'll probably write something around load to do that, then I can tell if that works after some time š
this is my current best solution, do you have other ideas?
the load mutation itself allows an arbitrary query. If you have something that needs to muck with an arbitrary query, you should probably use that.
I already use a custom load
function that wraps fulcro load (because I add some app-specific things around that), so I'll try adding it there
what is "the load mutation"?
@myguidingstar is that not in the book?
@myguidingstar it's the mutation that fetch/load
calls underneath
if you look at load
impl you'll see it
http://book.fulcrologic.com/#_using_the_code_fulcro_load_code_mutation_directly_not_recommended
ah, I didn't read it š
but what about normalization?
it is the underlying mechanism behind load
and load-field
. The latter two are just helpers for common cases.
@myguidingstar I don't recommend, but the truth is that the only different from fp/get-query
is that it adds a :component
meta to the query root, you can do that by hand too
Fulcroās client-side query processing ignores parameters by default, so a set-query!
on the Comments component could be used to add params that would show up in the load
@tony.kay I'll try to check that out, have to move now, see you later
@wilkerlucio can you give me some example code for such metadata?
okay, see you
@myguidingstar unfortunately, looking at the code, Iām not sure params is properly implemented yet in set-query!
š
but that shouldnāt be necessary. Iād consider this a case that probably needs a slight bit more API in Fulcro
The best workaround I can think of for the moment is to namespace your parameters (e.g. :comments/offset
) and just have the parent parse handler pass params down to all sub-parsing. Then children can pick up the params āmeantā for them.
you mean parse handler in server side?
ha, I see
@wilkerlucio Feeling appreciative of pathomā¦such a good set of ideas
@tony.kay glad to hear š are you trying it on some project?
have one Datomic structure that needs to morph into anotherā¦connect looks like it will work nicely
cool, I hand't had the chance to use with datomic actually, please let me know how the experience goes, I know the folks on Atlas CRM are doing pulls on datomic entities and using that as the output for chaining the things, I wonder if we could use the datomic entities directly (considering they interface like a map), and then would be a matter of saying which outputs you want to declare at any given time, but the entities also has the problem of maybe giving too much (because user can still ask for things that are not explicitly declared as the output), so I wonder how you are going about it
@tony.kay one problem that I often hit when developing fulcro is when errors happen during mutations, most of the time the stack trace is useless, it only gets to the mutation start, but nothing inside, do you think we can make something to improve that?
@wilkerlucio do you mean on the backend?
@wilkerlucio Yes, that is a problem. Using js/console.log
directly should make it possible to fold open the back-trace, but then youāve got console.log stuck in there. I have not had time to research a better way of reporting
@currentoor on the front-end, the last relevant stack point is the entry of the mutation, but I often have to add custom console.logs in the middle to figure exactly what line broke
ah i see
i use a macro that injects (js/console.log '<sym-name> sym)
so often that i never relied on the stack trace to begin with š
but thatās probably a sub-optimal workaround