Fork me on GitHub
#om
<
2016-03-10
>
folcon02:03:20

Hi, anyone have a moment? Just banging my head against this at the minute. Does anyone know how to list the props available in om/props? I’m trying out the https://github.com/omcljs/om/wiki/Components,-Identity-&amp;-Normalization tutorial and the line (println "Render ListView" (-> this om/path first)) gives me Render ListView nil. I’m trying to see if I’m passing in the wrong props, but so far all I’m getting is nils for all the props I’ve attempted...

tawus02:03:03

On first look, shouldn’t it be om/props instead of om/path.

folcon02:03:47

sorry if I’m unclear: The function that appears to be the problem is:

(defui ListView
  Object
  (render [this]
    (println "Render ListView" (-> this om/path first))
    (let [list (om/props this)]
      (apply dom/ul nil
        (map person list)))))
Where the line (println "Render ListView" (-> this om/path first)) prints Render ListView nil, which I don’t think is correct.

folcon02:03:33

in the example it is referenced by:

(def list-view (om/factory ListView))

(defui RootView
  static om/IQuery
  (query [this]
    (let [subquery (om/get-query Person)]
      `[{:list/one ~subquery} {:list/two ~subquery}]))
  Object
  (render [this]
    (println "Render RootView")
    (let [{:keys [list/one list/two]} (om/props this)]
      (apply dom/div nil
        [(dom/h2 nil "List A")
         (list-view one)
         (dom/h2 nil "List B")
         (list-view two)]))))
I think the props I’m passing are at fault

folcon02:03:04

as if I insert a println as follows:

(defui RootView
  static om/IQuery
  (query [this]
    (let [subquery (om/get-query Person)]
      `[{:list/one ~subquery} {:list/two ~subquery}]))
  Object
  (render [this]
    (println "Render RootView")
    (let [{:keys [list/one list/two]} (om/props this)]
      (println "Below Let" one two)
      (apply dom/div nil
        [(dom/h2 nil "List A")
         (list-view one)
         (dom/h2 nil "List B")
         (list-view two)]))))
I get: Below Let nil nil

tawus02:03:37

What is the output for @reconciler ?

folcon02:03:47

=> @reconciler
{:list/one [{:name "John", :points 0} {:name "Mary", :points 0} {:name "Bob", :points 0}], :list/two [{:name "Mary", :points 0, :age 27} {:name "Gwen", :points 0} {:name "Jeff", :points 0}]}

tawus02:03:43

and read function ?

folcon03:03:34

as in what is the read function? as defined in in the appendix.

(defmulti read om/dispatch)

(defn get-people [state key]
  (let [st @state]
    (into [] (map #(get-in st %)) (get st key))))

(defmethod read :list/one
  [{:keys [state] :as env} key params]
  {:value (get-people state key)})

(defmethod read :list/two
  [{:keys [state] :as env} key params]
  {:value (get-people state key)})

tawus03:03:49

I am asking for these details as the state doesn’t seem to normalized. If you are following the tutorial correctly, it should be normalized. (I am assuming the tutorial has not broken since I tried it :))

tawus03:03:18

Your read function is assuming that the data is normalized but it is not. So you should look at the Person’s ident function.

tawus03:03:12

BTW are you passing an atom to the state instead of a map ? that can also prevent normalization.

folcon03:03:22

sorry, I’m not entirely following

folcon03:03:15

to get a normalised example I can define it:

=> (def norm-data (om/tree->db RootView init-data true))
=> (pp/pprint norm-data)
{:list/one
 [[:person/by-name "John"]
  [:person/by-name "Mary"]
  [:person/by-name "Bob"]],
 :list/two
 [[:person/by-name "Mary"]
  [:person/by-name "Gwen"]
  [:person/by-name "Jeff"]],
 :person/by-name
 {"John" {:name "John", :points 0},
  "Mary" {:name "Mary", :points 0, :age 27},
  "Bob" {:name "Bob", :points 0},
  "Gwen" {:name "Gwen", :points 0},
  "Jeff" {:name "Jeff", :points 0}}, 
 :om.next/tables #{:person/by-name}}

tawus03:03:11

@reconciler shows your current state which doesn’t seem to be normalized.

folcon03:03:00

ok but what would prevent that from happening?

tawus03:03:04

What do you pass as :state to om/reconciler ? an atom or a map

folcon03:03:26

A map:

(def init-data
  {:list/one [{:name "John" :points 0}
              {:name "Mary" :points 0}
              {:name "Bob"  :points 0}]
   :list/two [{:name "Mary" :points 0 :age 27}
              {:name "Gwen" :points 0}
              {:name "Jeff" :points 0}]})

(def reconciler
  (om/reconciler
    {:state  init-data
     :parser (om/parser {:read read :mutate mutate})}))

tawus03:03:10

Then the only reason I can think of no normalization can be if ident is not correct for component Person.

folcon03:03:54

ok, I just attempted swapping the state value to an atom, which didn’t help. Prior state values have been atoms, but I’m just trying this out, so I thought I’d go with it.

(defui Person
  static om/Ident
  (ident [this {:keys [name]}]
    [:person/by-name name])
  static om/IQuery
  (query [this]
    '[:name :points :age])
  Object
  (render [this]
    (println "Render Person" (-> this om/props :name))
    (let [{:keys [points name] :as props} (om/props this)]
      (dom/li nil
        (dom/label nil (str name ", points: " points))
        (dom/button
          #js {:onClick
               (fn [e]
                 (om/transact! this
                   `[(points/increment ~props)]))}
          "+")
        (dom/button
          #js {:onClick
               (fn [e]
                 (om/transact! this
                   `[(points/decrement ~props)]))}
          "-")))))
Here is my person component.

tawus03:03:09

If you want normalization then you have to pass map NOT atom as :state

folcon03:03:45

ok, I’m doing that at the moment. I’ll just try restarting figwheel just in case.

folcon03:03:42

nope, unfortunately that isn’t the issue. So if there’s something wrong with the om/Ident, perhaps I can redefine it?

tawus03:03:56

Seems, you have done everything right. What version of om are you using ?

folcon03:03:30

[org.omcljs/om "1.0.0-alpha30"]

tawus03:03:05

Sorry, but I am really out of ideas simple_smile

folcon03:03:39

oh well, I’ll have to try this again some other time then >_<… Thanks a lot for helping, it’s always frustrating when you’re trying out a tutorial for the first time and it doesn’t work. Btw, do you know how to get a listing of the props? I’ve tried (println (om/props this)) and (println (keys (om/props this))), but unfortunately I get a lot of js I have to then read through...

folcon03:03:42

If not, much appreciated! I’ll wrap up there for now and try looking it over in the morning, maybe it will work then simple_smile...

tawus03:03:24

(om/props this) should give you the properties.

tawus03:03:14

but I am sure it should not be a lot of js simple_smile. If there is no data it should be empty map.

matthavener03:03:43

folcon: can you paste the entire thing?

folcon03:03:55

um to (om/props this)?

folcon03:03:17

or what I’ve written?

folcon03:03:41

1 sec I’ll do both.

matthavener03:03:49

what you've written

matthavener03:03:00

definitely shouldn't be js, although the js might give you some clues..

tawus03:03:33

You should try (om-next-root RootView) instead of root-view

tawus03:03:38

I am not sure what om-next-root is

matthavener03:03:40

yep, according to the docs that takes a component instead of a factory https://github.com/bhauman/devcards/blob/master/example_src/devdemos/om_next.cljs#L16

tawus03:03:49

but shouldn’t it be (om/add-root! reconciler RootView (gdom/getElement "app”))

matthavener03:03:10

tawus: its a devcards thing for testing om-next components

tawus03:03:51

@matthavener: Thanks, didn’t know that.

folcon03:03:02

thank you simple_smile...

folcon03:03:05

much appreciated @tawus and @matthavener for both your help! Hopefully I don’t get stuck again. Looks like I misread that part of the devcards docs

currentoor04:03:22

I've got an om.next component that relies on remote fetch. I've hard-coded the parameters for the remote fetch in the params.

(params [_]
    {:ds-id   #uuid "56e071fe-4141-4f4f-8e60-38695b16b068"
     :columns ["campaign_name" "impressions" "account_name" "clicks" "spend"]})
  (query [_]
    '[(:data-source/table {:ds-id   ?ds-id
                           :columns ?columns})])

currentoor04:03:05

How should i programmatically set these params? Or is this even the correct way to go about it?

matthavener04:03:31

currentoor: (set-query! this {:params {:ds-id ... :columns ...}})

currentoor04:03:55

@matthavener: where and when should that happen?

matthavener04:03:09

up to you.. on some user action?

matthavener04:03:20

i'd also say columns should really be a join

matthavener04:03:02

but i'm not sure exactly what they mean semantically, but if its choosing the data to return, it should probably be a join

currentoor04:03:41

let's say i have ds-id and columns in the app state already

currentoor04:03:06

how do i pull them out of the app-state and use them for remote fetch?

matthavener04:03:25

you could just read them out of the state in your :read function

matthavener04:03:51

and then return {:some-remote ast-with-ds-id-and-columns}

seanirby04:03:21

why would react be complaining about not supplying unique keys when I'm just using nested om.dom/div components ? I can verify on the DOM that all the react ID's are unique

currentoor04:03:53

ah i see, so in the read function for :data-source/table add them into the AST

matthavener04:03:23

seanirby: for what its worth, I see that too but I haven't debugged it

seanirby04:03:12

matthavener: cool. thanks

therabidbanana04:03:00

@matthavener: @currentoor: what if the columns in the app state changed and you want a new read to happen? Will that happen?

matthavener04:03:52

i don't think so.. it does sound like you'd want to use something like set-query!

matthavener04:03:19

since your query params are varying on user input..

matthavener04:03:03

i may not be understanding your use case well enough, though

therabidbanana04:03:57

The goal use case would be - we have a table widget that can be configured - configuration initially loaded from the server (initial list of columns) - then the user can mutate that list of columns - and that will generate a new report, requiring a new request on the server to generate a new table of data (which might take a few seconds to generate if the data is not currently all available)

seanirby06:03:02

i have some data, :foos/list that i query for in my root component. In addition to rendering a FoosList component I also need to render sibling components that each need a particular foo. When it comes to render these sibling components, what is the idiomatic to get the data that each one needs. I was thinking about including a foos-by-name list in my root query and doing (foo-factory (foos-by-name foo-name)) to render each component, but that doesn't feel right. Any suggestions?

jamescroft11:03:28

Has anyone run into a problem on alpha31 with the data and the UI going out of sync? It can be demonstrated by using the alpha31 for the Autocomplete example from the wiki (Code: https://gist.github.com/crofty/0e6e63fc676059ac1287). It works as expected on alpha30 but on alpha31 the UI for the autocomplete results goes out of sync

petterik11:03:54

I have been using query params wrong, like this {(:key {:param :val}) [...]} , instead of ({:key [...]} {:param :val}). It's been working fine until I updated a child in a component with query params. (om/focus->path ..) doesn't remove (and it shouldn't?) the params when the params wrap the key instead of the map. Just a warning for you all

tawus12:03:40

I am using datascript and on mutation I keep getting No queries exist for component path. I am using routing using unions.

matthavener12:03:54

tawus: does the transact!ing component have a query?

rauh12:03:10

So I do a remote mutation: If I simply say :remote true what gets send to transit is [(example/foo {})], if I however do a :remote (om/query->ast '[(example/foo {})]) then what gets send is [[(example/foo {})]]. Note the extra vector. Thoughts?

anmonteiro13:03:04

@jamescroft: I'm seing that too, it's seems like a regression. Will look into it and submit a fix

tawus13:03:11

I am trying to understand how recursive queries works. Is it valid to return something like {:value {:one {:two result}}} from read to simulate a recursion or we MUST call (parser env query) for read for a valid recursion.

tawus13:03:02

I have difficultly in having proper key values when I use recursive parser functions. The query I am working on is a union query.

anmonteiro15:03:08

@jlongster: looking into the path-meta stuff today

anmonteiro15:03:25

seems promising so far.

anmonteiro15:03:48

do you still have the code for the slow bits around?

anmonteiro15:03:08

would be cool to test if there's any improvement

jlongster19:03:51

@anmonteiro: awesome! it's pretty messy code and is broken right now, but I can try to get it working again for a test case

jlongster19:03:04

will probably be tomorrow

adamfrey19:03:28

why does om.next recalculate props after a call to set-state!, which just touches component local state? The app state shouldn’t have changed, so why parse again? Is there something I’m missing?

anmonteiro20:03:40

@jlongster: time in path-meta with 5000 items down from 109ms to 55ms

anmonteiro20:03:29

losing ≈3 frames instead of almost 7

jlongster20:03:37

@anmonteiro: sweet! that's great. that will make it so that I could treat my data as a blob and not query into it, returning the entire list raw on a single key, so the query shouldn't take any time

anmonteiro20:03:57

I'm seeing a lot of time somehwere else though

anmonteiro20:03:02

not sure if it's React or not

jlongster20:03:04

even if querying across lots of data still takes time, at least that escape hatch is available

anmonteiro20:03:08

(there are 5000 items on the page)

anmonteiro20:03:28

trying to figure it out

jlongster20:03:38

5000 is a lot, wouldn't be surprised if it was DOM access or something. or maybe the React keys aren't right and it's recreating DOM nodes?

anmonteiro20:03:16

keys are right

anmonteiro20:03:54

I'm trying to figure out if it's an Om bottleneck of just DOM mutations

jlongster20:03:39

@anmonteiro: Chrome or Firefox's flame graph might help with that

anmonteiro20:03:18

let me try that

anmonteiro21:03:57

@dnolen: working on the path-meta issue

anmonteiro21:03:24

I need to call some helpers like union-entry, join?, ident? etc.

anmonteiro21:03:38

should I just repeat them in the parser namespace?

anmonteiro21:03:54

or do you want some different org?

anmonteiro21:03:06

(ended up not needing query-template, etc. after all)

dnolen21:03:24

@anmonteiro: I’d be ok with moving stuff like that into utils if it’s not a big hassle

anmonteiro21:03:30

@dnolen: OK, I can do that. Should it be included in the same patch?

dnolen21:03:01

@anmonteiro: yes that’s fine

anmonteiro21:03:09

I've also noticed that there's a unique-ident? helper for testing links

anmonteiro21:03:29

and we're sometimes comparing with _

anmonteiro21:03:39

I'm going to go ahead and refactor that too if that's OK

dnolen21:03:45

no problem

anmonteiro21:03:52

cool, thanks!

anmonteiro21:03:03

I'm seeing lots of perf gains

anmonteiro21:03:14

esp. with the #556 example

anmonteiro21:03:40

mutations are completely bypassed by path-meta so the behavior is as expected (fast !)

anmonteiro21:03:01

@dnolen: FWIW, I'll have to move om.util to .cljc

anmonteiro21:03:11

to use it in the parser.

dnolen21:03:16

yes will be necessary

anmonteiro21:03:24

I meant FYI *

currentoor21:03:32

I've got this read function that retrieves a local value and a remote value.

(defmethod read :boards/active
  [{:keys [state query parser ast] :as env} key _]
  (let [st @state]
    (if-let [ref (get st key)] ; ref looks like [:board/by-id 1]
      {:value  (om/db->tree query ref st)
       :remote (assoc ast :params {:board-id (second ref)})}
      {:value nil})))
Using the parser on the backend I get a response of the form {:boards/active ...} and this requires me to "unwrap" the response on the client's send-callback, like so.
(defn transit-post [url]
  (fn [params cb]
    (ajax/POST url
        {:params  params
         :handler (fn [resp]
                    (cb (:boards/active resp)))})))

currentoor21:03:10

This works but I need to "unwrap" in a more generic way for other types of remote queries. Suggestions?

currentoor21:03:55

So I've traced it to the merge-tree function, it seems to be aggressively pruning out too much data.

currentoor22:03:30

Hmm, after poking around I found a way to make it work, by writing my own merge-tree function.

(def reconciler
  (om/reconciler {:state      initial-state
                  :parser     parser
                  :merge-tree (fn [local-state remote-data]
                                (merge-with
                                 (fn [ls rd]
                                   (if (and (map? ls) (map? rd))
                                     (merge ls rd)
                                     (or rd ls)))
                                 local-state remote-data))
                  :send       (transit-post "/api/")}))

currentoor22:03:57

Is this something we are expected to do, or is this not a good idea?

hueyp22:03:17

I think writing your own merge is normal

hueyp22:03:26

from the om-tutorial : “`:merge-tree` A function used to take a response from the server and merge it into your app database. Defaults to a very naive shallow merge."