Fork me on GitHub
#om
<
2016-01-07
>
bplatz01:01:02

@zalky I can confirm for you that you that when using Datascript you need to implement a read method that will figure out the right thing to do. In practice you'll have nested components (which in turn will be in nested maps) that will require some logic to pull all the data anyhow.

bplatz01:01:22

Also, I see union queries being inquired about frequently, I think because there is an idea that you'd do routing with them. I've personally found the need for them extremely rare. So I'd just confirm for yourself that you really need it to begin with.

bplatz02:01:00

I've come up with two strategies that make queries with datascript much easier for me:

bplatz02:01:34

1) I use identities everywhere I possibly can... I've started to add new unique identities on entities that might not have otherwise had them just so I can use identity + recursive pull syntax. I think over half of my otherwise needed queries have been replaced by thinking a bit differently about the data and figuring out ways to get identity into everything.

bplatz02:01:48

2) I've come up with a standard naming convention for keys referring to nested compoenents/queries, I just prefix them with a "_" and as convention, nothing with with that prefix ever needs to go to the server or datascript, it is just organizational. That allows me to easily pull out nested component queries from another query and perform a recursive parse on them.

bplatz02:01:49

This, incidentally is also how I handle filtering queries based on route...

bplatz02:01:09

Not sure if it is the best way, but after trying other things it is working for me quite well.

danielstockton08:01:06

@bplatz Do you have a server/backend? Are client side mutations optimistic or everything is sent to the backend? I'd be interested in hearing about your tempid migration strategy.

danielstockton08:01:37

To anyone with custom merge fns: The response from a read is different from mutate (mutate has :keys/:tempids and :result). Interested in what sort of conventions people are using..

danielstockton08:01:56

Do you check for a :result key and make a distinction?

danielstockton08:01:23

Also, is transacting (datomic or other database) outside of the :action fn in a backend mutation acceptable? It seems like it's necessary to be able to pass the tempid/permid translation to :value.

danielstockton08:01:09

But then I don't even need :action, my frontend is really only concerned with updating the tempids most of the time (since i already have the rest of the data from optimistically transacting on the client side)

danielstockton08:01:18

Feel like im inventing a lot and doing a lot wrong

steveb8n09:01:58

and I’d also like to hear peoples thoughts on doing datomic transactions outside the :action fn. It seems like the only way to resolve tempids but it is a side-effect

iwankaramazow10:01:00

Aren't all database interactions 'side-effects'?

danielstockton10:01:21

Thanks, good to see others dealing with the same problems!

steveb8n10:01:22

yes but it seems to contradict this sentence in Tony’s tutorial "Action method on server-side must be the thing that figures out the new ID (side-effect free). The return value of the action methods ends up as :result on parser return...you must post-process that or you'll send the wrong stuff to the client."

iwankaramazow10:01:27

hmm I'll take a look at it later today currently I'm trying to write a parser for a backend in PHP 5.3 without ssh access, only ftp 🙃😬 (only thing my backend support 😭 )

danielstockton10:01:12

@steveb8n Interesting that you do it on the server side rather than in merge, am i right that your tempids key is inside :result?

danielstockton10:01:46

although if you're having custom merge/migrate fns, i don't suppose it really matters much

steveb8n11:01:57

yes but because of the side-effect issue we are wondering about I followed this example https://github.com/awkay/om-tutorial/blob/master/src/main/om_tutorial/simulated_server.cljs#L52 while I wait for the idiom to be well documented. It works for now but I’ll be happy when I know I’m using the best practice

danielstockton11:01:38

I'm in a similar position but feeling slightly happier knowing that at least I had some valid concerns and wasn't missing something obvious, so thanks!

geraldodev11:01:50

Does the tempid translation works if you use datascript database ?

danielstockton11:01:36

No, you have to come up with this yourself. Probably means adding identity attributes everywhere for idents rather than using db/id

danielstockton11:01:51

So that they can be updated..

bplatz12:01:57

@danielstockton: @steveb8n: I at least don't use action functions on the back-end, only the front-end. I just pass back a {:value ..} with my result.

bplatz12:01:23

I've yet to do tempids, so that may make me change this behavior, but for now it works.

bplatz12:01:05

On the front-end my strategy was going to be to just use with-db in datascript, and store that novelty in an atom.

bplatz12:01:29

Then reconcile it with returned tempids in my merge.

iwankaramazow13:01:52

Does anybody have some design related advice about params vs union type joins? Current scenario: I have a list with race results: :results [[result/by-id "1204" etc.]] result/by -id {"1204 {...}} I currently fetch all race results only for a given year. Should I venture with my query union type join as in [{:results { 2016 [:id :place :time]}] or is this more of a params use case [({:results [:id :place :time]} {:year 2016})]

bplatz13:01:21

@iwankaramazow: I'd be using params.

bplatz13:01:05

I believe the main use case for unions is when rendering multiple different types of components together, i.e. the facebook feed example given is a good one. At least for me, if it doesn't fit in that bucket, unions is not the answer.

iwankaramazow14:01:55

when fetching new values from a remote and merging it like #(cb {:results (t/read r (.stringify js/JSON %))}) it overwrites/deletes all values in :results instead of adding them. Any ideas on dealing with this?

anmonteiro15:01:30

@dnolen: trying to improve the Om Next story with devcards, I've submitted this Devcards patch

anmonteiro15:01:39

let's see what @bhauman thinks

anmonteiro15:01:33

@iwankaramazow: if you want a different behavior, you need to override merge-tree! in the reconciler

iwankaramazow15:01:47

I'll take a look :hugging_face:

zalky15:01:08

@bplatz, thanks for the response! I will look into how identities can simplify queries. I can see how the underscore convention would help with parsing, but I'm reluctant to add adhoc conventions into the query specification unless there no better alternative. The reason I was exploring union queries was it was the suggested approach for dealing with heterogeneous content in the tutorials. However, any recursive read methods that rely on :query will not have the sub-query/sub-selector passed to them as the ast seems to stop breaking the query down on unions. I'm wondering if that's expected behaviour. For instance, if you run (om/query->ast (om/get-query Dashboard)) on the union queries tutorial example, I would expect :children and :query attributes for the union elements, but there are none. It seems it just stops breaking down the queries at a union. I'm just wondering if I'm mis-understanding how unions or the ast work, or if that's just not been implemented yet, or what.

bplatz15:01:19

@zalky I'd expect that to work, I'd lean towards that being a bug but perhaps there is some reason for it.

anmonteiro15:01:46

@zalky: it's normal behavior that you don't get the union entries in (:query env)

bplatz15:01:47

As for new convention, something needs to know what is datascript data and what is UI structural, so I think you will need some convention to do so regardless of what you do... unless you store your structural data also in datascript, which I do not.

anmonteiro15:01:37

@zalky: but if you're using recursive parsing as you say you are, there's nothing preventing you to do this: (parser (assoc env :union-entries query) query)

anmonteiro15:01:04

and consume that :union-entries bit in the desired places

jlongster15:01:08

hey all, is there a reason the read function is called twice when going through the reconciler's transact!? https://gist.github.com/jlongster/5120b6b1160866affc12

jlongster15:01:19

just noticed that at the REPL

anmonteiro15:01:47

@jlongster: the parser is called twice for local & remote targets

anmonteiro15:01:53

if you also print (:target env) (besides "called") in the code you linked you'll get something like false and true

zalky15:01:59

@anmonteiro: thanks for the clarification, and the simple solution!

zalky15:01:35

@bplatz: I have not had to break queries into structural/data components yet, but thanks for the idea, I will keep that in mind once I get there.

dnolen16:01:53

@jlongster: more generally neither :read nor :mutate can have side-effects in their bodies

jlongster16:01:37

@dnolen yeah, I knew it was harmless but I was just curious

peeja16:01:52

Could someone point me to info on no-local-state and how it's supposed to work (unless the code is the only documentation)?

peeja16:01:35

I'm upgrading from 0.8.8 to 0.9.0 and having trouble with state stuff breaking

peeja16:01:52

but I'm not the person who added no-local-state, so I'm a little lost. simple_smile

jlongster16:01:57

is there a way to listen to app state changes in the reconciler? what's the lower-level API that add-root! users? I see an add-watch call in there but that's not defined anywhere

jlongster16:01:26

(I'm not trying to do anything except learn at the REPL. I have data sent back from a remote and would be neat to see a notification)

peeja16:01:30

@jlongster: add-watch is just Clojure

jlongster16:01:45

@peeja: oh! I'm still learning Clojure in depth simple_smile

peeja16:01:53

It's a deep cut simple_smile

peeja16:01:41

You can watch any atom for changes (or any other IRef)

peeja16:01:53

(I think it's IRef…)

dnolen16:01:53

@peeja: no-local-state more or less broke in 0.9.0 due to changes to React

dnolen16:01:09

it was always an experimental thing in previous Om

peeja16:01:19

Shoot. That's kinda the impression I was getting

dnolen16:01:28

I have a new way to deal with that which doesn’t rely on React internals

dnolen16:01:36

but it’s not a back portable thing really

peeja16:01:52

Is there a good description somewhere of what it does that might help me rip it out properly, or should I just crawl the code?

dnolen16:01:09

you shouldn’t have to do anything at all

dnolen16:01:17

the whole point was that nothing in your code changes

dnolen16:01:32

just stop using the no-local-state option when you start your app

peeja16:01:42

Oh, awesome

dnolen16:01:51

right I thought that far ahead at least simple_smile

peeja16:01:01

The point was to push "local state" changes into the app state atom?

dnolen16:01:16

as long as your code doesn’t unwisely rely on the state rep you should be fine

dnolen16:01:24

it was always intended as a debugging feature more than anything else

dnolen16:01:28

yes re: “push"

peeja16:01:09

Yeah, we had big plans for serializing, transporting, and rehydrating the state, but given we don't try to do that anymore… simple_smile

peeja16:01:25

That's really helpful. Thanks!

dnolen16:01:15

again the feature will re-appear - but it will work without any help from React itself

iwankaramazow17:01:24

Man, Om Next is awesome Redux combined w/Falcor was nice on paper but in reality it was a horrible mess Om Next is everything I wanted React/Redux/Falcor to be 😌

tony.kay17:01:32

@steveb8n: On tempids, datomic outside of :action, etc. The contract of the parser read/mutate functions is that they are side-effect free. As a result I personally see no need for :tempids to be a thing you'd ever actually return directly from a server-side mutate, since as you note this breaks the contract of side-effect free: There is no way to generate an id without side effects (even random number generators have state, and therefore calling them causes side effects). It seems you should always return them as data from the :action function, which then turn up as :result, and you then post-process them to the real return value to the client which does have the :tempids key. Of course, the other thing is that if you had two+ mutates in a single transaction you'd have to combine the tempid remappings in post processing anyway. On the bright side, this makes your job in the mutate functions themselves a little easier, since you don't have to worry about idents, only ids...the post-processing can convert all of the various mutate tempid mappings into a single value pulled from the various action results.

akiel18:01:45

Do I have to pass normalized state to the send callback? I do not see this in the todo-mvc example. But in my app, the data doesn’t get normalized after delivered from the server.

dnolen18:01:23

@akiel: denormalized state to the send callback

dnolen18:01:52

if it’s not getting normalized something else is up

akiel18:01:28

@dnolen: thanks I will have a look into it

iwankaramazow19:01:10

does anybody know if it's possible to partially load an app with google closure ?

iwankaramazow19:01:26

Webpack w/ React Router partial app loading trough require.ensure is all the rage these days

iwankaramazow19:01:50

Has anybody tried this with Om?

dnolen19:01:56

@iwankaramazow: it’s possible but that isn’t an Om question at all

dnolen19:01:13

I would direct questions about Google Closure capability to #C03S1L9DN

iwankaramazow19:01:26

k, thx for the response, I'll move it there

akiel19:01:39

@dnolen Solved my problem. But it means that the database format has to follow the query structure 1:1 and one has to possibly re-root remote queries. There could be more support for re-rooting or do I miss something?

dnolen20:01:24

@akiel: the first part, yes that’s always been the case 1:1

dnolen20:01:30

there not much value in that not being the case

dnolen20:01:53

as far as re-rooting, what more do you actually need then what is already provided?

akiel20:01:00

I like to transform a query like [{:a [:b]}] into [:b] under re-routing of :a

dnolen20:01:03

I either don’t know what you are saying

akiel20:01:14

essentially remove certain joins

dnolen20:01:14

or you don’t know that you can already do this with :query-root

akiel20:01:14

yes I haven't found usage for :query-root

dnolen20:01:11

at this point in the venture, looking at tests and the devcards is probably a good idea

akiel20:01:35

ok I will do - thanks

anmonteiro20:01:58

@dnolen: was going to ask you to cut a release of Om whenever you merged #566

anmonteiro20:01:12

since that happened just now.. ^^ simple_smile

dnolen20:01:32

@anmonteiro: still want to mess around with caching stuff - so will cut a release after that

anmonteiro20:01:18

@dnolen: makes sense, thanks

anmonteiro20:01:03

@dnolen: does the caching problem occur when you use db->tree with arity query data state ?

anmonteiro20:01:08

I ran a few simple tests with memoize and it seemed to work. should I be trying more complicated stuff?

bplatz20:01:34

Is it expected that using remotes with recursive parsing does not work?

bplatz20:01:16

I think I have a minimal case where it doesn't. I'll have to double-check it.

dnolen20:01:38

@anmonteiro: memoize won’t actually work since the recursion calls don’t go through the memoized thing

anmonteiro20:01:27

@dnolen: it's what I thought, so maybe I missed something. as I said I've only run very simple test cases. Won't bother you again until I try further

dnolen20:01:44

@anmonteiro: i mean it will work just fine

dnolen20:01:50

you just won’t get any perf benefit

anmonteiro20:01:03

@dnolen: yes, that I understood simple_smile

bplatz20:01:35

@anmonteiro: The code sample is helpful, essentially you need to create a parser wrapper around the parser. I was expecting (I guess hoping) that you could just return {:remote ast} in a recursive query and it would just work.

anmonteiro20:01:26

@bplatz: I think hoping is the word there; it could never work that way if it's recursive simple_smile

bplatz20:01:36

I thought it might queue the remote requests and then process them at the end of the cycle together. Essentially that is what the custom parser wrapper that Tony wrote is doing.

iwankaramazow20:01:42

Recursion & hope do not tend to co-exist with clarity of mind 😬

griff20:01:49

In om next what is the best practice for making a component that can contain any other component? I want to make a modal container and was wondering what best practice was for setting the content with the new interface in next.

iwankaramazow20:01:13

it depends, how many new interfaces are there?

anmonteiro20:01:18

@griff: this is a React thing, but your container component should call om/children to render its children

steveb8n20:01:02

thanks @tony.kay glad to know I’m on the right track there. your tutorial pays dividends once again

anmonteiro22:01:33

for anyone who's interested, I've just written something about recursive union queries: https://twitter.com/anmonteiro90/status/685226213539999744

dnolen23:01:41

great stuff, makes me happy 😄

ethangracer23:01:14

@dnolen are there any predicates for checking if some data is an om temp id or not? I’m not seeing anything in the om.tempid namespace

ethangracer23:01:08

easy enough to write, just wondering if I’m looking in the wrong place

ethangracer23:01:46

oops, missed the channel description, my bad! has anyone found such a predicate?

dnolen23:01:52

@ethangracer: there isn’t but we should probably add one, file an issue