Clojurians
# om

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

dnolen 00:01:10

people opening issues

dnolen 00:01:18

do not link to external repos

dnolen 00:01:24

I will not look at them

dnolen 00:01:36

in future if you do this I will simply close the issue w/o further comment.

dnolen 00:10:59

changes around error handling means I'm killing off the possibility of ever passing nil props

dnolen 00:11:22

I don’t see any value in supporting such a thing and it’s a simple way to detect that an error has occurred

dnolen 00:11:50

that is … you should never pass nil but we may internally pass nil for internal error handling reasons

glv 00:16:21

@dnolen: Thanks for setting me straight on that. Missed that part of the Contributing doc.

dnolen 00:16:42

@glv no problem I need to be more proactive in sending people there

dnolen 00:16:52

also people in this channel here and elsewhere should do the same

dnolen 00:18:17

issues need to be very high signal / noise or I just will not look at them

glv 00:18:50

Makes complete sense

dnolen 00:19:54

@glv I’m pretty sure your issue is due to the path marking traversal in om.next.parser

dnolen 00:20:33

om.next.parser/path-meta

glv 00:21:05

That’s what the Chrome profiler was pointing me to, but I haven’t understood that code well enough yet to see the issue clearly.

rhansen 00:25:18

@dnolen: Not sure if you saw this question earlier or not, but is set-state! and update-state! supposed to trigger a query when they are run? They schedule the component for re-rendering, which makes sense, but I had this impression that the component query is only run on mount and on transactions :confused:

dnolen 00:25:22

information added to the ticket

dnolen 00:25:57

@glv not sure when I will get to it since it’s a finesse thing at this point - higher priority architectural stuff needs sorting out first.

dnolen 00:26:16

happy to take a patch of course - the idea would be to use the query to figure out what values to ignore

glv 00:27:08

No worries … for my actual project, I can use the atom-wrapping trick as a workaround for now, and I’ll try to find time to dig into the code and figure out how to patch it. I’m a bit intimidated by that code, though. :simple_smile:

dnolen 00:27:37

@rhansen some advice as well as any other newcomers to this channel

dnolen 00:27:51

do not direct questions at me if your problem would clearly be a stow shopper for actual usage

dnolen 00:28:23

assume you are making some simple mistake that other people can answer if clearly the problem would prevent everyone from proceeding

glv 00:28:45

Mind if I add a note to that effect to the channel topic?

dnolen 00:28:59

@glv sure

dnolen 00:29:35

@rhansen: also at this early stage referring to source is a really, really good idea

dnolen 00:30:35

all calls to set-state! and update-state! must go through this

dnolen 00:31:03

all re-render must go through this

dnolen 00:31:33

@rhansen: the key thing to understand is ui->props

dnolen 00:32:12

it should be self-evident there is no such thing as calling set-state! and not re-running queries

dnolen 00:32:34

read the source

dnolen 00:32:41

if your expectations of the source are not met

dnolen 00:32:46

then we have a bug we can talk about

dnolen 00:33:53

at some point in the future this will be less necessary but we are not there yet :simple_smile:

dnolen 00:34:13

if you’re jumping on board a certain level of engagement is going to be required to both advance your understanding

dnolen 00:34:16

and the quality of the project.

johannjohann 00:34:18

source is pretty interesting to read also!

dnolen 00:36:54

also I’m actually very happy to explain how the source works

dnolen 00:37:05

less happy to answer the same questions over and over again

dnolen 00:37:08

“learn to fish” etc.

dnolen 00:42:12

@glv yeah atom wrapping trick works for the time being - will definitely get to it post error handling / routing hooks work

johannjohann 00:50:45

@dnolen i wonder if theres a slack integration that would allow you to tag the repetitive questions and build an adhoc wiki--did a little googling and found this. might be useful. https://slack.com/apps/A0FHVR2R0-guru

dnolen 00:54:53

@johannjohann: hrm maybe, the problem is not precisely getting the same questions over and over again

dnolen 00:55:31

rather newcomers who are just starting out who have questions about anything working at all

dnolen 00:56:01

in the longer term hopefully this will get addressed by a cookbook / structured guide

dnolen 00:56:34

encouraging the former and kick starting the later are definitely on my todo list for 2016

selfsame 00:56:39

have to admit the learning curve is rough

dnolen 00:56:57

no disagreement there - it’s very steep

dnolen 00:57:13

thus the need for something more structured & hand holding

dnolen 00:57:23

from the basic stuff to fully working non-trivial thing

johannjohann 00:59:53

yep. its principled, and for good reasons, but i think its somewhat easy to want to fall into old habits of building up stuff--you get maybe 40% there without really taking in the principles, get confused etc

rhansen 01:01:13

@dnolen: I should’ve read the source first, but in this case I probably would’ve had the same question. After some brief hammoc time I’m chucking this down to me not understanding the differences between Om and React and will make changes accordingly.

dnolen 01:01:58

@rhansen: it’s also easier to understand misconceptions with some minimal example

dnolen 01:02:05

"I expect this example X to work"

rhansen 01:02:26

I was using component local state to store the contents of several input fields in a form component. But that would be bad in the case of remote queries in the tree.

dnolen 01:02:45

rather than vague "does concept Y in Om Next work at all?"

dnolen 01:02:59

the later gives no guidance for anybody to understand what you are going through

selfsame 01:04:32

the awkay devcard tutorial is great for that

rhansen 01:04:56

@dnolen: No argument there, and it should be self-explanitory. I will do better.

dnolen 01:06:15

@rhansen: as a rule and really it applies to everyone - when you encounter a problem take the 5-10 minutes to make some smaller case of something that fails your expectations

dnolen 01:06:28

this will eliminate tons of lost time of people trying to read each other minds :simple_smile:

dnolen 01:07:03

usually with an example many more people can chip in and identify the problem

dnolen 01:07:07

and usually very quickly

tony.kay 02:50:42

@dnolen: When you get a minute, I'd like to know which internal thing is correct, and I'll try to fix the Om bug I reported. Should components that use a top-link like [:a _] be indexed (when you build the index from root) by the ident, or just by the :a keyword?

dnolen 02:51:05

just :a keyword

tony.kay 02:51:15

k, I think that is the problem. I'm seeing the ident link in the indexer

tony.kay 02:52:02

thanks, I'll take a look

olivergeorge 06:46:05

Wow, you can make a pretty powerful db remote using korma...

olivergeorge 06:46:59

That covers picking fields, limit, offset, ordering and basic where clauses!

olivergeorge 07:57:15

Hmm, I'm fallen quite flat trying to hook up a reader for my paramatised query. db->tree doesn't seem to like params. It's possible I've been misusing it.

dnolen 11:59:14

@olivergeorge: db->tree does not understand parameterized queries - there’s no intention to change that. query parameters should no longer be present by the time you invoke db->tree. They will either not be applied at all or should be applied on the result.

dnolen 11:59:54

people that want fancier recursive querying will need to experiment on their own - this is what the Query AST is for

olivergeorge 13:34:47

@dnolen thanks

janherich 13:35:18

Hi everybody ! I would like to ask one question about merging new state deltas into app-state. The format of the state delta which default-merge (https://github.com/omcljs/om/blob/master/src/main/om/next.cljs#L1348, argument named res) expects seems to be little bit odd: {some/mutation {:tempids {[:key-path tempid] [:key-path real-id]}}} is the correct form for the default-merge to resolve tempids. But it doesn't pick state delta when it's nested in {some/mutation {:result some-state-delta}} - so it's not possible to directly feed external (server) mutation parse result to merge! function. It's possible only when we are parsing read expression or mutation which results in tempids... It's not particularly difficult to handle, but it seems little bit strange to me, am i missing something ?

olivergeorge 13:35:58

For what it's worth. Not being applied would work fine for me (the params are applied server side, the client just needs to access the data). At the moment I think the presence of params means it returns nothing.

dnolen 13:36:55

@olivergeorge: that's worth pondering. I thought about that too.

juhoteperi 13:44:08

@dnolen: Btw, CONTRIBUTING.md (https://github.com/blog/1184-contributing-guidelines) might help bringing the contributing rules to everyone's attention.

olivergeorge 14:22:19

@dnolen I gave that a go. Stripping out was easy. Seems helpful to me.

olivergeorge 14:24:52

The next thing which caught me was handling a vector of maps. I expected my query [{:friends [:name]}] to handle data in this format {:friends [{:name "anna"}{:name "bob"}]} but there's no checking to differentiate between the value of :friends being a map and a vector.

olivergeorge 14:25:33

That's not much harder to add.

olivergeorge 14:27:37

(slightly fiddly actually since we're accumulating idents-seen along the way)

olivergeorge 14:29:55

I'll log tickets in case I'm on track and it proves a helpful reminder later.

dnolen 14:36:16

@olivergeorge: the later is just a bug if true but I'm skeptical that's a showstopper bug. Do not report an issue without a minimal case.

dnolen 14:37:14

@juhoteperi: yeah I'll probably copy the wiki there

olivergeorge 14:38:05

@dnolen righto, thanks.

olivergeorge 14:45:03

This is my minimal case:
(let [query [{:friends [:name]}] data {:friends [{:name "anna"}{:name "bob"}]} actual (om.next/db->tree query data data)] (if (= data actual) :good [:bad {:expected data :actual actual}]))

olivergeorge 14:45:37

I get [:bad {:expected {:friends [{:name "anna"} {:name "bob"}]}, :actual {:friends [{} {}]}}]

dnolen 14:50:40

@olivergeorge: that's not how db->tree works

dnolen 14:50:49

Your db isn't normalized

olivergeorge 14:51:23

Also I think that code has had changes since alpha28

olivergeorge 14:51:34

I'll look again

anmonteiro 15:07:52

``` (def state {:friends [[:friend/by-name "anna"] [:friend/by-name "bob"]] :friend/by-name {"anna" {:name "anna"} "bob" {:name "bob"}}})

(om/db->tree [{:friends [:name]}] state state) ``` @olivergeorge: FWIW this works for me ^^

anmonteiro 15:08:11

you need to use normalized data in db->tree

olivergeorge 15:11:34

Thanks @anmonteiro. I just got there myself.

olivergeorge 15:12:41

So requiring normalisation means I will need to define a client side schema using defui in order to use db->tree

anmonteiro 15:13:54

@olivergeorge: exactly

anmonteiro 15:14:15

in your example it would be something like:
``` (defui Friend static om/Ident (ident [this {:keys [name]}] [:friend/by-name name]) static om/IQuery (query [this] [:name]))

(defui Friends static om/IQuery (query [this] [{:friends (om/get-query Friend)}])) ```

olivergeorge 15:14:40

Yeah, that's pretty much in my editor just now!

anmonteiro 15:15:38

:simple_smile:

olivergeorge 15:15:47

I was hoping not to have to do that. Seems like a lot of code. I wonder if denormalised could be supported or if it would get in the way.

anmonteiro 15:16:31

@olivergeorge: you can use denormalized data if you like, nothing prevents you from doing that

anmonteiro 15:16:43

just don't call db->tree then

anmonteiro 15:16:56

deref state and access it directly

olivergeorge 15:16:58

It's 95% of what I want but easy to copy/past and modify.

olivergeorge 15:17:24

(really the recursive select-keys is what I want I guess... I'll go away and play)

olivergeorge 15:17:30

Thanks for confirming for me.

akiel 17:48:18

I have a component which should render one particular entity. Should I use a JoinExpr with IdentExpr for the query like: '[{[:foos/by-id ?id] [:name]}]?

adammiller 18:02:45

Hard to say without knowing more but seems like you’d always want a component that just has the query for the attributes for which it should display. Then a parent component would do the job of selecting and providing a specific one. Is that not possible in your case?

akiel 18:09:05

In my case I like to show only one particular entity on a page. The URI would be /foos/123.

akiel 18:09:28

In contrast to the page which shows a list of all foos where the URI would be /foos.

akiel 18:09:53

Now I have routing which selects one component to render.

akiel 18:10:24

In the list case, I have a list component which than uses a list item component as usual.

akiel 18:10:40

But in the single entity case - what should I do?

anmonteiro 18:12:43

@akiel: from your description, the query looks okay

anmonteiro 18:12:56

@akiel: hard to say more without looking at some actual code

jonsmock 18:13:55

@akiel I’ve been playing with this too, and that’s the query I came up with (still in progress, though). The only alternative I could see is to use params

jonsmock 18:14:25

Note the result of the query will then be something like {[:foo/by-id 1] {:name “Jon”}}

jonsmock 18:14:47

(I chose 1 for ?id)

akiel 18:15:22

@jonsmock Yes I will also try params because I get the data back keys under [:foos/by-id 123] which is hard to unwrap.

jonsmock 18:15:30

Exactly

akiel 18:15:39

oh yes :simple_smile:

jonsmock 18:17:35

A slight tweak I can think of is to have the query param as the full [:foo/by-id 1] ref

jonsmock 18:17:58

So ‘[{?foo-ref [:name]}]instead of '[{[:foos/by-id ?id] [:name]}]

jonsmock 18:18:25

Which might make it slightly more straightforward to unwrap

jonsmock 18:19:36

(let [foo-ref (:foo-ref (om.next/get-params this))] ….)

jonsmock 18:19:43

Is sort of what I’m thinking

akiel 18:21:05

I have now ’[({:foo [:name]} {:id ?id})] and (defmethod read :foo [{:keys [state query]} _ {:keys [id]}] (let [st @state] {:value (select-keys (get-in st [:foo/by-id id]) query)}))

jonsmock 18:22:14

I’m pretty new still, but both seem reasonable to me. I don’t know what the trade-off is exactly.

akiel 18:23:20

I’m also new. I saw the IdentExpr in JoinExpr and thought that it would be useful in my situation.

jonsmock 18:23:37

Personally I’m trying to make the ref way work, but I think the params way is good for search results lists and things like that

akiel 18:25:30

I think going by the :foo key for a particular foo and using :foos for the list of all foos sounds reasonable.

edpaget 18:26:56

I ended up using another different way to accomplish a similar thing. I created a function and returned and anonymous class and passed the id I wanted to render

edpaget 18:27:11

```(defn page* [{:keys [id]}] (ui static om/IQuery (query [this] [{[:items id] query}]) Object (render [this] (something))))

(def page (memoize page*)) ```

edpaget 18:27:41

since I couldn't figure out a non ast manipulating way to pass the id down from my router

edpaget 18:28:44

but i'm also new to om.next and don't know if this is a bad way to handle it

jonsmock 18:30:13

Thoughts: 1. Seems like your using the ref query way, which is what @akiel was asking about 2. You’re creating a page for each item - how many items do you have?

jonsmock 18:30:39

Not saying that’s bad - I didn’t even think of that way

jonsmock 18:30:48

Just seems like a space leak

edpaget 18:32:26

I'm not sure i haven't run into problems with it so, far other than discovering i had to memoize the result so om could figure out the query-path to the component

akiel 18:33:06

@edpaget: I read the id directly from the location:static om/IQueryParams (params [_] (:route-params (r/match-route (u/location-path))))

edpaget 18:33:13

and this is simplified version of what i'm actually doing, there's just one specific thing in my om app that has pages like that

edpaget 18:37:49

@akiel: i hadn't thought about doing it that way at all, nice. ! I was just trying to keep all the routing logic in my top-level component

a.espolov 18:54:23

@dnolen: David I have to you a couple of query and parameters
https://gist.github.com/dark4eg/d7dc2364606fae3fb435

  1. In the example described in gists, use get-query. In this case, I cannot set-query! with the new settings, so in reality have to simply duplicate the IQuery in components A and B
  2. I have the IQuery component A to specify all the keys for remote synchronization and it turns out that when your application runs, stretch all the keys that say to me on the first page of the application does not need a list of employees.

Is there a point above or I still have to live with that?)

dnolen 18:55:55

@a.espolov ask questions to the channel please, thanks

a.espolov 18:56:29

@dnolen: this is channel)

anmonteiro 18:57:24

@a.espolov: David meant don't direct the question towards him but to the channel; see the channel topic

a.espolov 18:58:36

I already asked but got no answer

anmonteiro 18:59:01

I'm happy to help but I don't understand what you are asking

anmonteiro 18:59:14

could you please rephrase?

anmonteiro 19:00:15

@a.espolov: also, is Insps in component A meant to be B ?

a.espolov 19:00:41

sorry, now I'll fix

a.espolov 19:01:36

Insps is B

dnolen 19:01:37

@a.espolov: if no one can answer you will just have to be patient - such is life :simple_smile:

a.espolov 19:01:38

gist updated

dnolen 19:02:00

but really do not ping me unless it’s about bugs

dnolen 19:02:08

I’ll be more available post-Beta

a.espolov 19:02:42

@dnolen: thanks, ok)

smeister 21:38:53

A question concerning tempids... (defmethod mutate 'company/add-user [{:keys [state ast]} _ params] (let [tempid (om/tempid) user (assoc params :db/id tempid) local (assoc user :user/confirmed true)] {:value {:keys [:users]} :remote (assoc ast :params user) :action (fn [] (swap! state update-in [:by-id] assoc tempid local) (swap! state update-in [:users] conj [:by-id tempid]))})) I need to know the tempid outside of the action to send it to the server with the same mutation, yet the above will not work, as it will generate multiple tempids. Any ideas on how to do this cleanly?

dnolen 21:41:00

@smeister: :result of :action will be put into :value

dnolen 21:41:05

:result can be whatever you want

dnolen 21:41:16

mutations are top level so post processing is trivial

smeister 21:42:26

Hm ok so i need a custom parse to achieve this? I mean generally i do not use the :result of client mutations.

dnolen 21:43:49

you do not

dnolen 21:43:52

re-read what I said :simple_smile:

dnolen 21:44:08

(I am assuming this is server-side)

smeister 21:44:14

Its client side

dnolen 21:44:18

if it isn’t you haven’t clarified

smeister 21:44:19

Sorry for being unclear

dnolen 21:44:57

I do not know why you need to know the tempid outside

smeister 21:45:13

I generate the tempid on the client, then send it off for resolving while storing it in the state

smeister 21:45:19

until the real id is known

dnolen 21:45:41

I’m going to repeat my last statement until you say something that clarifies what you are doing :simple_smile:

dnolen 21:46:02

there should be no need to track tempids at all

dnolen 21:46:17

you just send data w/ tempids across the wire

smeister 21:47:17

Ok. But i also want to store the newly created object locally in some places and use it until it is resolved.

dnolen 21:47:30

so do it

dnolen 21:47:42

everything you’ve said is exactly how tempids work

dnolen 21:47:46

w/o doing anything at all

smeister 21:48:21

Yes, my question is only how to get the same tempid sent off to the server that is stored in the state.

smeister 21:48:33

Still don't really get that.

dnolen 21:48:52

I have no idea what you are asking

dnolen 21:48:56

you need rephrase

dnolen 21:49:06

what you’ve said so far doesn’t make any sense to me at least

dnolen 21:50:03

again everything you’ve said is just how it works

dnolen 21:50:07

you don’t need to do anything at all

dnolen 21:50:15

given that I’m telling you this is case

dnolen 21:50:30

you need to explain in this context what your failing expectation is

smeister 21:53:36

Ok. I'll explain what i am doing and how it fails. I generate a tempid for a new object, say a user in a local mutation. I want to insert this new user into some collections as well as into my by-id collection (i use normalization). The mutation is remote as well to generate the object on the server. Datomic gives me back the real id and i send a map mapping the tempids coming from the client to the real ids and want to resolve them. But the tempids in the state are not the ones returned by the server for resolving, as they are generated outside of the action method. So different targets get different tempids.

dnolen 21:54:21

the last 2 sentences make no sense

dnolen 21:54:29

they sound like a bug in your server code

smeister 21:56:07

Hm no already checked everything. The server resolves the same id that is requested to be resolved correctly. Why doesn't it make sense? Isn't the mutate called multiple times by the parser?

smeister 21:56:51

If this is so i have no idea how i could get what i am seeing - two different tempids, one sent, one written.

dnolen 21:57:01

what I’m saying is I cannot understand the behavior you are describing at all

dnolen 21:57:04

it just sounds like a bug

dnolen 21:57:15

in your code

dnolen 21:57:29

you create a tempid on the client, if the server returns a mapping from that to the new one it will work

smeister 21:57:54

Ok. But it seems like i am creating two different tempids on the client.

dnolen 21:58:32

right because your mutation handler has side effects

dnolen 21:58:39

which the docs tell you not do

smeister 21:58:47

I understand that.

smeister 21:59:02

And i didn't expect that to work.

smeister 21:59:18

I just wanted to know if there is a way to do this in one mutation cleanly.

smeister 21:59:49

If i had a separate save mutation it would work of course

dnolen 21:59:54

OK but showing examples that obviously aren’t going to work don’t help me understand the problem

dnolen 22:00:09

I cannot relate what you are saying, with your intentions, with what you are trying

smeister 22:01:30

Sorry, i thought the description for my snippet made it clear. Will think about it again.

dnolen 22:03:00

@smeister: something I do not understand is why you aren’t passing the tempid as a parameter to the mutation

smeister 22:04:10

Ok this would solve my problem. It just didn't seem that clear, as it should be an implementation detail which is better kept inside of the mutation. I'll do it that way now, thanks a lot.

dnolen 22:04:45

I don’t see the value of hiding tempid constrution

dnolen 22:05:01

you wouldn’t really be hiding it when dealing with Datomic either

dnolen 22:05:23

@smeister: reading back over your example I see that I misread

smeister 22:05:27

Hm yeah just didn't like it inside of the ui code

smeister 22:05:37

But i'll do it anyway as it's a simple fix.

dnolen 22:05:47

it’s just how it’s done

dnolen 22:06:00

you cannot put tempid creation anywhere else

dnolen 22:06:17

because mutations will run multiple times

dnolen 22:07:25

well I can think of a couple alternatives - but they aren’t any simpler than just generating this when you invoke the transaction

dnolen 22:07:33

transaction stuff isn’t generally portable anyway

dnolen 22:07:43

and tempid are unique so I don’t really see any problems

smeister 22:08:17

Ok thanks!