Fork me on GitHub
#om
<
2016-03-05
>
threepointone00:03:55

Hello all. I've been reading the source trying to understand what's going on, and I'm focussing on the parser right now. I don't understand where join get resolved after the initial key match. From the looks of it, parser maps through the root query expressions, does a read on each, and returns a map with results https://github.com/omcljs/om/blob/master/src/main/om/next/impl/parser.cljc#L186-L202 where do the join resolutions happen? please, and many thanks!

ethangracer00:03:28

anyone know if it’s possible to parameterize a top-level read with multiple props? e.g. [(:id :type {:some :params})]

ethangracer00:03:07

not particularly looking to use a join in this case, otherwise I would

reedho00:03:09

@threepointone afaik, parser dont recurse automatically and wont resolve join, union, etc..

reedho00:03:07

Its our task to implement it, normally by recursively call the parser

reedho00:03:28

However, there are db->tree utility that does that, usually we use this in our parser

threepointone00:03:06

thank you very much! diving in

reedho00:03:10

@ethangracer do you mean something like [(:id {:type ?type :some ?params})]

ethangracer00:03:05

@reedho not quite, more like [(:id :name {:type ?type :some ?params})]

ethangracer00:03:47

not sure if it’s possible, my tests are failing when using om/ast->expr hoping to produce something like this

ethangracer00:03:38

an alternative, using a join, might look like [({:my-data [:id :name]} {:type ?type :some ?params})]

reedho00:03:40

Is the latter valid? I guesa not

ethangracer00:03:55

the latter is valid, yes

hueyp00:03:01

the latter is valid, former is not

ethangracer00:03:01

you can parameterize a join

ethangracer00:03:13

@hueyp do you know why?

hueyp00:03:24

what does :id :name mean as a key?

ethangracer00:03:46

the idea is I want to be able to read two different keys

ethangracer00:03:53

and send the same parameter to both

ethangracer00:03:08

it would be nice not to parameterize each read with the same data

hueyp00:03:13

I don’t know … it makes sense to me that you’d parameterize the root and then just join keys that you want … parameterizing each individual key with the same params … without context that doesn’t make sense to me

hueyp00:03:49

({:user [:id :name]} {:foo :bar})

ethangracer00:03:41

yeah, it’s a particular use case I’m running into with my app that could very well be a less than ideal design decision on my part… hard to explain or provide the code that would clarify. essentially I have no use for the join keyword, so just want to get rid of it

hueyp00:03:01

yah its cool, it could make sense with context simple_smile

ethangracer00:03:31

haha, generous of you to give me the benefit of the doubt 😬

hueyp00:03:34

it might work for your case to just make a helper do it … the query is a fn

hueyp00:03:54

but you just need to be careful if you are getting child queries that you don’t trample their metadata

reedho00:03:55

Yes, the former does not make sense logically, but the latter is ...

reedho00:03:00

Thats why it just work 😀

ethangracer00:03:11

interesting, i’ll give that a shot. i should probably just use a join but tinkering is too much fun

george.w.singer05:03:57

How do you pass a component's instance method to an event handler in Om Next? In pure JS, you can use the this construct:

<form onSubmit {this.handlerFunction}>`
So far I have something like:
(dom/form #js {:onSubmit (fn [e] (.preventDefault e) (.log js/console "I was just submitted."))}

...which uses an anonymous function. However, how can I do something like: (defui Component Object (myHandler [e] (.preventDefault e) (.log js/console "I was just submitted.")) (render [this] (dom/form #js {:onSubmit myHandler} ...) without the this construct that is only available (AFAIK) in pure JS?

george.w.singer05:03:25

Whoops, that last code snippet should read:

(defui Component
 Object
 (myHandler [e] (.preventDefault e) (.log js/console "I was just submitted."))
 (render [this]
   (dom/form #js {:onSubmit myHandler} ...)

cjmurphy05:03:22

Are you saying what is given to :onSubmit needs to be a particular varity, yet you need myHandler to take two arguments? You could use a partial function of myHandler to do this.

cjmurphy05:03:50

myHandler would take this/component as first argument.

george.w.singer05:03:33

I just discovered you can use the (js* ...) macro:

(defui MyComponent
  Object
  (handleSubmit [this e] (.preventDefault e) (.log js/console "handleSubmit."))
  (render [this]
     (dom/form #js {:onSubmit (js* "this.handleSubmit")} ...)))

george.w.singer05:03:03

@cjmurphy: no it's not an arity issue

george.w.singer05:03:33

I was just trying to figure out how to pass a component's instance method into the props of one of its children

george.w.singer05:03:42

I probably didn't ask it very clearly

george.w.singer05:03:33

So I guess you can use the (js* ...) macro to use the this keyword within clojurescript. That solves the problem fine, although I suspect this is not the idiomatic way to do this.

cjmurphy05:03:54

Is there a reference for that js* - never heard of it before.

george.w.singer05:03:43

It feels like a hack to use js*, but I actually think it makes interop code more readable to write it in its native syntax.

george.w.singer05:03:00

Come to think of it: it's probably not idiomatic to define a handler function within a component class method to begin with. It would probably be better to just make a (defn handle-function ...) OUTSIDE of the defui macro, and then just call it by its usual name when passing it as a prop.

seanirby05:03:44

i didn't know about

js*
either, thats a helpful one

reedho08:03:58

@george.w.singer it seems this-as is what you need for it

anmonteiro11:03:52

@george.w.singer: doesn't #(.handleSubmit this %) work for you?!

george.w.singer11:03:35

I also tried the de-macroed (fn [arg] (.handleSubmit this arg)) for good measure

george.w.singer11:03:51

Yet (js* "this.handleSubmit") does work.

anmonteiro12:03:56

@george.w.singer: doesn't make any sense, I'm pretty sure I've gotten away with doing that before

anmonteiro12:03:25

@george.w.singer: just confirmed this works:

(defui MyComponent
  Object
  (handleSubmit [this e]
    (println "handling submit")
    (.preventDefault e))
  (render [this]
    (dom/div nil
      (dom/button #js {:onClick #(.handleSubmit this %)} "Click me"))))

(def reconciler (om/reconciler {:state {}}))

(om/add-root! reconciler MyComponent
  (gdom/getElement "app"))

george.w.singer12:03:57

Yes that does work

anmonteiro12:03:28

it has to... You're defining methods on the JS object prototype, so iterop must work

george.w.singer12:03:07

will post my own snippet of code

george.w.singer12:03:28

I stand corrected. I'm not sure what was going on before, but it was only working with the (js* ...) macro. I just tried your method in 3 seperate places in my code and it worked.

anmonteiro12:03:57

You probably weren't using the . syntax

george.w.singer12:03:06

It had to have been something like that

anmonteiro12:03:49

using the js* macro is discouraged, as you might expect

george.w.singer12:03:28

Out of curiousity, are there any reasons other than aesthetic ones to avoid js*?

seanirby12:03:10

george.w.singer: i suppose that makes it a bit more difficult if you want to modify your source programmaticaly

seanirby12:03:44

if your code is in a list then its much easier to modify compared to a string

george.w.singer12:03:00

True, you're avoiding homoiconicity by using js*

seanirby12:03:20

thats atypical though, i can't think of any other reason

george.w.singer16:03:15

Do om next mutations occur in order? I.e.: [(first-mutate-one) (and-then-mutate-two {:param "val"})] ?

george.w.singer16:03:49

Will those mutations happen in order on all remotes?

iwankaramazow16:03:55

from reading Om's source that seems to be the case

iwankaramazow16:03:01

correct me if I'm wrong

matthavener20:03:53

george.w.singer: from my reading of the parser source, yes, i don’t know if that is spec’d though

tomjack20:03:29

it looks like the action fns are all called in a reduce. how do remote mutations work?

tomjack20:03:03

if you start some async process in the action fns, they'll all get started at the same time, before any finish?

matthavener20:03:07

i’d say any async stuff should be done by a sender processing a mutation query expression

matthavener20:03:31

so coordinating that ordering would be done by the send function, which could get hairy (i think)

george.w.singer20:03:37

I'm having a lot of issues with race conditions on remote mutate functions: Basically, on the backend remote, there is a mutate function which executes an asyncronous change; but the reader gather their values before the asyncronous mutation is finished.

george.w.singer20:03:00

Lots of channels -- which is fine

george.w.singer20:03:21

The problem is that the parser function hides to the user how it is executing reads/mutates

george.w.singer20:03:43

At least with respect to call order

george.w.singer20:03:10

The other big issue is that when the parser is finally finished making its return map

george.w.singer20:03:19

It becomes a map with tons of channels

george.w.singer20:03:27

Which you can't send over the wire

george.w.singer20:03:52

So you have to go through and get the values out of the channels before you send it back to the client

tomjack21:03:10

matthavener: thanks. I found that om-next-demo has a remote mutation example in https://github.com/swannodette/om-next-demo/blob/master/todomvc/

matthavener21:03:30

yep, check out awkay’s excellent tutorial as well for some examples https://awkay.github.io/om-tutorial/#!/om_tutorial.H_Remote_Mutation

matthavener21:03:51

senders are also covered in the om.next wiki