Clojurians
# om

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

seantempesta 23:03:33

Ugh. This is all feeling too hacky. I’m going to take a break and then I’ll just try implementing my own router as an om.next component. Thanks again guys for helping me work through those bugs!

threepointone 02:10: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!

ethangracer 02:12:28

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

ethangracer 02:13:07

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

reedho 02:26:09

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

reedho 02:27:07

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

reedho 02:29:28

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

threepointone 02:31:06

thank you very much! diving in

reedho 02:37:10

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

ethangracer 02:38:05

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

ethangracer 02:38:47

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

ethangracer 02:39:38

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

reedho 02:40:40

Is the latter valid? I guesa not

ethangracer 02:40:55

the latter is valid, yes

hueyp 02:41:01

the latter is valid, former is not

ethangracer 02:41:01

you can parameterize a join

ethangracer 02:41:13

@hueyp do you know why?

hueyp 02:41:24

what does :id :name mean as a key?

ethangracer 02:41:46

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

ethangracer 02:41:53

and send the same parameter to both

ethangracer 02:42:08

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

hueyp 02:43: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

hueyp 02:43:49

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

ethangracer 02:45: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

hueyp 02:46:01

yah its cool, it could make sense with context :simple_smile:

ethangracer 02:46:31

haha, generous of you to give me the benefit of the doubt :grimacing:

hueyp 02:46:34

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

hueyp 02:46:54

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

reedho 02:47:55

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

reedho 02:48:00

Thats why it just work :grinning:

ethangracer 02:48:11

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

george.w.singer 07:02: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.singer 07: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} ...)

cjmurphy 07:32: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.

cjmurphy 07:32:50

myHandler would take this/component as first argument.

george.w.singer 07:33: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.singer 07:34:03

@cjmurphy: no it's not an arity issue

george.w.singer 07:34: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.singer 07:34:42

I probably didn't ask it very clearly

george.w.singer 07:35: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.

cjmurphy 07:36:54

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

cjmurphy 07:39:26

Can't see it here

george.w.singer 07:40: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.singer 07:47: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.

seanirby 07:49:44

i didn't know about js* either, thats a helpful one

reedho 10:15:58

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

anmonteiro 13:03:52

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

george.w.singer 13:28:59

@anmonteiro: no, it does not.

george.w.singer 13:29:35

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

george.w.singer 13:29:42

neither work

george.w.singer 13:29:51

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

anmonteiro 14:02:56

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

anmonteiro 14:25: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.singer 14:31:57

Yes that does work

anmonteiro 14:32:28

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

george.w.singer 14:33:07

will post my own snippet of code

george.w.singer 14:40: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.

anmonteiro 14:40:57

You probably weren't using the . syntax

george.w.singer 14:41:06

It had to have been something like that

anmonteiro 14:41:49

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

george.w.singer 14:43:28

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

seanirby 14:47:10

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

seanirby 14:48:44

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

george.w.singer 14:50:00

True, you're avoiding homoiconicity by using js*

seanirby 14:51:20

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

george.w.singer 18:10:15

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

george.w.singer 18:10:49

Will those mutations happen in order on all remotes?

iwankaramazow 18:27:55

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

iwankaramazow 18:28:01

correct me if I'm wrong

matthavener 22:30:53

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

tomjack 22:39:29

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

tomjack 22:43:03

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

matthavener 22:47:07

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

matthavener 22:47:31

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

george.w.singer 22:48: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.singer 22:49:00

Lots of channels -- which is fine

george.w.singer 22:49:21

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

george.w.singer 22:49:43

At least with respect to call order

george.w.singer 22:50:10

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

george.w.singer 22:50:19

It becomes a map with tons of channels

george.w.singer 22:50:27

Which you can't send over the wire

george.w.singer 22:50:52

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