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