Fork me on GitHub
#fulcro
<
2018-08-20
>
levitanong03:08:44

@hjrnunes sorry for the late reply, I had fallen asleep. šŸ˜… May I see your load code?

hjrnunes17:08:08

@levitanong

defsc SecurityRow [this {:keys [db/id ui/editing?
                                 ;ui/dropdown
                                 ] :as row}]
  {:query       [:db/id :security/name :security/sym {:security/currency [:currency/sym]}
                 :ui/editing?
                 {[:ui/dropdown '_] (prim/get-query bs/Dropdown)} ;reusable dropdown
                 fs/form-config-join]
   :ident       [:security/by-id :db/id]
   :form-fields #{:security/name :security/sym}}
  (sui/ui-table-row nil
                    (for [field securities-header-ks
                          :let [value (if (= :security/currency field) (get-in row [field :currency/sym])
                                                                       (get row field))]]
                      (sui/ui-table-cell
                        {:key field}
                        (if editing?
                          (if (= :security/currency field)
                            (form-input this row field "String required") ; dropdown would go here
                            (form-input this row field "String required"))
                          (dom/span #js {:onDoubleClick (fn [evt] (prim/transact! this `[(gmut/edit-row {:id ~id :class ~this})]))}
                                    value)
                          )))))

(def ui-security-row (prim/factory SecurityRow {:keyfn :db/id}))


(defsc SecuritiesTable [this {:keys [sec-table/title sec-table/securities]}]
  {:query [:db/id :sec-table/title {:sec-table/securities (prim/get-query SecurityRow)}]
   :ident [:sec-table/by-id :db/id]}
  (dom/div
    (dom/div title)
    (sui/ui-table nil
                  (securities-header)
                  (sui/ui-table-body nil
                                     (mapv ui-security-row securities)))))

(defsc SecuritiesRoot [this {:keys [securities/table]}]
  {:query         [{:securities/table (prim/get-query SecuritiesTable)}
                   {:ui/dropdown (prim/get-query bs/Dropdown)}
                   ]

   :initial-state (fn [p] {:securities/table (prim/get-initial-state SecuritiesTable {})
                           :ui/dropdown      (bs/dropdown :security/currency "Currency" [(bs/dropdown-item :foo "foo")
                                                                                         (bs/dropdown-item :bar "bar")])
                           })}
  (dom/div
    (ui-securities-table table)))

(defcard-fulcro securities-card
  "# Full App"
  SecuritiesRoot
  {}
  {:inspect-data true
   :fulcro       {:started-callback (fn [app]
                                      (df/load app :securities/table SecuritiesTable
                                               {:post-mutation `gmut/init-rows)
                                      (js/console.log :STARTED))}})

hjrnunes17:08:25

The data is being loaded in :started-callback init-rows just initialises :ui/editing?

hjrnunes17:08:26

I don't get why I'm seeing [:ui/dropdown _] [:fulcro.client.primitives/not-found nil] in the data-tree

cjmurphy17:08:57

All your query joins have a get-query except this one: {:security/currency [:currency/sym]}. Is it supposed to be like that?

hjrnunes17:08:53

yeah, not sure what to do about that

hjrnunes18:08:19

I don't need a UI Component for that

cjmurphy18:08:56

Yes but I think you should make one anyway.

hjrnunes18:08:14

One just for the query, then?

cjmurphy18:08:21

ie no render, but needed for the state.

cjmurphy18:08:38

Yes - and will need an ident.

hjrnunes18:08:22

ok, do you think that's the issue?

hjrnunes18:08:47

this is what I get with that load

hjrnunes18:08:34

the link query for Dropdown gets filled with not-founds

hjrnunes18:08:04

sorry, not the query, but the data-tree when matched to query. The query actually seems fine

cjmurphy18:08:43

`(bs/dropdown :security/currency "Currency" [(bs/dropdown-item :foo "foo") (bs/dropdown-item :bar "bar")])` <--- why can't that be a call to get-initial-state

cjmurphy18:08:52

It is no problem at all to have components that don't render - with the rendering done elsewhere, often in the parent.

hjrnunes18:08:23

I'm not sure DropdownItem actually has initial state

cjmurphy18:08:05

Just put a {} if something needs to be there.

cjmurphy18:08:56

That way you get a table in the state, with a record. All singletons probably need to be in state from the outset.

hjrnunes18:08:16

ok, this is what my components look like, now:

(defsc CurrencyCell [this _]
       {:query [:currency/sym]
        :ident [:currency/by-sym :currency/sym]})

(defsc SecurityRow [this {:keys [db/id ui/editing?
                                 ;ui/dropdown
                                 ] :as row}]
       {:query       [:db/id :security/name :security/sym {:security/currency (prim/get-query CurrencyCell)}
                      :ui/editing?
                      {[:ui/dropdown '_] (prim/get-query bs/Dropdown)} ;reusable dropdown
                      fs/form-config-join]
        :ident       [:security/by-id :db/id]
        :form-fields #{:security/name :security/sym}}
       ...)

(defsc SecuritiesRoot [this {:keys [securities/table]}]
       {:query         [{:securities/table (prim/get-query SecuritiesTable)}
                        {:ui/dropdown (prim/get-query bs/Dropdown)}
                        ]

        :initial-state (fn [p] {:securities/table (prim/get-initial-state SecuritiesTable {})
                                :ui/dropdown (prim/get-initial-state bs/Dropdown {:id    :security/currency
                                                                                       :label "Currency"
                                                                                       :items [(prim/get-initial-state bs/DropdownItem {:id :foo :label "foo"})
                                                                                               (prim/get-initial-state bs/DropdownItem {:id :foo :label "bar"})]})})}
       (dom/div
         (ui-securities-table table)))

cjmurphy18:08:46

So looking at the state with Fulcro Inspect, are there any problems?

hjrnunes18:08:47

Yep:

{:ui/dropdown                  nil
 :securities/table             [:sec-table/by-id 1]
 :security/by-id               {1 {:db/id             1
                                   :security/name     "Facebook"
                                   :security/sym      "FB"
                                   :security/currency [:currency/by-sym "usd"]
                                   [:ui/dropdown '_]  [:fulcro.client.primitives/not-found nil] ; <---------------------
                                   :ui/editing?       false}
                                2 {:db/id             2
                                   :security/name     "Apple"
                                   :security/sym      "AAPL"
                                   :security/currency [:currency/by-sym "usd"]
                                   [:ui/dropdown '_]  [:fulcro.client.primitives/not-found nil] ; <---------------------
                                   :ui/editing?       false}
                                3 {:db/id             3
                                   :security/name     "BP"
                                   :security/sym      "BP.L"
                                   :security/currency [:currency/by-sym "gbx"]
                                   [:ui/dropdown '_]  [:fulcro.client.primitives/not-found nil] ; <---------------------
                                   :ui/editing?       false}}
 :ui/locale                    :en
 :fulcro.inspect.core/app-uuid #uuid "bc2f57b4-de02-44b7-87ff-c4f18f7ecd9a"
 :fulcro/loads-in-progress     #{}
 :sec-table/by-id              {1 {:db/id           1
                                   :sec-table/title "Securities"
                                   :sec-table/securities
                                                    [[:security/by-id 1]
                                                     [:security/by-id 2]
                                                     [:security/by-id 3]]}}
 :fulcro/ready-to-load         ()
 :ui/loading-data              false :currency/by-sym {"usd" {:currency/sym "usd"}
                                                       "gbx" {:currency/sym "gbx"}}}

hjrnunes18:08:16

that's the issue

cjmurphy18:08:14

Why not put a {} in :ui/dropdown?

hjrnunes18:08:46

well, I did already. I get the dropdown to initialise fine in there, and normalised, and even rendered, to a point

cjmurphy18:08:51

Or actually an ident, since it is a component.

hjrnunes18:08:00

but I took it off to debug this thing

hjrnunes18:08:08

because it was showing there

hjrnunes18:08:53

that's what I don't get. Why is that part of the query marked as missing

hjrnunes18:08:32

if you look in the pic, the query is fine. The Dropdown query is all there

cjmurphy18:08:39

bs/Dropdown has an ident right?

cjmurphy18:08:09

And there is only one of them in the whole application?

hjrnunes18:08:28

I debugged it in Chrome, and the issue is in (prim/mark-missing response query)

hjrnunes18:08:01

that data is being marked as missing

hjrnunes18:08:12

but it seems it shouldn't

cjmurphy18:08:27

:ui/dropdown nil <--- that is the problem to fix IMHO

hjrnunes18:08:34

since :ui/dropdown doesn't even get sent to the server

cjmurphy18:08:07

Give it initial state in your root component.

hjrnunes18:08:38

ok. I'll put it there to demonstrate

hjrnunes18:08:55

you'll see the data in the the security entity still marked as missing

cjmurphy18:08:39

[:dropdown/by-id :singleton] <-- something like that.

cjmurphy18:08:15

Instead of nil.

tony.kay18:08:59

@currentoor oh right..crap. Yeah, that was an oversight. I thought that change would be transparent. Your change is the exact opposite of what I was trying to do.

tony.kay18:08:40

@hjrnunes So, the :ui prefix keywords do not key elided from server queries if you use them as link queriesā€¦that is a butā€¦link queries should not appear in loads at all, really.

tony.kay18:08:59

Also, it makes no sense to use a link query with a ui prefixā€¦.I really donā€™t think it a good idea to use link queries very much at all. A dropdown is something that is a proper child of something, and should just be coded that way. Create an edge in yoru data graph instead of trying to access the root node

tony.kay19:08:27

but you are right that mark-missing should not mark that link as missing

hjrnunes19:08:36

@tony.kay right, the reason I switched to :ui was precisely to avid sending to the server

levitanong19:08:37

@hjrnunes it might help if you modify your load call to make use of the :without key. http://book.fulcrologic.com/#_pruning_the_query

tony.kay19:08:06

but why a link query?

hjrnunes19:08:26

@levitanong that's the state with the dropdown in

tony.kay19:08:54

So, what you want is to initialize the dropdown into the graph with an edgeā€¦I take it youā€™re adding this security thing with a load, right?

tony.kay19:08:29

so, each row should probably have itā€™s own state, right? So the dropdowns arenā€™t shared

hjrnunes19:08:38

@tony.kay Yep. Eventually I'd like to dynamically set the options to the dropdown

hjrnunes19:08:16

Yes, I'm aware of it. I was just trying to get a dropdown working

hjrnunes19:08:56

next step would be to normalize dropdowns to :security idents

hjrnunes19:08:09

In my understanding of it, at least

tony.kay19:08:38

ok, so hereā€™s what you do: 1. Change the query to include a join on the dropdown with a more normal key, like {:ui/meaning-of-dropdown (get-query bs/DropDown)} 2. Issue your load for the row(s), and include a post-mutation:

(load ... {:post-mutation `populate-dropdowns})
3. In the post-mutation, go through and create state for the dropdowns and link them into the new rows.

tony.kay19:08:03

OR, use a controlled dropdown that uses component-local state that is sent from the parent

tony.kay19:08:13

then the dropdown wonā€™t need to have itā€™s own state

hjrnunes19:08:15

in the OR case then, I'd, say, load the currencies elsewhere and pass them in as props to each row?

tony.kay19:08:20

You canā€™t ā€œshareā€ the dropdown, because then they would share their state (what they have selected)

tony.kay19:08:44

sureā€¦and you could use shared for things like that which are just global lists of data

hjrnunes19:08:34

Well, I tought of that, but given that the currency list might change, I put it off

hjrnunes19:08:48

the shared state option

hjrnunes19:08:00

I intend to allow creation of new currencies

hjrnunes19:08:05

Ok, the immediate issue, here, so I understand, is that the link query isn't being actually ellided, right? So if I'd add it to :without on the load query, that will get rid of those missings?

hjrnunes19:08:50

I understand that won't actually solve my problem, but I'd like to understand

hjrnunes19:08:08

Right, yep, got rid of it

tony.kay19:08:17

shared changes on root render if you use shared-fn

tony.kay19:08:56

the bug on elision is either on the query side or in mark-missingā€¦not sure

hjrnunes19:08:00

The funny thing is I had added it to :without but whilst it being :ui/dropdow I changed it to :root/dropdown and it worked

hjrnunes19:08:24

I think it's when it checks for ui prefixed keywords

tony.kay19:08:33

the :ui prefix is special, so it could be that it was removing it properly for the serer, but not from mark-missing

hjrnunes19:08:48

; plain missing prop
                  (and (keyword? element) (nil? (get result element)))
                  (assoc result element ::not-found)

tony.kay19:08:59

It is technically not supported to use :ui in a linkā€¦it just makes no sense to me

hjrnunes19:08:07

doesn't check for idents prefixed with :ui

hjrnunes19:08:20

I noticed that, but wasn't sure if it was a bug or feature šŸ˜„

tony.kay19:08:44

The intention of that feature was to allow local props on components that were known to not come from the server

tony.kay19:08:59

they are intended to be implementation details that donā€™t leakā€¦and links are definitely leaks

tony.kay19:08:56

using a link for a thing like a dropdown is definitely an antipattern

tony.kay19:08:16

youā€™re basically coding your app so you have to think globally about your database instead of locally

hjrnunes19:08:52

yep, i realised that, but I'm just dipping my feet, and wanted a dropdown working, so I went for that specific example

tony.kay19:08:29

sureā€¦I should really remove the need for links from my examples. The two I can think of that are relatively ā€œvalidā€ are example that link to the routers or load marker tablesā€¦because sometimes you just want to know ā€œwhere an arbitrary route is pointingā€ or ā€œwhat some random load marker is doingā€ā€¦.but shared is probably better for those cases. It is cheap enough to do a root render to update those.

tony.kay19:08:02

@currentoor Iā€™m not sure it should be doing thatā€¦.can you help me understand where it is making you require where you didnā€™t before?

tony.kay19:08:17

also, you might need to cleanā€¦it really should work without that

hjrnunes19:08:11

@tony.kay anyway, thanks for the help!

tony.kay19:08:16

I think I can still patch that to work

tony.kay20:08:24

@currentoor Please try RC9-SNAPSHOT

tony.kay20:08:38

that should fix it without adding back the bloat

currentoor20:08:19

also what i proposed, that would only add bloat to the clojure version right? not the cljs builds

currentoor20:08:29

at least that was my understanding

tony.kay20:08:04

hmā€¦it didnā€™t look like it would work to me. The defsc was still referring to a function in css ns

tony.kay20:08:16

perhaps I misreadā€¦that would suck. I just spent 2 hours refactoring it all šŸ˜•

tony.kay20:08:47

no, see the destructuring parameter (4th to defsc) uses css/get-classnames

tony.kay20:08:06

which infects everything. I just refactored it all to split that out, since that didnā€™t need garden directly

tony.kay20:08:40

It seems to work. I just updated the template to use it, and the new sytle element works with SSR now, and everything compiled okā€¦

hjrnunes21:08:58

is there a shorcut fn to get an entity by ident? for when traversing the state in a mutation

wilkerlucio21:08:56

@hjrnunes the ident is literally the path on the app state for that entity, so a get-in should be enough