Fork me on GitHub
#om
<
2016-03-20
>
drcode01:03:33

Hi, debugging question: I have a situation where a component isn't updating after a mutation. Likely, I need to put in better "read queries" after the mutation to get it to update properly. However, it would be awesome if there was a way I could just tell OmNext "Just always update everything after a mutation" to basically turn off the "optimized updating" that the indexer performs- This would give me valuable debugging information. Is there any way to do this? Thanks in advance!

tomjack01:03:18

you could try just putting the root query in there?

tomjack01:03:43

also, I learned today that you can see the actual queries om will do with (om/transform-reads reconciler tx)

drcode01:03:00

@tomjack: Yes, I agree that should work, but I was hoping there was a way to just do it globally with a flag, because I have a sneaky suspicion I'm running into an OmNext bug and having a way to "turn off OmNext's optimization brain" directly might be a better way to track that down. @tomjack: Good point about transform-reads, I hadn't given that one a close look yet, probably would be wise for me to study it very closely.

tomjack01:03:41

I don't guess there is any way to turn off its brain. if om/touch! were implemented already (there is a todo issue for it), you could maybe call it yourself after a mutation

drcode01:03:54

@tomjack: Thanks for the info!

drcode01:03:38

@tomjack: LOL that's kinda what some of my code looks like right now, and I'm trying my darndest to stop doing that kind of thing by getting to the bottom of my refresh issues

drcode01:03:36

@tomjack: Good tips, good to see what other people are doing

iwankaramazow07:03:20

@thiagofm:

(defmethod mutate 'mutate/update
  [{:keys [ast state ]} key _]
  {:remote (assoc ast :params {:some-param (get @state :some-stuff)})})

petterik08:03:50

@danielstockton: I was also hitting an error when merging remote state with alpha-31 and datascript. To fix my error I had to set :migrate nil in my reconciler(s). I hope this works for you

thiagofm09:03:47

@iwankaramazow: for some reason, if I don't return {:remote true}, things doesn't work

thiagofm09:03:20

woops, it actually worked, yay!

thiagofm09:03:05

@iwankaramazow: I was trying to return {:remote ...} with very different values and none worked, but now that i've returned the ast, it does work. Why?

iwankaramazow09:03:41

@thiagofm: It's either {:remote true} or {:remote ast}

thiagofm09:03:36

@iwankaramazow: how do you know those things? 😛

iwankaramazow09:03:28

@thiagofm: I think I read in the tutorials/Tony.Kay's tutorial

iwankaramazow09:03:41

aside from that, blood, sweat & tears 😛

tawus10:03:31

I am also using datascript. I first tried state = conn but my transact used to fail with No queries exist. Then I used {:conn conn}. It is working well for me. May be that will help some one.

tawus10:03:46

I am also using custom merge. I use it to transact into datascript.

tawus10:03:02

I had also set :migrate nil but I don’t need to do that now. Don’t remember what changed that simple_smile

thiagofm11:03:20

@tawus: do you have any example of all this? I guess I'm still struggling my way out to do remotes using datascript

thiagofm11:03:14

Are IQueryParams meant to be static? In the sense I can't change whatever I've specified, like: static om/IQueryParams (params [this] {:language "ruby"}) So, query params will always be ruby. Is this useful? There could be the possibility to change the params to something else, or specify them by the caller of the component, is this possible?

iwankaramazow12:03:37

@thiagofm: you can update them through set-query! or update-query!

thiagofm12:03:22

@iwankaramazow: But I need the component to do that, so I can't do it inside render

thiagofm12:03:40

and only inside render I have the params that I want it to be

thiagofm12:03:36

I feels a bit weird, if I pass computed props to a component, it doesn't care about it in IQueryParams or IQuery, it's not even accessible from that point

thiagofm12:03:37

Component List passses down to Item the computed props of {:name "Item A"}

iwankaramazow12:03:34

I think computed props are meant for callbacks

thiagofm12:03:47

In Item, there's a IQueryParams (params [this] {:name "Item K"}) and IQuery with [:abc {:name ?name}]

thiagofm12:03:24

I just can't get what is the point that I have to use set-query

thiagofm12:03:41

Inside the render method of my component?

iwankaramazow12:03:05

just like you would transact a mutation

thiagofm12:03:08

If I do: (render [this] (let [github-repositories (:language (om/get-computed this))] (om/set-query! this {:query {:a 1}}) (dom/div nil github-repositories)))) I see nothing in the read of that component (I don't see {:a 1})

thiagofm12:03:46

Example, I'm printing everything in the read and I see no {:a 1}: (defmethod read :remote/github-repositories [{:keys [state query ast]} key params] (let [language (:language params) github-repositories (d/q '[:find ?e . :where [_ :remote/github-repositories ?e]] (d/db state) query)] (if (nil? github-repositories) (do (.log js/console "Github repositories read:") (.log js/console (pr-str github-repositories)) (.log js/console (pr-str query)) (.log js/console (pr-str params)) (.log js/console (pr-str state)) (.log js/console (pr-str key)) (.log js/console (pr-str ast)) {:remote (assoc ast :params {:language language})}) {:value github-repositories})))

iwankaramazow12:03:37

Yea but you change the query of that component to :a

iwankaramazow12:03:01

your parser will read :a

thiagofm12:03:44

So, I need to specify the whole query, like: https://gist.github.com/thiagofm/4db7ffce772b08167797 ?

thiagofm13:03:57

Whenever I do set-query! in my component, I start getting the following error: Uncaught #error {:message "No queries exist for component path (haxlife.components.window/Window haxlife.components.window/GameWindow haxlife.components.code/Code)", :data {:type :om.next/no-queries}}

tawus13:03:06

@thiagofm: Not sure my code is going to help simple_smile. Here is how I proceeded after struggling for some time. I had a small project I wanted to move to om.next. I don’t use remote reads. I use a “read mutation” to load data into the datascript db at load. I merge results from mutation into datascript using datascript transact. I use a simple pub/sub with mutation to have a callback after mutation in case local state needs modification (in case of errors in form). I have put all this code into om-hack.cljs to keep telling me that it might not be the right way to do it and as I learn more I have to fix them. It is working well for me. simple_smile If you still need the code of my om-hack.cljs I can send you that in a private message.

tawus13:03:54

@thiagofm: Are you using datascript conn as state ?

thiagofm13:03:47

@tawus Hmm, I'm always using transact and normal datalog queries, does it answer your question? 😛 I like your approach!

thiagofm13:03:05

@tawus: I just wonder why set-query! isn't working as expected... Can't see the correlation with this and datascript

thiagofm13:03:30

With a mutation I guess it would be easier, I can just mutate whenever I load the component and handle everything by myself

thiagofm13:03:10

Kinda regretting the fact I've picked datascript. I don't even need any fancy query. Everything I need is a key/value storage

thiagofm13:03:20

Maybe it's worth going back

tawus13:03:24

Here is my take. om.next uses state to store queries in :om.next/queries. It uses get-in state [..] to retrieve the data from state but for datascript conn, it returns a datom which causes nil and hence the error.

thiagofm13:03:26

Makes sense

tawus13:03:31

You can avoid that by using idents but then you have to overuse them and it is not pleasant simple_smile

tawus13:03:02

If you don’t need datascript you can always use default database.

thiagofm13:03:07

I expect to only have a single remote

tawus13:03:24

You can help om.next to normalize it by using idents.

tawus13:03:00

and @cjmurphy has a good library for debugging in case you make a mistake simple_smile

thiagofm13:03:57

I'll go with the mutation, not a bad idea

thiagofm13:03:30

It's just one, I can comment it well and refactor it later if datascript gets more adoption

thiagofm13:03:01

Thanks for the idea @tawus

tawus13:03:17

NP, it is not mine. I got it from a discussion here. It was @tony.kay’s idea simple_smile

thiagofm13:03:42

Fair enough, soon a new wiki page: "Datascript workarounds"

iwankaramazow14:03:40

@thiagofm: that error means the root query is somehow invalid, in 99% of cases this means you're 'stealing' a query from a component and things don't compose

haywood15:03:49

yea @iwankaramazow my issue yesterday was a root query that looked like [{:thing [:id] (om/get-query Legend)]

haywood15:03:05

where get-query was {:legend [:id]}

haywood15:03:19

it wasn't until I moved the {:legend} part to the root that it worked

haywood15:03:44

I guess it doesn't make sense to hide the key

haywood15:03:05

I kind of wish the data required by components was automatically passed

thiagofm15:03:42

So hard to wrap my head around async stuff in clojurescript, yet, so fun

iwankaramazow15:03:11

@haywood: Makes sense, because [{:thing [:id] {:legend [:id]}}] isn't valid. It's either [{:thing [:id {:legend [:id]}]}] or [{:legend [:id]} {:thing [:id]}]

haywood15:03:00

that was a typo

haywood15:03:12

the root component was [{:thing [:id]} (om/get-query Legend)]

haywood15:03:22

used to structural clojure 😛

haywood15:03:55

omg, I was only getting the error on re-render

haywood15:03:02

I wonder if it's because it was calling it in isolation

haywood15:03:16

it would error after a mutation

haywood15:03:44

my legend component didn't have a legal query by itself, because it was just (query [this] {:legend [:id]})

anmonteiro15:03:06

@haywood: you only notice these illegal query errors after mutations because they are currently only detected in full-query

anmonteiro15:03:43

I've been thinking about ways to add invariants that detect this sooner

haywood16:03:20

could you clarify what you said? "they are currently only detected in full-query"

haywood16:03:24

what do you mean

anmonteiro16:03:15

after you run a mutation, Om calls reconcile!

anmonteiro16:03:32

this in turn calls the component's full-query

anmonteiro16:03:40

in default-ui->props

haywood16:03:55

so my legend component's full-query?

anmonteiro16:03:09

the full-query is computed with the help of the indexer

anmonteiro16:03:41

if the query paths or data paths are not valid, there'll be an error

anmonteiro16:03:23

or rather, if there are no data paths between the components' queries

haywood16:03:24

ok, I'm going to test it out in the repl to understand what your saying better

anmonteiro16:03:53

[{:some/join [:some/key]}]

anmonteiro16:03:11

if you take this query, and [:some/key] is the query of a component, and its parent has [{:some/join (om/get-query OtherComponent)}]

anmonteiro16:03:53

there's a path between the parent and the OtherComponent

anmonteiro16:03:01

and the path is [:some/join]

haywood16:03:19

damn thank you for explaining that, I fixed the issue without understanding fully what was happening

anmonteiro16:03:05

now problems arise when you have something like [(om/get-query OtherComponent)] in the parent

anmonteiro16:03:27

in this case there's no path between the parent and the OtherComponent because you're stealing queries

anmonteiro16:03:41

this will just not work, and cause you problems down the road

anmonteiro16:03:10

alas, it's not a valid way to compose queries, because both parent and child end up with the same queries

haywood16:03:27

gooooooottttt it

iwankaramazow16:03:03

so what's the exact difference between get-query vs full-query? (relative vs absolute)

iwankaramazow16:03:08

Full-query uses the indexer?

anmonteiro16:03:28

@iwankaramazow: using a different example:

(defui Child
  static om/IQuery
  (query [this]
    [:foo :bar]))

(defui Parent
  static om/IQuery
  (query [this]
    [{:child/one (om/get-query Child)} {:child/two (om/get-query Child)}]))

(defui Root
  static om/IQuery
  (query [this]
    [{:root (om/get-query Parent)]))

anmonteiro16:03:57

we can agree that the root query is: [{:root [{:child/one [:foo :bar]} {:child/two [:foo :bar]}]}]

anmonteiro16:03:30

however, if you (get-query Child), you'll get [:foo :bar]

anmonteiro16:03:50

if you instead get the full-query of an instance of Child

anmonteiro16:03:21

you'll get [{:root [{:child/one [:foo :bar]}]}] if it's in the :child/one path and [{:root [{:child/two [:foo :bar]}]}] if it's in the :child/two path

anmonteiro16:03:30

that's the absolute query

anmonteiro16:03:36

which is also focused along the given path

iwankaramazow16:03:55

Ah totally make sense

thiagofm16:03:12

I give up trying to use datascript 😛 Going to rewrite everything in the normal way

tony.kay17:03:48

@adamkowalski: Om (next) is alpha, has few add-ons at the moment, and does not try to solve routing issues. This is a topic that comes up a lot. There are various examples of people doing routing, but the basic answer is: you do it yourself.

tony.kay17:03:25

I’m not remembering what links there are to solutions, but you might search the web for examples.

anmonteiro17:03:23

@adamkowalski: FWIW, I've written about different ways to implement routing in Om Next http://anmonteiro.com/2016/02/routing-in-om-next-a-catalog-of-approaches/

adamkowalski18:03:08

Thanks, those sound like solid ideas. I do think that one thing the om community really needs is a https://js.coach/react equivalent

adamkowalski18:03:28

that way we could create a catalog of reusable components that could help standardize the different approaches people take to different problems.

michaelr18:03:44

why could it be that the read parser function in om.next is not being called?

anmonteiro18:03:22

@michaelr: that's too generic a question

anmonteiro18:03:33

could you give more context?

michaelr18:03:09

sure, it's a react native app.. i created the reconciler with the parser entry, also defined some components with queries. the component is rendered.. but i don't see any of the debug prints in the parser read function being called

michaelr18:03:45

maybe it has something to do with how navigation is handled..

michaelr18:03:57

the om component component is just called here, maybe it doesn't trigger the query?

anmonteiro18:03:18

@michaelr: well your parser is not called because the root component doesn't have a query

michaelr18:03:11

interesting.. is this documented somewhere? i think i missed it while reading om.next docs today

anmonteiro18:03:54

@michaelr: not sure where it's documented

anmonteiro18:03:59

but queries must compose to the root

michaelr18:03:14

what does that mean?

anmonteiro18:03:59

that a query in a component down the UI tree won't get picked up

anmonteiro18:03:09

the root component must have knowledge of it

michaelr18:03:18

i'm still not sure what exactly this means.. will try to find some docs

anmonteiro18:03:56

@michaelr: if you have a query [:foo] in e.g. a Child component

anmonteiro18:03:34

you must compose it to the root with e.g. [{:child (om/get-query Child)}]

anmonteiro19:03:17

If you're new to Om Next, I encourage you to go through the tutorial on the wiki

iwankaramazow19:03:46

Think of composing queries as a graph where every node somehow connects to your root component

michaelr19:03:16

yeah, i think i get it now. this example did it: [{:child (om/get-query Child)}]

michaelr19:03:42

anmonteiro: i went through the tutorial today, don't think it was mentioned there

iwankaramazow19:03:00

the om/get-query part gives you the query composition

anmonteiro19:03:23

I think it's more comprehensive and beginner-friendly

michaelr19:03:28

ah another tutorial simple_smile

michaelr19:03:32

cool thanks

michaelr19:03:04

so in practice i should always compose the child component's queries in the parent up to the root component

iwankaramazow19:03:45

one important part is the fact that you can't 'steal' queries from a child-component

michaelr19:03:14

what does this mean?

iwankaramazow19:03:14

(defui Child
  static om/IQuery
  (query [_]
         [:some/property]))
 

(defui Parent
  static om/IQuery
  (om/get-query Child))

iwankaramazow19:03:54

Parent steals the query from Child, this will result in some problems down the road

iwankaramazow19:03:43

(defui Parent
  static om/IQuery
  [{:some/join (om/get-query Child)}]) 
This is the right solution to composing the queries

iwankaramazow19:03:05

Just remember this one, gave me a lot of problems before I understood this 😄

michaelr19:03:27

hmm.. but in which case would a parent want to "steal" a child's query?

michaelr19:03:45

if it doesn't have a query of it's own?

iwankaramazow19:03:15

You might think, oh this component just needs the exact same data as its child, I'm just going to take that query

iwankaramazow19:03:35

that's a red flag

iwankaramazow19:03:19

Either the child doesn't need a query -> pure function

iwankaramazow19:03:38

or you compose the child's query in a join [{:some/join (om/get-query Child)}]

michaelr19:03:07

anmonteiro: thanks also for the awkay tutorial - it's really much better

michaelr19:03:40

iwankaramazow: how should i compose if my root component doesn't need queries? same join way?

adamkowalski19:03:25

your root component query should be the aggregate query of all of its child components

adamkowalski19:03:24

look at the rootview component

michaelr21:03:56

my root component is just a container for a single child component at a time, and this child component changes (depending on which screen the user wants to see). i wonder whether i should dynamically change the root component's query to only aggregate the current's child query?

michaelr21:03:45

does it make sense?

symfrog21:03:29

I am seeing the mutate function being called multiple times (twice) on a single call to om.next/transact!, is this expected? I would have expected only a single call to the mutate function when om.next/transact! is invoked.

anmonteiro22:03:52

@symfrog: the parser is called once with target set to nil and once for each remote target

symfrog22:03:47

@anmonteiro: Thanks, I was hoping to communicate parameter validation errors back to the component that initiated the mutation by putting/taking vals in a shared async channel. Since the mutation function could be called multiple times, it would end up putting multiple values on the async channel for the same validation error. Is there a more idiomatic way of communicating errors back to a component that initiated a mutation in om next? I was also going to use the async channel for communicating errors in the send and merge functions back to the component during mutations.