# om

This page is not created by, affiliated with, or supported by Slack Technologies, Inc.

peeja 23:00:14

Damn. Fair enough. :simple_smile:

peeja 23:00:39

It looks like there's no way to use Devcards without Om 1.0 (ie, React 0.14)

minimal 23:01:28

I’ve been using the om 1 alphas for old om in production for a bit

peeja 23:01:53

I'm going to try using the alphas in a devcards-only profile, which should work, but obviously just running 1.0 in production would be simpler :simple_smile:

peeja 23:02:13

@minimal: Well, that's good to hear

minimal 23:02:49

Get some react warnings in dev and had to replace one function get-node

minimal 23:03:58

I needed react 0.14 and devcards too

minimal 23:06:00

I removed (.getDOMNode)from my version of get-node

minimal 23:07:19

@minimal uploaded a file: Untitled

tony.kay 23:10:33

@anmonteiro: not sure what you're talking about on order. OR is commutative, and the short-circuit doesn't matter

anmonteiro 23:11:36

@tony.kay: right, maybe that line doesn't need changing after all

tony.kay 23:12:57

I'm submitting PR now

dnolen 23:22:23

@tony.kay: cool

tmorten 23:36:41

@frankie: For basic routing I have been using a mix of secretary and I usually have a parameter on the Root UI view that I set via set-query! from my “defroute” to pass in anything additional from route info...

tmorten 23:37:55

@tmorten uploaded a file: Untitled and commented: Something like this...

hueyp 23:45:07

@tmorten: do you have a more-root-than person-dashboard/RootView ?

hueyp 23:45:40

ah nvm, is mount setting it as the root?

tmorten 23:48:56

@tmorten uploaded a file: Untitled

hueyp 23:49:10


tmorten 23:49:33

might not be the most straight forward or “cool” but works for me

hueyp 23:49:55

no I really like it … I don’t know why I that hadn’t cross my mind to switch out roots like that on routing

hueyp 23:50:55

do you know what the behavior is like between the add-root! and set-query!?

tmorten 23:51:07

I know some people like to flip-out the different “views” inside a root view based upon state, but that just never works that great for me.

hueyp 23:51:08

e.g. before something like the id is set

hueyp 23:51:21

yah, for some reason that is where my mind just immediately went

hueyp 23:51:30

but this is another good perspective to think about

tmorten 23:51:34

The RootView needs to be mounted before you can actually set-query! on the component

hueyp 23:52:05

yah, and so it kind of reconciles twice? once without an id, once with? or do you just not notice any kind of in between state?

hueyp 23:52:42

maybe all that jazz is queued so it only happens once

hueyp 23:54:50

maybe reconcile is not the word … but like parsing, e.g. sending a remote query once on add-root! and once on set-query!

tmorten 23:55:05

It does but I think I have it handled via lifecycle methods in component

hueyp 23:55:13


tmorten 23:55:21

I set initial parameter to nil

tmorten 23:55:34

(params [_] {:id nil})

hueyp 23:56:13

thanks for description :simple_smile: it does seem like it would be nice to be able to set initial params on add-root!

tmorten 23:57:34

@hueyp: no problem. I have a small app in production and this method has been working for me thus far

hueyp 05:31:12

is there a detectible difference in the parser for when a read is part of a transact! vs add-root! / set-query!?

tmorten 05:42:51

@hueyp: I think that in the AST the :type keyword is set to :call on a mutation...someone can correct me if I am wrong on that one...

tmorten 05:43:00

i.e. transact!

hueyp 05:45:01

but for the reads in the transact! query, e.g. [(user/set-name {:name “Bob” }) :user/name] — the :user/name parse

hueyp 06:02:38

I guess the idea is to use force to mark them … but not sure why transact! reads don’t default to force?

tmorten 06:05:13

@hueyp: not sure on that one...

hueyp 06:07:14

yah, might be barking up the wrong tree … just thinking of what information to use when deciding whether to cache a read. Like if I transition routes, I’m cool not hitting a remote for anything already in the local state, but if I do a transact! I want to make sure I always send remotes … I kind of view the reads in transact! as existing for exactly this purpose

drcode 06:30:33

Hi everyone: I'm a bit confused by what types of "reads" are supported by om/transact!... In the documentation it calls them "keys" instead of "reads" and looking at the source code it uses the term "simple reads"
I have a transaction as follows:
(om/transact! reconciler '[(do-this {}) {:read [:inner-read]}]) Am I correct in think that this is not a "simple read" and that I instead can only write:
(om/transact! reconciler '[(do-this {}) :read]) In other words, are "reads" other than atomic keywords supported? (I ask because I can't get more detailed reads to work)

hueyp 06:36:08

you can have joins in those reads too. the distinction is that not all queries are … not sure the proper word, but ‘root’ queries

hueyp 06:37:15

for instance [:id :name] is a legit query, but might not have enough context to parse … the parser might have a key :current-user … so the [:id :name] query on a component would be pulled into a root query

hueyp 06:37:59

so I’d say [:id :name] is a simple query, and [{:current-user [:id :name]}] is a full or root query or whatever the non-simple term is

drcode 06:39:29

@hueyp: Thanks- that's how I thought they worked as well, thanks for the confirmation (it's seems like the logical expectation that you can have arbitrary reads, as long as they are rooted) now I still need to figure out why it doesn't seem to work that way for me...

hueyp 06:40:23

and to add to that … when you transact!, om is going to transform those reads by walking to the root … the exact mechanics I’m not 100% sure of, but if you have two components, [{:current-user (om/get-query Child)}] and Child transacts! with a key of :name it might resolve into a full query of {:current-user [:name]}

hueyp 06:41:13

but its smarter than just that … I’ve had it notice that the key isn’t in the direct components query, but in the parents query, and it worked out fine … so I have that on my list to really understand

hueyp 06:42:36

it might also remove read keys from a transact! too

hueyp 06:43:19

which seems to go against documentation, so I want to double check .. but I think that happens :simple_smile:

hueyp 06:43:45

actually it might not go against docs at all, maybe if its not indexed it feels free to remove it

drcode 06:43:58

okok, so there's a bit of black magic there after all that might be tripping up my transactions... that's probably what I'm running into...
> it might also remove read keys from a transact! too Yup, that's what seems to be happening, and I can't figure out why...

drcode 06:44:34

> maybe if its not indexed Sigh... guess it's time to dig into the indexer logic :simple_smile:

drcode 06:44:51

@hueyp Thanks for the helpful pointers

hueyp 06:44:56

I don’t know exactly whats going on … I just remember testing by putting a fake key into a transact! and it didn’t survive

hueyp 06:45:11

lemme see if I have the sample handy :stuck_out_tongue_winking_eye:

drcode 06:45:50

@hueyp: Yes, it seems like the correct action with a garbage key would be to throw a parser error, not just sanitize the query

drcode 06:47:57

(but I don't understand the situation well enough to know for sure)

hueyp 06:48:54

yah, so if I do: (om/transact! this [(todo/toggle {:id ~id}) :completed? :random-key]) it turns into transacted '[(todo/toggle {:id 1}) {:current-user [{:todos [:completed?]}]} {:current-user [{:todos [:completed?]}]}] … the :random-key is gone

hueyp 06:49:55

my assumption is that :random-key is not indexed so it gets taken out … but I need to check the code … clearly it was just me trying things out :simple_smile:

hueyp 06:50:16

but you can see it properly … sorta … hoists the :completed? up into the root query

drcode 06:52:04

Yes, that's the cause of my confusion in a nutshell, I see the same weirdness... I assume it's because the transactions go through the indexer in a "bottom up" fashion, causing the resulting query to be very ugly, but technically correct (As long as you are doing everything else correctly)

hueyp 06:53:59

I couldn’t actually get that above to reliably work … e.g. [(todo/delete {:id ~id}) :todosCount :todos] gets turned into [(todo/delete {:id 1}) {:current-user [:todosCount]} {:current-user [{:todos [:id :text :completed? {:author [:id]}]}]}] which has :current-user twice as a root key the parser just overwrites the value

hueyp 06:54:52

so instead I tried: [(todo/delete {:id ~id}) {[:person/by-id author-id] [:todosCount {:todos (om/get-query Todo)}]}]

hueyp 06:54:56

e.g. I root it myself

hueyp 06:55:40

that results in: transacted '[(todo/delete {:id 1}) {[:person/by-id 1] [:todosCount {:todos [:id :text :completed? {:author [:id]}]}]}]

drcode 06:56:19

Yeah, all sensible advice, thanks for this info, I feel like I am at step 27 of the 100 steps of Om Next mastery now :simple_smile:

hueyp 06:56:23

but this is just a braincheck app seeing how things work

hueyp 06:57:29

a lot of this stuff doesn’t matter unless you have a remote … local state just re-running ui->props hides any data synchronization problems you might have

hueyp 06:57:35

like if your data is 100% local

hueyp 06:58:05

but if you need remotes to be synched, you have to figure out how to make om send the right reads

hueyp 06:58:13

which is like exactly what I’m working on right now

hueyp 06:58:45

but you can make a sample app which has local state and no reads in transacts and it works great

drcode 06:59:00

Yes, the remote part is where things are so challenging...

drcode 07:24:03

@hueyp: FYI, your advice was spot-on... followed your thinking and it fixed all my problems- Thanks!

thiagofm 18:21:58

(om next) Hello, I have a component nested inside another one. The main one has a query to [:game/tutorial], for example. That query works perfectly and returns the desired output. But the nested component have the same query specified and (om/props this) returns nothing. Anyone knows what could be happening?

iwankaramazow 18:25:18

@thiagofm: Is your query composing to the top? (i.e. by om/get-query)

thiagofm 18:28:29

iwankaramazow: no, very straightforward stuff, like this:

thiagofm 18:28:40

for some reason it doesn't execute the query :disappointed:

iwankaramazow 18:32:18

'[lambdas/total] -> '[:lambdas/total]

iwankaramazow 18:32:47

just a guess, but make sure you're using keywords if your parser handles keywords

thiagofm 18:36:28

Yeah, sorry. My example is wrong. I'm using your version but still doesn't work :disappointed:

thiagofm 18:37:01

I'm using the exact same query of another component, which retrieves results, but this one doesn't, it just returns nil for om/props

thiagofm 18:40:09

It kinda sucks to hit those walls and get no error message or anything at all :disappointed:

thiagofm 18:40:38

Can't seem to be able to debug what goes to query or anything :stuck_out_tongue:

iwankaramazow 18:46:42

hmm, if another component has that query and gets its props

iwankaramazow 18:46:52

just pass those props down to the child

iwankaramazow 18:47:10

the query on the child is redundant

thiagofm 18:47:43

@iwankaramazow: I'm just using the same query because it doesn't work for any query. I actually wanted to use a different query

iwankaramazow 18:48:52

do you have a link to the project? (github?)

thiagofm 18:55:53

@iwankaramazow: it's using chestnut, so even to run it is easy. So, both the Window and Player components are the same, but in the Player component, the query doesn't run (it prints just nil, while the other prints {:game/tutorial #{[1 false]}})

thiagofm 18:56:14

I'm trying to write a simple game

thiagofm 18:56:49

One detail is that I'm using Datascript, the queries you can find in haxlife/data/query

iwankaramazow 19:07:26

Window is your root, you're not passing any props down

iwankaramazow 19:07:42

(tutorial-comp id) (game-window-comp)

iwankaramazow 19:07:59

only id get's down

iwankaramazow 19:09:01

the root-query 'injects' the result as props on the root component

thiagofm 19:09:19

Why do I need to pass the props down? If I (game-window-comp 1) it still doesn't work :disappointed:

thiagofm 19:10:13

I admit I'm a bit lost. But if I'm querying in the Player component, shouldn't this be enough by itself?

iwankaramazow 19:11:30

If a component deep inside the tree has a query, your have to compose it to the top with om/get-query on the root-component
The root-component will get the result of that query as props

iwankaramazow 19:11:38

then you have to manually pass them down

iwankaramazow 19:11:43

no way around this

thiagofm 19:12:02

Okay, so I can't query on Player?

iwankaramazow 19:12:48

I'll write some example code

thiagofm 19:14:11

Okay, thanks a lot for all the help so far.

iwankaramazow 19:14:12

(defui Window static om/IQuery (query [this] [(first (om/get-query Player))]) Object (render ...))

iwankaramazow 19:14:50

Window is your root-component, Player a child. This is an example of the query composing to the top.

thiagofm 19:15:19

So, Window will basically do queries for the whole app?

thiagofm 19:15:53

Wouldn't this make Window become a 1k line component soon? :disappointed:

anmonteiro 19:15:54

@dnolen: updated wiki docs to reflect the computed docstring about replacing previous computed props. here's the diff:

anmonteiro 19:16:47

@iwankaramazow: your example is wrong because Window is stealing Player's props

iwankaramazow 19:16:50

@thiagofm: it'll contain the full query, that might be a long query :stuck_out_tongue:

thiagofm 19:17:42

Okay, great. I guess this is written in "Components, Identity & Norm...", I've read all the docs multiple times, but I'm just really getting it now that I'm writing stuff down.

iwankaramazow 19:18:15

Just remember that a child when implementing IQuery, doesn't get the result from it's query, it gets the result trough props passing down the tree

thiagofm 19:18:31

I quite like this approach though, if I could query separately in each component it would feel as everything had some sort of state

thiagofm 19:18:58

That was very nice help, thanks!

iwankaramazow 19:19:59

If I recall correctly, Relay/GraphQL injects props at without you passing down the props from the root

iwankaramazow 19:20:52

In the beginning the notion of passing everything down seemed a bit strange at first, but it hasn't given me any problems (yet :innocent:) )

thiagofm 19:24:08

Great, lemme see if I can fix it now :simple_smile:

thiagofm 19:24:48

@iwankaramazow: any special reason of why you used the first function on (first (om/get-query Player)) ?

dnolen 19:24:50

@anmonteiro: looks good to me

iwankaramazow 19:34:12

@thiagofm: would be [[:game/tutorial]] without (first ...), with it gives you [:game/tutorial]

iwankaramazow 19:34:32

just making sure the query is valid

thiagofm 19:36:16

@iwankaramazow: just a last question, I guess. the Player component is deep down, how can I reference it? I have to require its file? Wouldn't this create a circular dependency error?

iwankaramazow 19:38:11

@thiagofm: no, you should require Player in Window. Only if you also require Window in Player you get a circular dependency

seantempesta 20:18:05

So, om-next is pushing my (rather limited) clojure skills. As soon as I moved my app-state, read, mutate and reconciler definitions to a separate namespace (weather-project.state) I started getting this error:

No method in multimethod 'weather-project.state/mutate' for dispatch value: weather-project.ios.core/zip-change

Sure enough, if I change the definition from:

``` (defmulti mutate om/dispatch)

(defmethod mutate 'zip-change [{:keys [state]} _ {:keys [zip-text]}] {:action (fn [] (swap! state assoc-in [:zip] zip-text))}) to (defmulti mutate om/dispatch)

(defmethod mutate 'weather-project.ios.core/zip-change [{:keys [state]} _ {:keys [zip-text]}] {:action (fn [] (swap! state assoc-in [:zip] zip-text))}) ```

seantempesta 20:18:54

It works. But how can I reuse that method if I have to explicitly define what namespace the function will be called in?

hueyp 20:20:53

I’m guessing the way you are quoting the transact is setting namespace

seantempesta 20:21:22

(om/transact! this `[(zip-change {:zip-text ~zip-text})])

seantempesta 20:21:37

How should I be quoting it?

hueyp 20:21:39

yah, add in front of zip-change I think

hueyp 20:21:59

I’m no expert on quoting, but I think in makes it so the symbol isn’t namespaced

hueyp 20:23:02

no I’m dumb its ~ in front of the symbol … crap lemme find it

seantempesta 20:23:51

Yeah, the quote doesn’t work:

`` weather-project.ios.core=>[(zip-change {:zip-text "asdf"})]
[(weather-project.ios.core/zip-change {:zip-text "asdf"})]

weather-project.ios.core=> `[('zip-change {:zip-text "asdf"})]
[((quote weather-project.ios.core/zip-change) {:zip-text "asdf"})] ```

hueyp 20:24:24

try ~zip-change

hueyp 20:24:51

the easiest thing to do is just namespace mutations yourself :simple_smile: zip/change :simple_smile:

seantempesta 20:25:24

Ah, it’s tilda quote

seantempesta 20:25:35

(om/transact! this `[(~'zip-change {:zip-text ~zip-text})])

hueyp 20:26:01

that makes sense

seantempesta 20:26:57

Oh yeah, zip/change is much easier to read.

hueyp 20:27:19

yah usually it works out your mutations have a ‘thing’ to namespace them by anyways

hueyp 20:27:26

user/set-name, etc

seantempesta 20:27:44

Yeah, makes sense. Thanks @hueyp!

hueyp 20:54:26

anyone have much experience with using :query-root meta?

hueyp 20:54:33

with process-roots

bensu 21:18:36

I need a little conceptual guidance. The root component composes a query which populates the state for the whole app. If I want something to be requested from the served afterwards, conditional on a child component being mounted, where should I do it? (or what should I read?)

iwankaramazow 22:50:49

Probably in your parser

iwankaramazow 22:51:15

check out the item, how can I delay loads

bensu 22:54:21

@iwankaramazow: thanks, I read that but couldn't figure it out. Is :loading somehow a special value to

iwankaramazow 22:57:01

@bensu: :loading is the possible value v of the destructured 'response' from (find @state key) :smile:

iwankaramazow 22:57:18

not om specific

iwankaramazow 22:57:56

it just means you can conditionally delay loads based on your app-state

iwankaramazow 22:58:40

maybe you could put something in your state indicating that the component is mounted

iwankaramazow 22:59:11

for example :is-mounted

bensu 22:59:48

@iwankaramazow: I get it. Thanks! That is not what I'm going to do though, I'm going to use the same condition I use to decide if the component should be mounted in the first place