This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2016-05-29
Channels
- # admin-announcements (2)
- # beginners (4)
- # boot (6)
- # cider (18)
- # cljs-dev (22)
- # cljsrn (13)
- # clojure (76)
- # clojure-czech (2)
- # clojure-dusseldorf (2)
- # clojure-russia (45)
- # clojure-sg (1)
- # clojure-spec (15)
- # clojurebridge-ams (6)
- # clojurescript (58)
- # cursive (4)
- # datomic (14)
- # dirac (31)
- # emacs (6)
- # funcool (2)
- # hoplon (2)
- # lein-figwheel (1)
- # om (124)
- # planck (17)
- # re-frame (12)
- # slack-help (11)
- # specter (12)
- # tmp-json-parsing (6)
- # yada (4)
now I still can’t figure out how to separate queries taking it out of context of joined query and only update things that are children in joined query
arrgghh… goddammit… someone please explain this to me, it's driving me nuts. I have a component that has a join query, e.g.:
(defui FooBar
static om/IQuery
(query [this]
`[{:data/foo ~(om/get-query Foo)}
{:data/bar ~(om/get-query Bar)}]))
I have read methods for Foo
and Bar
that dispatch on :data/foo
and :data/bar
, now I want to have RootComponent
that would include FooBar
, e.g:
(defui Root
static om/IQuery
(query [this]
`[{:root-data ~(om/get-query FooBar)}]))
now I need a read method that dispaches on :root-data
, right? So how do I tell it to find pieces in the state for :data/foo
and :data/bar
and if something missing ask server for data? Do I have to manually analyze the query?, is there a trick I could use to make this easier? I thought I could just do something like this:
(defmethod read :root-data
[{:keys [ast query]} _ _]
... {:remote (om/query->ast query)}
)
basically instead of sending whole AST (including :root-data
header) only send for foo
and bar
, and since server already knows how to handle that data, it should just work but it doesn'tI just don’t get it - if I have a component that works fine when used independently, but adding it to another component, makes it difficult to fetch data? Clearly I am missing something here
@ag It doesn't need to be so difficult. set-query
and fiddling with the AST on the client, in fact even having reads at all - these things are not that necessary for developing simple applications. I'm sure there are useful things you give up when you use a higher level framework such as Untangled. But I haven't minded giving up these three features.
can someone help me with query-root
thing: I believe in my send
I did everything right, yet something seems to be wrong with read
, can someone check if this looks right, it’s not sending what I want it to:
(defmethod read :root-data
[{:keys [state ast query]} key params]
(let [st @state]
{:remote (assoc ast :query-root true)}))
(defui SuperRoot
static om/IQuery
(query [this]
[{:root-data (om/get-query LedgerAccountIndexRoot)}])
Object
(render [this]
(html [:div])))
(om/add-root! reconciler SuperRoot (gdom/getElement "app"))
according to this SO post http://stackoverflow.com/questions/35675766/om-nexts-query-ast-and-ast-query-functions query-root
has to be added to the children components related reads
, but that’s the thing - I need to change it from root’s read
maybe something wrong with my send?
(defn send [queries cb]
(let [xhr (new js/XMLHttpRequest)
{:keys [query rewrite]} (om/process-roots (:remote queries))
request-body (transit/write (transit/writer :json) query)]
(.open xhr "POST" "/ledger-data")
(.setRequestHeader xhr "Content-Type" "application/transit+json")
(.setRequestHeader xhr "Accept" "application/transit+json")
(.addEventListener
xhr "load"
(fn [evt]
(let [response (transit/read (transit/reader :json)
(.. evt -currentTarget -responseText))
restructured-response (rewrite response)]
(cb restructured-response))))
(.send xhr request-body)))
using process-roots
shouldn’t break any existing components, right? now when I add LedgerAccountIndexRoot
as a root component it doesn’t work, whereas without process-roots
it used to
is there anything needs to be done on the server side to accommodate process-roots
in send
?
@ag: process-roots
is client-side only
your need to call the parser recursively at :root-data
only then will the parser dispatch on :data/foo
and :data/bar
do you know what 'calling the parser recursively` means?
I'll make an example
(defmethod read :app
[{:keys [parser query ast target] :as env} key _]
(let [remote (parser env query target)]
(if (and target (not-empty remote))
{:remote (update-in ast [:query] (fn [query] remote))}
{:value (parser env query)})))
(defmethod read :navbar/items
[{:keys [query state ast]} key params]
(let [st @state]
(if (nil? (get-in st [:app key]))
{:remote (assoc ast :query-root true) }
{:value (om/db->tree query (get-in st [:app key]) st)})))
Let's say you have a navbar under the key :navbar/items
that currently lives under :app
in a join.
The parser will dispatch on :app
. There you need to go one level deeper (because of the join). To go one level deeper, you call the parser recursively: (parser env query)
, the parser sits in the env. You just have to pick it outthe full query in this example is [{:app [{:navbar/items [:path :title]}]}]
the :query-root true
part needs to live in the children you want to make the root of the query
in your example you mark :root-data
with :query-root true
, and :root-data already is a root...
😄 took me a long time, till I figured this out
(let [remote (parser env query target)]
(if (and target (not-empty remote))
This is logic to determine if we're currently targeting remote queries, we have to make sure they aren't empty.
The parser runs twice, once for local reads, once for remote reads {:remote (update-in ast [:query] (fn [query] remote))}
, means we need to modify the ast to pull the query one level up
try running this example, and println
every step
Yea Om Next needs to be documented like x10. Tony Kay's tutorial does a good job though.
It's still alpha...
I convinced everyone to use Om Next, and for that it keeps stabbing me in my back over and over. I can’t even build simplest things… ;(
Om Next is a wonderful piece of engineering, you have to master the basics first though
Skin in the game 😄
Also if you don't have the problems that Om Next is trying to solve, you might be better off choosing something else
yeah, thanks but, no. I’ve been through that. it would seem at first that you won’t have those problems ever, then suddenly you wish you never started that project on angular
np, feel free to shoot me questions
@iwankaramazow: there’s something missing I’m not sure exactly what, it’s fetching data, but not putting it in the right place in state, can you take a look here please: https://github.com/agzam/om-basic-app/blob/master/src/cljs/om_basic_app/core.cljs
if I use component directly by swapping commented line, it works fine, but when it is sitting inside SuperRoot
it’s not working
@ag: you can either override merge-tree
in the reconciler or call the callback in send
with a query that you want to use to normalize the response
I haven't looked at the link but that should be what you're struggling with
merge-with into
it depends on what you're trying to acomplish
so I did :merge-tree (fn [a b] (spy (merge a b)) (merge a b))
and it does show that :root-data
has indeed the data, but the data does not appear in (om/props this)
in render
@iwankaramazow: thanks to you I may able to sleep tonight… although it’s 4 in the morning here 😉
The FAQ says, that if a transact! updates an unrelated key, one needs to add it to the transaction. Isn't this a blatant violation of encapsulation? How do people get around this, so that update logic can be contained within the reconciler?
@ag: http://untangled-web.github.io/untangled/ & #C0PULSD25 might be worth a look if you want a framework/set-of-libraries to help you use om next it’s made by @tony.kay btw
@bendlas: Saw that too, I’m not sure but I think the docs are out of date, because now the mutate function is supposed to return a map with a :value map that reports which keys were touched, thus keeping knowledge of all keys touched by a mutation encapsulated in the same place.
@dgroulx: I was under the impression, that the :value key, returned by the mutate function, is purely for documentation purposes. I didn't think it would update anything ..
related, how do I directly combine queries for components, without putting the query of a sub-component into a sub-key? I can't just concat the queries, because then the sub-component isn't on the metadata, which leads to this
not being usable for transact!
my current workaround is to use a sub-key and duplicate the data in the reconciler, but that leads to the problems above, with updating "unrelated" keys
@bendlas: you can't circumvent this
About breaking encapsulation: transact!
is a subtree concern. A UI cannot possibly indicate what things it depends on at some "leaf" of the UI and hope to remain composable, since that leaf component should care only about its local state. Thus, mutations that can affect many things can only be reasoned about at some node in the UI tree that "owns" all of the things that can be affected.
The top-level component of that subtree should pass a function down which contains the mutation.
I don't know if this makes sense though
a mutate function in the parser doesn't care about return keys though
those are only for documentation purposes
@iwankaramazow: doesn't quite make sense. I thought composability was gained by having an interpreter in the reconciler.
subcomponents should just specify a transaction. everything else will be decided up-tree
or are you talking about the inability of a containing component to transform transactions from its subcomponents?
I'm talking about: example (transact! reconciler [(fire-missiles!) :some-unrelated-key])
it's the :some-unrelated-key
I'm talking about
exactly: that example doesn't make any sense at all, since a leaf should never now about :some-unrelated-key
except, maybe, if you don't use transact!
in leaves and instead pass down an event handler
if you have a mutation deep down in the view, it triggers an update. let's say you add a friend. Your friend-count on some other part of the screen has to increment
coupling is needed?
does this really break encapsulation?
I've no idea if this is possible
@anmonteiro knows why this probably isn't 😄
well, I hope that he'll get around to straighten this out for me. it's really frustrating
@bendlas: imagine you have a list of counters in your UI, and you have each counter remove itself from the list.
because Om Next indexes which queries map to each components, when you transact!
on a single counter to remove it, it doesn’t have any knowledge about the list it belongs to (or possibly, a number of lists).
So you need to specify which part of your UI tree needs to react (pun intended) to such change, by specifying a key to re-read
also to clarify, :value {:keys …}
in a parser’s mutation doesn’t serve any other purpose than documentation of such mutation
@anmonteiro: ohai.
well, I was just telling @dgroulx about the :value {:keys ..}
caveat, so no news there.
in most cases this might not be needed
i.e. if you get transactions right
in my example you could perform the transaction on the list (passing the counter ID by param to the mutation) and not need to re-read any key
so there you go
2 different subtrees
are you using normalization though?
so there you go 🙂
if you were using normalization, it would solve itself
because Om Next is smart enough to know which ident
s map to which components and keep the UI in sync
but what you said just screams normalization to me
my use case is just this: I have information about a currently showing modal dialog in my app state
“two sibling components render the same piece of data"
normalization
as soon as that dialog is enabled, i need to show it as well as darken the background, this are sibling components
so if normalization is the answer, this needs to be documented in the FAQ, because right now I wasn't able to make that connection on my onw
I can agree that Om Next needs a lot more documentation
feel free to add that to the FAQ, I think the FAE is supposed to be collaborative
also still, I don't see why I need to make my components smarter for this use case and not just be able to solve it in the reconciler
what do you propose?
2) the same behavior, whether an update comes from within a transaction or from swap!
ing the underlying atom
@anmonteiro: sorry if you're getting tired of answering this questions for the nth time .... I promise to update the FAQ as soon as I have a clear picture
I suspect apply
is misplaced in om.next/update-query
. Shouldn't it be (set-query! component (apply f ...))
instead of current (apply set-query component (f ...))
:
https://github.com/omcljs/om/blob/master/src/main/om/next.cljs#L743
PSA: Remember to add the ^:once
metadata to all of your components created with defui
if you are using a reload tool like Figwheel or boot-reload. If you don’t you might run into errors like mine: "No queries exist for component path (sample/A sample/B)"
I just spent an hour debugging that error. sample/B
was missing the ^:once
metadata and getting re-evaled on every reload, but the old component instance was cached in the Om indexer.