Fork me on GitHub

@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.


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.


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


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.


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.


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


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


@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.


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..


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


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.


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)


Feel like im inventing a lot and doing a lot wrong


and I鈥檇 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


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


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


yes but it seems to contradict this sentence in Tony鈥檚 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 must post-process that or you'll send the wrong stuff to the client."


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 馃槶 )


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


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


yes but because of the side-effect issue we are wondering about I followed this example while I wait for the idiom to be well documented. It works for now but I鈥檒l be happy when I know I鈥檓 using the best practice


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!


Does the tempid translation works if you use datascript database ?


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


So that they can be updated..


@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.


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


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


Then reconcile it with returned tempids in my merge.


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})]


@iwankaramazow: I'd be using params.


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.


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?


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


let's see what @bhauman thinks


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


I'll take a look :hugging_face:


@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.


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


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


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.


@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)


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


hey all, is there a reason the read function is called twice when going through the reconciler's transact!?


just noticed that at the REPL


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


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


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


@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.


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


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


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


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


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


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


(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)


@jlongster: add-watch is just Clojure


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


It's a deep cut simple_smile


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


(I think it's IRef鈥)


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


it was always an experimental thing in previous Om


Shoot. That's kinda the impression I was getting


I have a new way to deal with that which doesn鈥檛 rely on React internals


but it鈥檚 not a back portable thing really


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?


you shouldn鈥檛 have to do anything at all


the whole point was that nothing in your code changes


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


Oh, awesome


right I thought that far ahead at least simple_smile


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


as long as your code doesn鈥檛 unwisely rely on the state rep you should be fine


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


yes re: 鈥減ush"


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


That's really helpful. Thanks!


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


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 馃槍


@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.


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鈥檛 get normalized after delivered from the server.


@akiel: denormalized state to the send callback


if it鈥檚 not getting normalized something else is up


@dnolen: thanks I will have a look into it


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


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


Has anybody tried this with Om?


@iwankaramazow: it鈥檚 possible but that isn鈥檛 an Om question at all


I would direct questions about Google Closure capability to #C03S1L9DN


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


@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?


@akiel: the first part, yes that鈥檚 always been the case 1:1


there not much value in that not being the case


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


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


I either don鈥檛 know what you are saying


essentially remove certain joins


or you don鈥檛 know that you can already do this with :query-root


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


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


ok I will do - thanks


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


since that happened just now.. ^^ simple_smile


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


@dnolen: makes sense, thanks


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


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


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


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


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


@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


@anmonteiro: i mean it will work just fine


you just won鈥檛 get any perf benefit


@dnolen: yes, that I understood simple_smile


@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.


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


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.


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


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.


it depends, how many new interfaces are there?


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


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


for anyone who's interested, I've just written something about recursive union queries:


great stuff, makes me happy 馃槃


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


easy enough to write, just wondering if I鈥檓 looking in the wrong place


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


@ethangracer: there isn鈥檛 but we should probably add one, file an issue