Fork me on GitHub

@dnolen: Regarding path optimization: I modified @drcode's example to render two child components, one "normal", and one with a nil prop. Both components' (set-state!) trigger reads from the "wrong" parser route, but only one of them actually displays it.


@dnolen: I didn't know transit for python exists. Thanks for the tip


👍 to computed props & queries from sub-nodes simple_smile


@thosmos: none of what you said means anything to me yet


I'm sure this has come up a bunch of times, but what's the Om Next idiom for rendering a child component?


(dom/div nil ((om/factory ChildComponent))) ?


I think it's normal to define a shortcut to the factory. eg (def my-component (om/factory MyComponent)) and use that to render the component


yeah. create the factory once and use that. Otherwise you'll just keep making factories


@tony.kay: thanks for your Om Next Overview. Really helpful


the IQuery doc mentions “This method should always return a vector.” but in Queries With Unions, DashboardItem returns a map. is that okay because maps can behave like vectors, or doesn’t (query [_]) always need to return a vector?


Is it an anti-pattern to use memoization in order to avoid allocating new objects? It’s not that the memoized function in itself is costly, but it will help Om avoid re-rendering the child component.


Eg, I memoize (map :foo my-list) which in itself is cheap, but allocates a new object every time it’s called (in render)


Nah, forget what I’m saying - lazyness wil save my ass in this case.


The reason my child component’s render was called was that I handled an anonymous function to my child component, and (= (fn []) (fn [])) evaluates to false.


@sander IQuery instances must return valid query expressions - this is always a vector except when you are returning a union expression (which is a map of query expression vectors).


@dnolen: clear, thanks


Forgive a silly question: The Om-next quick start says "This component only declares one JavaScript Object method - render." So Object is a protocol? Where is Object as used in Om/ClojureScript defined/documented? I've grepped through the code of Om and ClojureScript looking for "defprotocol Object" but I can't find it anywhere. What am I missing? Thanks.


@simonb i think the name Object just refers to the generic javascript Object, and you can declare any method on your instance


@sander Thanks. So Object is not a protocol? The syntax looks like a protocol. But I think you're right. It's saying something like "this is a JS Object's methods" I'm just wondering where the semantics of Object are defined.


@simonb: i think this is just the design of the om/defui macro. any function declared under Object will added as a property to the instance (prototype)


so read defui's definition if you want to understand how it works


@simonb Yes, all functions after Object become methods of the JS Object. You can define (foo [this ...] ...) in there and e.g. call it from render like this: (.foo this ...).


@simonb: defui is basically deftype and using Object is a way to define instance methods on the type it creates


slight difference to clojure though since defining functions on Object is not possible there


OK, I have now created my first non-trivial Om Next project, will post it on github in the next few of days. I picked something that has a lot of cross-cutting concerns (a generic rectangle packing layout component) and the indirection enabled by the query selector approach made it surprisingly easy to implement (once I understood the design of Om Next well enough, which certainly required some effort)


@drcode: cool! yes it’s certainly a lot of novelty to sort through simple_smile


but hopefully once you get past the hump the whole thing just levels off really fast


@nolen: As you say, it's more a matter of novelty, not complexity. The bottom line is that any programmer creating a component first asks themselves "What does this component need to know about?" and the fact that previous-gen client libraries don't have a way of specifying this explicitly within components, via a DSL, adds lots of extra complexity in other libraries.


@drcode: yep I think as people get used to this model they’ll wonder they ever got anything done before 😉


I'm playing with a little toy animation example in Om Next, and have encountered some odd behaviour that I suspect might be a bug. Here's a gist of a simple app that displays a button and a ClojureScript logo, and which makes the logo slide to a random location when the button is pressed:


It works. However, if I change the query in the (defui App ...) form to include the animating? key, it no longer works, even if I never use the value of the key anywhere in the component.


The code is a bit long, but the first 60 lines or so are not important. All you need to know is that the swap-animated! function uses js/requestAnimationFrame to modify an atom at every frame.


@tord yeah that example isn't particularly minimal


Yes, sorry about that. I haven't found a way to reduce the size. If it's not immediately obvious what is wrong, I can try...


but can you clarify about the nature of the bug?


sounds like you don’t see any errors?


No, I don't see any errors. But when I include that key in the query, the logo no longer smoothly slides to its new destination, it just jumps there.


@tord: I would do some more bug isolation


println etc.


OK, thanks. I'll see what i can find, and let you know if the bug seems to be in Om.


@tord: please do, it could very well be a bug! would be nice to have a basic theory of what’s wrong though.


OK, after some println-ing, I now know that the state atom is updated correctly at every animation frame and that the UI component's render method is called at every update, but that the query does not return the updated results. Manually calling the parser inside the render method and printing the results shows the correct values, though.


Outline of the relevant parts of the code:


The query looks like this:


(query [this] [:x :y :animating?])


and here is the start of the render method:


(render [this]
  (let [{:keys [x y animating?]} (om/props this)]
    (println "rendering App: x: " x " y: " y " animating?: " animating?)
    (println "parser result: " (parser {:state app-state} [:x :y :animating?]))


The output looks like a long sequence of lines like this:


rendering App: x:  0  y:  0  animating?:  true
parser result:  {:x 142.803804, :y 199.72560000000001, :animating? true}
rendering App: x:  0  y:  0  animating?:  true
parser result:  {:x 142.973312768, :y , :animating? true}


So the values returned by the queries don't match those obtained by calling the parser manually.


I tried printing out (om/props this) for the component, too:


rendering App: x:  0  y:  0  animating?:  true
props:  {:x 0, :y 0, :animating? true}
parser result:  {:x 271.592088, :y 289.433904, :animating? true}


And the strange thing is that when I remove :animating? from the query, everything works, and the props are always equal to the result of manually calling the parser.


@tord hrm file an issue with the complete example … I have a hunch what’s going on but I’m curious why changing the query would trigger this.


Will do. Trying to simply the example a little first.


@tord part of the problem I suspect is that the reconciler uses a counter to know quickly whether props are stale


but you’re not incrementing this from the outside so the old props gets used


pretty sure that’s the issue, and I’m curious how your example worked in the first place to be honest


You mean that the app state should only be mutated directly from the parser, and that mutating it from animation frames messes things up?


@tord mutating it directly messes things up yes. this can probably be easily fixed.


anyways post your issue and I’ll try a couple of things.


banging on the atom from the outside w/o the reconciler being involved is something that should work - so it’s a bug for sure.


@dnolen OK, I managed to reproduce the behaviour with a much simpler app. Will file an issue. Thanks for your help!


@dnolen: David, I'm updating my overview docs, and I wish to make sure I've got this right: "for *queries*: the parser will only be invoked for either the top-level query, or starting at some node with an Ident". I'm trying to make sure people are not surprised at an entry into the parser at some other point that might jump into a read in an unexpected way. Am i missing anything?


I wouldn’t write much about parsing yet


still thinking through the implications of path optimization


saying too much will mean it will just be wrong when this gets sorted


ok, I'll keep it loose.


(the prior versions says quite a bit about parsing...but just from a root perspective)


What is the effect of including keys defined by the read multimethod in the query expression of transact!? e.g.

(om/transact! this `[(boards/activate {:ref ~ref}) :boards/active])
the :boards/active part. I got this from @jannis kanban app which I'm finding very helpful to look through simple_smile


@paulb: It tells Om Next to rerender components that query :boards/active after the transaction.


ah ok, thanks


@paulb: Initially, I thought the :value part of what the corresponding mutate function returns would have that effect. Turns out that's only a way of documenting what keys are affected by the transaction. It doesn't have any effect.


yes, I had made the same assumption. imo would be helpful for the Om wiki to include an example of this.


how do you know if it's necessary to provide keys? In the Om wiki examples, components rerender without providing any


@paulb: I've updated the parser documentation: (hope @dnolen is happy with the explanation)


David said that when transact! is called on a component that component is implicitly included, so (transact! c ‘[(do/it!)]) is actually (transact! c ‘[(do/it!) ~c]) @paulb


I probably shouldn't need to append :boards/active in my demo then. Need to try that.


ah ok the mystery of re-rendering makes much more sense now simple_smile


@jannis I tried removing and everything still works


Updating the code as we speak


I just updated my overview docs to fix my mutation misunderstandings. Relevant to the current discussion. Reading the Relay docs cleared up a lot of things. It is a rough draft, but hopefully it helps people understand the "why" of transact, callbacks, and ":value as documentation on mutation".


New intro section covers some conceptual stuff, and the Mutation section was heavily revised


I still need to integrate the new "computed" stuff


@tony.kay: The new introductory material is really good. I know it always helps me to have a motivating case in mind when learning something new. One typo: search for docuemtation


@monjohn: Thanks. Typo fixed.


@tord: fixed your bug, was in fact simple, just needed to move the internal counter into another place.


@tord as far as I can tell, your animation code never should have worked so I didn’t really look into much further than the fix.