This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2016-03-10
Channels
- # admin-announcements (2)
- # alda (16)
- # beginners (22)
- # boot (55)
- # cider (11)
- # cljs-dev (5)
- # cljsrn (7)
- # clojure (115)
- # clojure-art (1)
- # clojure-finland (3)
- # clojure-greece (18)
- # clojure-madison (2)
- # clojure-norway (2)
- # clojure-poland (50)
- # clojure-russia (122)
- # clojure-uk (9)
- # clojurescript (28)
- # core-async (7)
- # cursive (4)
- # data-science (4)
- # datomic (65)
- # devcards (3)
- # dirac (91)
- # editors (7)
- # emacs (9)
- # hoplon (1)
- # jobs (5)
- # jobs-discuss (10)
- # juxt (2)
- # keechma (7)
- # ldnclj (31)
- # leiningen (3)
- # off-topic (22)
- # om (129)
- # om-next (1)
- # onyx (6)
- # proton (15)
- # protorepl (2)
- # re-frame (49)
- # reagent (13)
- # remote-jobs (1)
- # ring-swagger (2)
- # yada (19)
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-&-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...
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.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 faultas 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
=> @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}]}
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)})
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 :))
Your read function is assuming that the data is normalized but it is not. So you should look at the Person’s ident
function.
BTW are you passing an atom to the state instead of a map ? that can also prevent normalization.
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}}
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})}))
Then the only reason I can think of no normalization can be if ident is not correct for component Person.
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.nope, unfortunately that isn’t the issue. So if there’s something wrong with the om/Ident, perhaps I can redefine it?
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...
If not, much appreciated! I’ll wrap up there for now and try looking it over in the morning, maybe it will work then ...
but I am sure it should not be a lot of js . If there is no data it should be empty map.
folcon: can you paste the entire thing?
what you've written
definitely shouldn't be js, although the js might give you some clues..
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
tawus: its a devcards thing for testing om-next components
@matthavener: Thanks, didn’t know that.
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
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})])
How should i programmatically set these params? Or is this even the correct way to go about it?
currentoor: (set-query! this {:params {:ds-id ... :columns ...}})
@matthavener: where and when should that happen?
up to you.. on some user action?
i'd also say columns should really be a join
but i'm not sure exactly what they mean semantically, but if its choosing the data to return, it should probably be a join
let's say i have ds-id and columns in the app state already
how do i pull them out of the app-state and use them for remote fetch?
you could just read them out of the state in your :read function
and then return {:some-remote ast-with-ds-id-and-columns}
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
ah i see, so in the read function for :data-source/table
add them into the AST
thanks!
seanirby: for what its worth, I see that too but I haven't debugged it
@matthavener: @currentoor: what if the columns in the app state changed and you want a new read to happen? Will that happen?
i don't think so.. it does sound like you'd want to use something like set-query!
since your query params are varying on user input..
i may not be understanding your use case well enough, though
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)
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?
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
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
I am using datascript and on mutation I keep getting No queries exist for component path
. I am using routing using unions.
tawus: does the transact!ing component have a query?
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?
@jamescroft: I'm seing that too, it's seems like a regression. Will look into it and submit a fix
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.
I have difficultly in having proper key values when I use recursive parser functions. The query I am working on is a union query.
@tawus: I wrote 2 posts on recursive union queries. If you want to check them out, here are the links: http://anmonteiro.com/2016/01/exploration-patterns-om-next-part-1/ http://anmonteiro.com/2016/01/exploration-patterns-om-next-part-2/
@jlongster: looking into the path-meta
stuff today
seems promising so far.
do you still have the code for the slow bits around?
would be cool to test if there's any improvement
@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
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?
@jlongster: time in path-meta
with 5000 items down from 109ms to 55ms
losing ≈3 frames instead of almost 7
@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
I'm seeing a lot of time somehwere else though
not sure if it's React or not
even if querying across lots of data still takes time, at least that escape hatch is available
(there are 5000 items on the page)
trying to figure it out
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?
keys are right
I'm trying to figure out if it's an Om bottleneck of just DOM mutations
@anmonteiro: Chrome or Firefox's flame graph might help with that
let me try that
@dnolen: working on the path-meta
issue
I need to call some helpers like union-entry
, join?
, ident?
etc.
should I just repeat them in the parser namespace?
or do you want some different org?
(ended up not needing query-template
, etc. after all)
@anmonteiro: I’d be ok with moving stuff like that into utils if it’s not a big hassle
@dnolen: OK, I can do that. Should it be included in the same patch?
@anmonteiro: yes that’s fine
I've also noticed that there's a unique-ident?
helper for testing links
and we're sometimes comparing with _
I'm going to go ahead and refactor that too if that's OK
cool, thanks!
I'm seeing lots of perf gains
esp. with the #556 example
mutations are completely bypassed by path-meta
so the behavior is as expected (fast !)
@dnolen: FWIW, I'll have to move om.util
to .cljc
to use it in the parser.
I meant FYI *
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)))})))
This works but I need to "unwrap" in a more generic way for other types of remote queries. Suggestions?
So I've traced it to the merge-tree function, it seems to be aggressively pruning out too much data.
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/")}))
Is this something we are expected to do, or is this not a good idea?
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."
Awesome
thanks