This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2015-11-25
Channels
- # admin-announcements (3)
- # beginners (165)
- # boot (123)
- # cider (106)
- # clara (1)
- # cljsrn (20)
- # clojure (199)
- # clojure-canada (2)
- # clojure-dev (3)
- # clojure-poland (29)
- # clojure-russia (7)
- # clojure-taiwan (2)
- # clojurescript (487)
- # cursive (25)
- # datavis (89)
- # datomic (26)
- # gorilla (2)
- # hoplon (15)
- # ldnclj (12)
- # lein-figwheel (9)
- # leiningen (2)
- # liberator (1)
- # off-topic (25)
- # om (380)
- # onyx (26)
- # parinfer (52)
- # portland-or (12)
- # re-frame (28)
- # reagent (132)
Am I overthinking this: let's say I'm modelling Cludo: I have people, weapons and locations. I can make components for each of these using (defui ...) with om/Ident and om/Query. Happy days. What I can't see is how to declare the query or ident for a component which displays information from, say, all three (Ms Brown in the Conservatory with the Lead Piping).
Can someone point me at an example of something vaguely related please.
This seems logical but introduces a non-data related structure into the query
(defui Accusing
static om/IQuery
(query [this]
(let [suspect-query (om/get-query Suspect)
weapon-query (om/get-query Weapon)
location-query (om/get-query Location)]
{:suspect suspect-query
:weapon weapon-query
:location location-query}))
Object
(render [this]
(let [{:keys [suspect weapon location]} (om/props this)]
(dom/div nil "I think it was"
(suspect-view suspect)
" with the "
(weapon-view weapon)
" in the "
(location-view location)))))
@dnolen: Figured it out. In order to observe a ref cursor and not see the No protocol method IRenderQueue.-queue-render!
error, the atom has to be passed to the om/root definition. I had replaced the om/root definition with {}
assuming that since I was using observers I didn't need to bother with the root having a state reference. Clearly, not the case.
Hi everyone, learning Om next. I have a legacy wsdl 😞 and rest services and using datomic is not a choice unfortunately. Any tutorials/articles/best practices/examples/helpers how to parse omnext queries into calls for those services? I guess it shouldn’t be that hard if I limit the amount of functions supported, for example without joins, etc.
@olivergeorge: What do you mean by non-data related structure? Looks about right to me
i think the query should be a vector though, queries are always vectors
is it possible to send extra environment information to the local parser? I need to call an external function during my parsing process (in some mutation parts), I can on my tests since it's easy to set custom env there, is there a way to send extra env vars to be available on the parser at the reconciler?
wilkerlucio: yes, you can pass anything as the environment
@danielstockton: how I do that for my main parser on the reconciler?
I tried adding to the reconciler config, but it doesn't get passed down to the parser calls
would that work?
i might be wrong actually, i misread something in the docs
trying to work it out myself..
I'm trying here too
@danielstockton: yes that does the job, thank you
good to know
@wilkerlucio , @danielstockton tony kay's tutorial also adds a :db-path
to the env
you can find it here: https://github.com/awkay/om-tutorial
I'm not really into the details, so I don't really know how he does it
actually I'm reading his parsing sources right now, good stuff
enough of manual data handling on the parser, hehe
@olivergeorge: why do you need a union for this?
@dnolen: not sure if this is a bug, or if it's even intended
but om/component?
has a boolean type hint, though it does not always return a boolean
(when om$isComponent
has not yet been defined, it returns nil in cljs, undefined in JS)
which is still false-y, so no bug there; but is the type hint used/applied (not sure of the wording here) in this case?
@denik I took a look at your example, not completely convinced that should work. Do you have a use case for that pattern?
@anmonteiro: huh good point, I don’t see how that could actually be a problem but will fix for correctness
@dnolen: just something I realized when looking at what om$isComponent
is and where it gets bound
@dnolen: On mutation: Used to be {:value ... :action ...}
and action's return was ignored. Then :tempids
went on the top-level, and then we realized that those need to be assigned (for side-effect free code) in action
...so now return value of :action
goes into :result
. I think we renamed :value
to :keys
, and I assume you still intend that to go in the return of the mutation call itself (not action).
What is the complete story on mutation now? Specifically:
where are we putting :keys
?
What should go in the return value of :action
? I assume the answer to this is "anything you want, but :tempids
is a good idea"
And the following is a "feature or bug?" question:
I notice that default-merge
still handles :tempids
, but not if they are in :result
...you have to restructure the result before returning it from the server (or after receiving it). I guess you could put tempids in the return of the mutate itself still, but generating them always seems like side-effect behavior...so I'm wondering if default-merge ought to be changed.
I'm doing post-processing...but seems like if you return :tempids in result from action (which could be standard), auto-merging that into value would be useful as a standard...tempid remapping is always side-effect code.
If you wouldn't mind clarification on :keys
and :result
. You intend :keys
to be "things that have change due to this mutation", and :result
(on value, not :action
) to be....what?
some people might returns futures and not want to bother and have different id migration strategies I don’t know
as I was alluding to above people who use Datomic often just return a future for a transaction
you may later decide you want some other behavior you can accomplish with a bit of post processing
E.g. I might not even be able to DO the real db interaction until after each individual mutation function has run on the server...I might just be collecting up bits of things to do?
In my om.next app, i tried to embrace linking and a better db structure (instead of the recursive reader chaos). I ended up to not include function-queries (example: [ (:search/results {:query ?query})]
into my root-queries. Also, I ignore readers that are not root readers. With this simple structure i render down my component-tree. Up to this point, it worked very nicely. Is this how it was intended ? i think that db->tree
works the same for not datascript users. After all components are rendered, there is missing data (like the search/results) - do i trigger it in my render-fn / or other life cycle functions like did-mount ?
@tony.kay: that’s a possibility - all I know is that server side you will want more flexibility
@thomasdeutsch: yes that’s how it’s intended
Yeah, I see now. Systems are usually aiming at helping you "do the right thing"...in this case, since we've completely abstracted the idea of what a mutation does to a higher-level thing there is really nothing more you can say.
it's very not obvious, you can always include links in your queries that are totally *virtual*
this should eliminate many many many cases where people believe they need recursive parsing
yeah, on the recursive parsing...I do need to rework a lot of my examples to that. So many things to do
@thomasdeutsch: I’m not sure I understand the missing data bit, you can put marker values in for that stuff
@dnolen: currently in the Om Next Documentation, set-query!
is under the section "Components"; shouldn't it be under "Query & Identity"? should I fix it?
@anmonteiro: yes, thanks
eventually people will see the the Tree/Graph thing is really, really a big idea. I’m patient!
@dnolen: what are marker values? by missing data, i simple mean that the reader for the query [(:search/results {:query ""})] is not there in the initial data, so i think i have to trigger it (for example by setting the query params)
@dnolen: :search/results []
as part of a query? sorry, i do not understand.
@thomasdeutsch: I think he meant as part of your state...markers in your state to indicate "missing data"
your read function can then make a decision about asking for it. If you want to force a refresh, simply mutate your marker value back over top of the state...transact causes local read and gather sends...so that will cause it to ask the server
@thomasdeutsch: If you care to share your parser that uses more links in state I'd love to see it
@dnolen yes I believe it makes sense for anything you'd want to constantly re-render. Sensor driven visualizations for example. I use it for a microphone input volume indicator. Currently using a manual request animation frame loop that keeps calling .forceUpdate as long the component is mounted.
I'm about to convert my main code in om tutorial to that form...be nice to see what you're doing
@tony.kay: it is not a matter of parsing... it is simply getting the initial data (the db->tree data) via root-query , then render down the components. That said, i have no clever parser.
but i am happy to share a small example
@thomasdeutsch: that would be great...I'd like to see the details of what you are doing
1. should a set-query!
in componentWillReceiveProps
or componentWillUpdate
trigger a re-render?
2. is 1. related to @denik's issue?
on the same topic as @anmonteiro, should a om/transact!
inside a componentDidUpdate
trigger a re-render? currently it doesn't
@anmonteiro: @wilkerlucio will look into these issue at the same time
another nice post on normalization https://twitter.com/softwarecf/status/669569941432049664
@anmonteiro: I did a dirty hack calling my om/transact!
inside a setTimeout
callback to get the re-render to work from a componentDidUpdate
, not ideal but is doing the trick for the moment
Hm. Anyone successfully using om/force
? I'm not seeing target
on the AST, but not sure if it is my misunderstanding or a bug
I'll try to track it down...it's weird, if I put just a forced read, I don't even get a gather sends...if I put a local read followed by a force, then I get see the local AND remote parse, but target isn't set...I'll trace it down and submit an issue if it is one.
If I have this query: [{:people (om/get-query Person)}]
and I do an transact on [(app/f) ':people]
which AST node should have the target?
probably my misunderstanding...I am assuming transform reads is giving me the UI-rooted query in transact*...but I'm obviously confused
I was thinking that since the local read looks up the full query that the forced remote should do the same, except mark those node AST's with a target
ok. Yeah, I was thinking of "force remote" as a "force re-read of stuff I already have", and that we we're going to namespace the keys to tease apart what is what in an abstract sense.
is there documentation on what keys the env
param in the parser contains?
@anmonteiro: we should document what appears there in clients
So, in many cases it seems like the query I want to remote is some dynamic thing on the UI, rooted at the key I'm targeting. I guess I can implement my logic on forced reads to look up the queries on the UI myself if that's what I need.
that makes sense, actually, since I might end up with multiple queries, and it is likely only one of them makes sense
In the case I'm working on, at least, I definitely want a query fragment from the UI that will work fine on Datomic on the remote
I was expecting my remote read logic to "just work" by looking for that target on the real query...that's why nothing was happening...but had it been there, it would have been quite elegant, because I would not have had to write any more code...the remote fetch logic is the same on initial reads as it is on forced ones....I mean, I could also just revert the node in the local state back to a placeholder during the mutate which would "force it" without force, but that causes flicker
OM-494 resolved https://github.com/omcljs/om/issues/494
so I was hoping for a way to say "when running though the remote read logic, if it isn't there fetch it, or if the AST says "force read it", then fecth it"
@tony.kay: the problem is keys don’t necessarily represent identities the way components do
Yeah, I had seen that discussion with jannis, and that is part of why I assumed it would work that way...so you think it is a bug now?
I've not been following the discussions for a few days and the whole story around AST is slightly overwhelming to me. But I trust you guys know what you're talking about 😉
@jannis the AST stuff is pretty boring it’s just to avoid needing to traverse maps, lists, vector, etc.
I seem to be missing something related to tempids and remote syncing, any examples of that? I'm doing an optimistic update and I end up with duplicate items in a list
I may need to do something special in my merge fn since im using datascript
@danielstockton: nobody has tried tempids + DataScript to my knowledge
more than happy to take them, but I don’t have time to verify every single feature myself in DataScript
don't mind being the first to try, is the general idea to return {:value {:tempids }} from the backend mutate method and then use that in merge to update the state?
and what should the value of :tempids look like?
ups clicked enter way too early
anyway, getting an error in master
on transact!
@bplatz or @hmadelaine may have tried to use tempids with datascript.
@anmonteiro: did you clean your build?
twice
I'm getting Uncaught Error: Vector's key for assoc must be a number.
,
@anmonteiro: I have a bunch of devcards that test stuff exactly like that
re-checking
@anmonteiro: are you using Cursive?
the error only started to appear after issue 494
(the rendering loop thing)
@anmonteiro: please include stack traces
in a second
any examples of tempids without datascript? i can probably make the leap to datascript with that
@danielstockton: I have a really cheesy simple example https://github.com/griffio/om-next-04
simple is good, thanks!
@thomasdeutsch: need more information, like how many times does it re-render when you press a key?
@dnolen: stack trace here https://www.refheap.com/112071
@anmonteiro: please put all the information in one place
@dnolen: will rerender 2x for every key that is pressed
does the snippet work for you, should I create a gist
@thomasdeutsch: hrm I don’t see anything obvious, but it should be super easy to see what’s happening by putting a breakpoint in the render part of the reconciler
so format of tempids is a map of vector (key/id) pairs
@anmonteiro: I’m not going to try it until I read it and correlate
we’re getting to the point where big bugs are diminishing and it’s faster for me just to read the code and stacktrace
I didn't mean does the snippet run
I meant does putting the stack trace in the snippet work for you
alright, edited
@anmonteiro: your mutate doesn’t make any sense
cljs.core/update
why doesn't it make sense?
@anmonteiro: your :value
is wrong
was it always like this?
oh alright
a map of what, then?
I was basing my mutations on @jannis 's kanban thing, which might be already outdated then
(https://github.com/Jannis/om-next-kanban-demo/blob/master/src/kanban/parsing/boards.cljs#L28-L31 )
OK that clarifies things. Thanks a lot!
@dnolen I’ve done some thinking, and I had an interesting idea that was unrelated to history, but may come in handy for working with the cache. Are you familiar with FM synthesis?
sorry for the waste of time
@lambdahands: only insofar as it exists
@anmonteiro: the error is terrible
So there’s a lot of wild math involved that I simply don’t understand, but the general concept is taking a waveform and applying a “carrier wave” to modulate its shape. What FM synthesizers can do is have a base waveform and many carrier waves that can arbitrarily be routed into each other. So I’m attempting to merge two ideas here: a linear “history” that allows us to step forward and back between a series of states, and a set of carrier waves if you will that may be toggled arbitrarily independent of time as you wish. So think of stepping back in time, yet some facts still exist. The big challenge is deciphering whether we’re creating illegal states in the process. Sorry for the brain dump, just wanted to share some thoughts I was having.
also playing around conceptually with commute!
to correspond to transact!
, where in the former you know the order of operations doesn’t matter
also putting transactions into the history so the the commutative operations can be replayed
Very interesting. So there would be two kinds of state recall, one that replays the commutative operations and one that doesn’t?
@anmonteiro: Oh, I should really update the demo
That makes sense. I’ll do some more hammock-ing over the next few days. My main concern is when there’s several of those optimistic updates, some of them fail, some do not, yet the ones that do not are dependent on the ones that did. Stuff can get hairy fast.
@lambdahands: yes that's the problem
I'm new at this, and it seems in line with what is being discussed regarding optimistic updates that fail, although I'm looking for a way to report back to the user that a transaction has failed. So a transaction is initiated, which of course goes through the mutate function, but the transaction on the state fails and therefore I need a way to update the UI to show this error/validation message. A use case I have is that a user updates the name of an entity, although the name must be unique, so I want to show the user that the name is already taken.
@dnolen: Yeah, the new code fails to propagate the :target
from force reads. The target is produced by the ast creation (next.cljs:768), but you only pull out dispatch key.
I got again on that situation where I need to render 2 different representation from the same data at the same component level, what I have is one component managing a new object creation on the top, and at the same level I have another representation that needs to display extra info depending on the creation state of the first, this time I can't group into a single parent element because their representations are apart on the tree representation
@dnolen: I remember that you said that my example on that time was fine if I was willing to dupe some query information, could you please say more on that? I don't got it
@wilkerlucio: One solution would be two separate read keys, both of which internally call the parser again to read and return the same data from the app state.
@wilkerlucio: 2 reps of same data...don't you want Ident?
@tony.kay: I have an ident there, the problem is that I can't send the same key to two differnet components
@jannis: makes sense, I'll try that, thanks
ok in the middle of adding new awesomeness that DataScript people like @thomasdeutsch are already enjoying
@jannis: I dont understand what you mean by combining with an ident, can you please say more on that?
@dnolen: I get the quote on the query instead of metadata: e.g. {:widget (quote [{:people [:ui/checked :db/id :person/name {:person/mate ...}]}])}
That is what I get...is that what I should expect? sorry...getting tired...that is query I get. I was expecting metadata instead of propagating the quote.
@wilkerlucio: you are rendering two reps...like a table in one place, a graph in another, right?
if that is the correct result, then I'll work with it...you don't have to explain it further.
@jannis: yeah, that's very similar to what I'm doing
in my case I'm using the awesome parsing code that @tony.kay wrote on his om-tutorial, so it looks like this:
@jannis: @wilkerlucio I think inventing an ident would be better...can place in the app state as a single thing, and then refer to it directly in queries like [:value [:something :name] ]
I haven't written that into my stuff yet...but David has mentioned it several times just today
now the query is {:widget [(quote {:people [:ui/checked :db/id :person/name {:person/mate ...}]})]}
.
@tony.kay: about your question, yes, that kind of dual representation
k. I'll work through the parser and see if it works now...it didn't just "heal", so something is still amiss, but likely in my code now
@dnolen: OH....I'm using an alternate remote, not the default name...plain quote is going to cause parser to mark with :remote, isn't it?
@dnolen: maybe it's time for a new alpha?
you mean ref
at the env
on parser?
um, @dnolen ident? and ref? are not the same. I am using ref? during reads to see if what I'm seeing in the database is a vector of 2 elements that starts with a kw