This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2016-01-07
Channels
- # admin-announcements (69)
- # alda (8)
- # beginners (6)
- # boot (182)
- # cider (10)
- # cljs-dev (24)
- # cljsrn (17)
- # clojars (70)
- # clojure (142)
- # clojure-brasil (5)
- # clojure-czech (1)
- # clojure-poland (4)
- # clojure-russia (96)
- # clojurescript (115)
- # community-development (37)
- # component (6)
- # cursive (11)
- # datomic (32)
- # events (4)
- # funcool (6)
- # hoplon (17)
- # ldnclj (10)
- # lein-figwheel (24)
- # mount (12)
- # om (141)
- # onyx (7)
- # parinfer (48)
- # re-frame (24)
- # reagent (31)
@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.
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
@danielstockton: I just fixed the :result thing in my app https://github.com/stevebuik/om-next-ideas/commit/32eb5bc54156935a0d9d51a1706c638c4b4499a7 after reading this https://github.com/awkay/om-tutorial/blob/23b55f828c2adb9ee96125fb6132646a82c2b7de/src/main/om_tutorial/simulated_server.cljs#L65
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
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’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."
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?
I thought it conventionally it should be in :value https://github.com/Jannis/copaste/blob/master/src/server/parsing/copaste.clj#L105
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 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
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.
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.
k, thanks!
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
@anmonteiro: cool thanks
@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!
? https://gist.github.com/jlongster/5120b6b1160866affc12
@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!
@anmonteiro: I see, thanks
@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
Could someone point me to info on no-local-state
and how it's supposed to work (unless the code is the only documentation)?
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
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?
Yeah, we had big plans for serializing, transporting, and rehydrating the state, but given we don't try to do that anymore…
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’t get normalized after delivered from the server.
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’s possible but that isn’t an Om question at all
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?
at this point in the venture, looking at tests and the devcards is probably a good idea
@dnolen: was going to ask you to cut a release of Om whenever you merged #566
since that happened just now.. ^^
@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?
@bplatz: it does work
@bplatz: have you looked at this? https://github.com/awkay/om-tutorial/blob/master/src/main/om_tutorial/parsing.cljs#L191-L228
@anmonteiro: memoize
won’t actually work since the recursion calls don’t 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
@dnolen: yes, that I understood
@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
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’m 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: https://twitter.com/anmonteiro90/status/685226213539999744
BAM thanks @anmonteiro !
@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
easy enough to write, just wondering if I’m looking in the wrong place
oops, missed the channel description, my bad! has anyone found such a predicate?
@ethangracer: there isn’t but we should probably add one, file an issue
done, thanks