This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2015-12-11
Channels
- # admin-announcements (26)
- # aws (1)
- # beginners (356)
- # boot (28)
- # cider (20)
- # clara (12)
- # cljs-dev (78)
- # cljsrn (22)
- # clojure (333)
- # clojure-brasil (1)
- # clojure-dev (15)
- # clojure-miami (1)
- # clojure-nl (3)
- # clojure-russia (61)
- # clojurecup (3)
- # clojurescript (137)
- # clojurex (4)
- # core-async (4)
- # data-science (3)
- # datavis (2)
- # datomic (31)
- # editors (1)
- # emacs (9)
- # hoplon (3)
- # juxt (8)
- # ldnclj (47)
- # leiningen (4)
- # luminus (4)
- # off-topic (20)
- # om (332)
- # onyx (1)
- # parinfer (23)
- # portland-or (4)
- # proton (161)
- # reagent (46)
- # ring-swagger (11)
- # specter (7)
- # yada (2)
@dnolen I was planning on using it to perform remote queries on specific items in a list. To lazy load associations
anyways I would not use it for now, or you should be OK with the fact that it might go away by beta.
I’m having a hard time conceptually understanding how to fetch additional data on top of an existing query
@caleb: One way to do what you want is to transact on the abstraction "load this thing now", in which case you can include the ident as a parameter to the mutation
then modify the app state in such a way that your read code (for the remote interaction) can determine what you want to do
I saw the defered loading example in the FAQ, but I can’t wrap my head around a more complicated example
It seems so simple, but coming to this model from a traditional JS background, everything is new
@tony.kay: it might make sense to write up some of these common patterns on a design page somewhere
it really is pretty dreamy how simple it is...but I understand that when you're new to the pieces it seems like you have to bend over backwards...then you realize...oh! I just say what I want to do!
@dnolen: Yeah, I keep trying to get time to work on more docs...I'm hoping the tutorial will eventually encompass a lot of these
Yeah, and learning that Clojurescript can act like a framework of sorts, with composition and multimethods etc is pretty wild… If I come up with a clean example I’ll create a repo somewhere that people might find helpful.
Hm, have we come across this before? If you build with :optimizations :advanced
and call om/update-state!
in a component (with a query), you get an uncaught No queries exist for component path (#object[Vu \"function Vu() {React.Component.apply...
error.
The kanban demo runs fine with advanced and it uses om/update-state!
in a component without a query...
Heh, tried to reproduce it with a minimal app, getting a completely different error about an undefined prototype. Perhaps I'll try another day
@jannis: I was getting that in my project when it was using set-query! to change page query params.
Without knowing anything about advanced optimizations it sounds like in certain situations components are optimized so much that Om can't identify them as belonging to particular queries anymore or statics and perhaps other Om "meta information" is lost.
this sort of minimalish router example breaks with :advanced: https://github.com/thos37/om-next-router-example/tree/pushy
It happens right after set-query! on this line https://github.com/thos37/om-next-router-example/blob/pushy/src/om_router/core.cljs#L152
Ok, will try later - time for bed. I'm already messing up my sleep cycle, as evidenced by the Friday 4am commit on https://github.com/Jannis/custard/graphs/punch-card 😉
so I’m pondering killing off :shared
and :shared-fn
, as far as I can tell the link based approach is just far superior
and the support to allow changing :shared
after the fact interacts badly with mock-root
and devcards testing
About that... if you have a structure like Root -> ListView -> ListItem and only Root and ListView have queries but you want to link to some global piece of data in ListView, it'll have to have a query, right?
Would that imply a need to introduce a read key like {:list-query (om/get-query ListView}
only to embed the list view query in the root?
It's not a problem and I'm happy with killing off :shared
and :shared-fn
. Just wondering how to use "global" links in an elegant way.
the other option is to make :shared
a once only thing and maybe provide a helper to re-render the root if you really, really want to change :shared
also would like to hear what people need componentWillReceiveProps
for, suspecting it’s more trouble than it is worth
componentWillRecieveProps
is useful for setting state in response to new props without causing 2 renders
I think that's the main use case
@chris-andrews: this is not relevant for Om
I added force-root-render!
for people that really want to recompute :shared
, not recommended for anything else
So if that's the case, then it's pretty likely componentWillUpdate
would also cover any uses I can think of for componentWillRecieveProps
there's an example use of componentWillRecieveProps
here https://github.com/advancedtelematic/parking-visualization/blob/master/src/cljs/parking_visualization/map.cljs#L111
So, if you call set-state!
from either shouldComponentUpdate
or componentWillUpdate
in om.next, will componentDidUpdate
have the new props and new state at the same time?
@chris-andrews: not sure you should try it, but we control the semantics
componentWillUpdate
seems useful as well as a standard behavior for what you see in componentDidUpdate
the signature of the send
callback has changed https://github.com/omcljs/om/commit/a13913cb8762f4d09ae0fe3d342059ddc3fb630d
in order to avoid too much breakage, if not supplied will fall back on the old behavior
@dnolen: we're using shared for i18n locale. Otherwise we have to pepper it into every query everywhere or access a separate global atom. Definitely don't want to kill it off completely. Having to force a refresh on locale change is fine for that case (in fact, we want to force re-render everything in that case), and I have found no other use for it so far.
@tony.kay - on UI Exercises I see this warning: Warning: Each child in an array or iterator should have a unique "key" prop. Check the top-level render call using <ul>. See https://fb.me/react-warning-keys for more information.
I see it from the given example at the top too, can I ignore it for now while I continue to get up to speed on this stuff
yeah, don't sweat it. I'm not sure if my code is missing those, or something in devcards...either way it is harmless at the moment
Thanks, also, can I feed back places where I find the instructions confusing? If so, what would be the best way?
@tomayerst: file an issue is probably best.
does anyone know how to run https://github.com/swannodette/om-next-demo/ project ?
@nxqd: You can try … from the todomvc folder lein repl (require '[todomvc.core]) (todomvc.core/dev-start) then http://localhost:8081/ That demo is a lot out of date and is not completed
Going through different example project of om-next I realised that it’s absolutely boring to go through the components itself as it’s contains mainly rendering and query/mutation names. You can get pretty much full picture by checking send, mutate and read logic, nothing else is needed much. This is awesome
is component local state considered bad practice for "intermediate app state data"? More specifically: Form element values (pre-validation, non-persisting)
pretty sure it is good practice in such cases
yes, exactly! I also found this pattern in the todomvc example. just had a discussion with a friend about whether to do this or not. he said that David explained in a talk, that this should not be done (but he could not refer to which talk this would have been).
Does this also apply to handling validation errors on inputs? He was arguing too, that errors should also be pushed into components via props, whereas I am using local state (for now)
not sure if I understand the question, but there's nothing wrong with pushing validation via props and storing it on local state (only when first initializing local state) for later use
Oh, sorry. I meant the actual error states. For example a vector holding the current errors (update-state my-component assoc :errors [:must-not-be-empty])
and then make use of that local state during rendering
not sure about that one
I'd say "depends"
which is not really helpful, but there are different scenarios there
if it's e.g. a truly reusable component, then nothing wrong with error states for validation being in local state
if the errors mean something application-wise I wouldn't put them in component local state, but rather in app-state
not only for truly reusable components, that was just an example; if the error state means nothing outside of that component, what's wrong with it being in local state?
@nblumoe: What I've been doing so far is have forms operate on entities in the app state (e.g. a user
) with mutations like (app/update-user {:name ~text-input})
and then annotate the entity with error properties (e.g. {:name "" :errors {:name "Name may not be empty"}}
. The form would include :errors
in its query and can then render the errors. So that's one way of doing via the app state and props.
But I agree, there are cases to be made for both approaches, app state and local state.
If you want to make the approach I described reusable, provide an :update-fn
callback to a reusable form and wrap the form in an app-specific component that has a query for the form data and knows up to turn :update-fn
into a transaction like the one described.
i prefer storing intermediate or unvalidated form data in app-state, reason being that i can get a copy of the app-state from someone and i should be able to trigger the same errors
but i don't store the errors themselves in the app-state, i see those as something that should result from the state but doesn't have to be a part of it
the same state should trigger the same error validation logic
when you put intermediate data into the app state, do you keep it in a separate subtree?
So far I've either been updating existing entities or creating entities with tempids (when creating a new user for instance).
yeah, i have a :forms subtree that holds data for the forms
once i hit send, it clears the form if valid and updates the 'real' data
i don't know if this is the best approach, just how i decided to do it
i keep remembering a demo from circleci (i think) where they printed the app state, copied it to another browser and everything rendered exactly the same
this is what im trying to follow in the back of my head
seems like it could be a useful way of debugging
@danielstockton: The separation certainly helps avoid dealing with conflicts/updates received from the server while editing a form. Do you react to updates happening in the "background" in any way?
You know, like when an editor tells you a file you're editing has been changed on disk and asks whether you want to reload it?
@jannis I don't, I guess that would add extra complications, good to think about
atm data only really flows in one (circular) direction
If you have explicit an "save" action, it's probably easiest to handle it then. Determine whether the data has changed/moved on, decide what to do in that case.
@dnolen: I still get the no components exist for component
error with master and advanced compilation. But I can't reproduce it outside my app.
@dnolen: as I commented in #536, I thought componentWillReceiveProps
was ultra-necessary, but I seem to be able to do everything in componentWillUpdate
for my actual use cases
@anmonteiro: ok, cool
(just a "go ahead" to remove support for willreceive if necessary)
@anmonteiro: still collecting feedback at this point
I meant from my part, definitely don't wanna impose lol
@dnolen: was the removed line in this commit only there because of :shared
? https://github.com/omcljs/om/commit/549166d9b74136b171cb69b758660729f9e4dd65
maybe should go ahead and remove the one with ::skip
too
since ::skip
is not queued anymore
Ah. boot cljs
requires (cljs :optimizations :advanced :compiler-options {:pseudo-names true})
, not (cljs :optimizations :advanced :pseudo-names true)
.
@jannis the way to do this is to verify that the body of full-query
in your project matches master
@jannis: does it only happen with advanced compilation?
let me try with an app I have
These are the two components involved: https://github.com/Jannis/custard/blob/master/src/web/app.cljs and https://github.com/Jannis/custard/blob/master/src/web/components/requirements.cljs
does your app start up? But then this error occurs if you try a transaction or something?
Here: https://github.com/Jannis/custard/blob/master/src/web/components/requirements.cljs#L21
You mean Requirement
instead of Requirements
. That returns #object[$web$components$requirements$Requirement$$ [object Object]]
.
@dnolen: is anything preventing us from using /
in query params?
e.g. '[(:some/key {:param ?this/param})]
doesn't seem to work
@anmonteiro: no reason that shouldn’t work
@dnolen: Could it be that get-query
needs to use (iquery? x)
instead of (implements? IQuery x)
, like full-query
?
getting Uncaught #error {:message "Invalid join, {:dom-com/props ?dom-com/query}", :data {:type :error/invalid-join}}
@dnolen: there are -> https://github.com/omcljs/om/blob/master/src/main/om/next.cljs#L234-L252
@dnolen: switching from using /
to -
in the param solves my issue. Is this something I could dig into?
@anmonteiro: sure go for it, probably something simple
where should I start? actually have no idea
@anmonteiro: https://github.com/omcljs/om/blob/master/src/main/om/next.cljs#L195 perhaps?
@anmonteiro: the bug is right there
right
@anmonteiro: var?
is also busted
are you fixing it or should I?
@anmonteiro: go for it
@anmonteiro: please add test cases for it too
alright, working on it
@dnolen yup that's where I'm starting
@dnolen: is it OK for my patch to add "src/test" to the project.clj source-paths
?
maybe I shouldn't conflate
@dnolen: since you were looking for feedback about componentWillReceiveProps
, I’ll try to describe how I was using it (under the assumption that the lifecycle methods had the same behavior as in React)
I need to render some things using three.js. Three.js has a very stateful API. As an example, you can't resize a buffer. If you have something where the number of vertices has changed from one pass to another, you actually need to remove that object from the scene and create a new one. For other modifications, like changing colors or position, you can update your original stateful object.
By using the react/om lifecycle methods, I can sort of smooth over that stateful API to get a functional API. What I want to do is pass in as props: the WebGL context where the object will be rendered, vertices, triangles, and color data. From there, the component should handle all of the logic of what three.js actually needs to do. The actual render
function always returns nil
because the output doesn't have a dom representation.
@chris-andrews: I understand how you might be using it, but realize it doesn’t actually work the way it’s supposed to in React for fairly arcane reasons
Ok, wasn’t sure I had ever explained it. In my case, I actually realized that there is a way around it for me
The workaround for me is actually solved by om providing the key function
@dnolen: param thingy should be fixed in PR #540
For my use case, it’s actually a simpler result to just key components based on certain data, and if that data changes, the old component is destroyed and the new one is created. I would say that since it doesn’t work the same way as react, it might be best to remove componentWillReceiveProps
, because really the only uses I can think of depend on it working exactly the same as in react
@dnolen: Would you be willing to push out another release for the fixes since alpha26?
what should I pass as the last argument to parser in case I want to do a remote read?
true
or the name of the remote?
hey guys, check this out - cross platform Om-Next component for mobile and browser + simultaneous editing on both platforms using figwheel: https://github.com/artemyarulin/ktoa/raw/master/omnext.browser.mobile.gif
@artemyarulin: Bravo !
nah, all the applause to @bhauman for the figwheel and to decker405 (don’t know your nick here sorry) for figwheel-react-native, I’ve just combined that
oh, now I know your nick here
Let’s say I’m organizing a top-level component that has several sub-level components, each of which has a query. What’s the correct was to handle parsing? I have a read function that tries to call (parser env query)
as the value, but that doesn’t seem to handle remotes.
if you need recursive parsing it’s nearly always about query roots, deferred loading, stuff like that
@dnolen: is this correct? when calling the parser with 2 arguments (local read), even if we return {:remote something}
it has no effect
in general you can expect that it will be called 1 for local and N more times for each remote you’ve declared
other than perhaps branching in your reads so that you’re not doing work for the remote cases that you don’t need to do
cool, thanks
@dnolen: I'm seeing the need for general recursive parsing. Any time you want to embed a parameter somewhere you have to walk down to that point. db->tree
nor Datascript help with that. The fact that some cases exist in arbitrary places often means walking through stuff that you would have otherwise been able to use db->tree
on. Fortunately, it is pretty easy to generate a few helper functions that make it pretty trivial to write (and even hide the recursive calls to parser). My helpers so far are not finished, but it might be something to consider providing as lego blocks.
Okay, so what I’m wanting to do is recursive parsing: where downstream components have arbitrary queries, and the things compose together. So that read can handle downstream joins, etc.
@jgdavey: I recommend separating remote and local logic at the entry point read function...case by target, then call off to a remote-read vs local-read. Then it isn't all hard to reason about
if you’re going to distribute parameterization through your query then that you’re own problem to solve
I'm not advocating standard functions for dealing with the parameters, just some to help you walk the graph on the way to it
fair enough, but it changes the problem domain from one that is more widely understood (recursive parsing) to data transforms on an AST (which are internally recursive). The helpers I've written so far make it trivial to understand and write what you want....just simply splits out the bits of db->tree
as reusable components. Because ultimately, you are just doing that...transforming the db to a tree...but all of the logic in db->tree is all jumbled together so it isn't reusable.
Well, you are providing db->tree, which IS about the default db format, so you do care about supporting that format. The default db format is fast (Datascript will be much slower...it does a lot more). I have a feeling that in practice you'll want to use plain maps. In which case, it makes sense to me to fully support ease of use for that. But, it is your library, and it is trivial to add these kinds of helpers as a separate one, so it probably isn't worth the breath
not advocating cycles...just talking in response to the comment of the form "you don't really want to do much recursive parsing". That is not my experience so far.
as long as we’re not interfering with user innovation / additions then we’re on the right track
Can I get a quick description of how to run https://github.com/swannodette/om-next-demo? There’s nothing in the README
@dnolen: I may have found another issue with advanced optimization: web.app/?state is not ISeqable
. I'm guessing this is about parameterized queries: https://github.com/Jannis/custard/blob/master/src/web/app.cljs#L18
Backtrace here just in case: https://gist.github.com/Jannis/e1b8df181deecbef381c
Right. The backtick turns {(:foo {:baz ?baz})}
into {(:foo {:baz my.app/?baz)}}
which I guess Om can't substitute