Fork me on GitHub
#om
<
2015-10-27
>
jannis00:10:45

Ha, in my kanban demo app (I'll share it tomorrow), adding an card editor dialog, with all the reads and mutations involved, requires less om.next code than it requires CSS to look nice. Pretty awesome.

jannis00:10:21

I may not have understand queries in every detail yet but I'm mostly adding components, mutations without looking at the result and when I'm done saving the last change, I look at the app and it usually just works. Development is becoming really intuitive and predictable.

dnolen00:10:23

@jannis to me one the biggest value propositions of this work is that Om apps should have a very regular, predictable structure

dnolen00:10:56

you should be able to jump into an Om application and have a really good idea what’s going on - yet still provide enough flexibility for people to solve their own problems however they see fit.

jannis00:10:29

I'm curious to see what structures other people come up and how easy or difficult it will be to understand those.

jannis00:10:35

But... early days. simple_smile

dnolen00:10:00

I’m sure people will come up with crazy things maybe even some of them crazy cool simple_smile

dnolen00:10:23

but still parser + queries + stateless components give you a lot of guideposts when reading some UI code

jannis00:10:36

Ha. The total app is ~ 377 lines of code, the CSS is 236. And that's with a menu to choose kanban boards, a view of the active board with lanes and cards in each lane, with card DND between lanes, and with a basic dialog for editing cards. And initial data to have something to look at. simple_smile

jannis00:10:10

I should head to bed. This is fun. G'night/day!

tyler00:10:34

Just realized the middleware pattern around the parser read and mutate functions is very powerful. With all the context info you have access to (ref, query, params, etc.) you can do some really interesting things: like decoupling your validations from your parsing logic by passing model definitions into middleware.

dnolen01:10:16

slowly putting the docs together

dnolen01:10:18

edits welcome

dvcrn01:10:17

thanks for the alpha10 release with the changes for RN simple_smile

dnolen01:10:09

@dvcrn: np, let me know if it actually works

dnolen01:10:18

I don’t have a setup for actually testing RN at the moment

dnolen01:10:35

@tyler yes interested to see how people end up using parsing

dvcrn03:10:40

@dnolen: got it working

dvcrn03:10:52

though I had to patch https://github.com/omcljs/om/blob/master/src/main/om/next.cljs#L626 - (gdom/isElement) won't work on native

dvcrn03:10:25

this time I made sure that no code is preloaded. That is entirely om.next

dvcrn03:10:39

and here's the updated version with queries simple_smile

dnolen03:10:01

nice! will fix that bad validation

dnolen03:10:25

@dvcrn: just cut 1.0.0-alpha11, DOM free

dvcrn03:10:29

works! wohoo

dvcrn03:10:19

I'll write something together how to run it

blissdev03:10:06

@dvcrn: which editor/theme is that?

dvcrn03:10:44

@blissdev: it's a emacs configuration kit called spacemacs - https://github.com/syl20bnr/spacemacs/ basically emacs pre-configured with vim keybindings and a ton of support like clojure(script) out of the box

dvcrn03:10:55

the theme is the default spacemacs one

blissdev03:10:06

ah ok, very cool, i actually use spacemacs, was curious if you had possibly made intellij look the same

blissdev03:10:22

hadn’t done any clojure yet in there

blissdev03:10:24

you like it?

dvcrn03:10:58

haha oh. I have a intellij license but once you get used to the superb keybindings of spacemacs, it's hard to switch to something else

blissdev03:10:14

yeah, i should try it for cljs dev

blissdev03:10:30

thx for the info

dvcrn03:10:58

it has a ton of clojure support and integrates very well with cider https://github.com/syl20bnr/spacemacs/tree/develop/layers/+lang/clojure

blissdev03:10:54

have you been using it with figwheel?

dvcrn03:10:58

that too, yes. Figwheel can embed nrepl. That makes it easy to connect to it with cider (or anything else)

blissdev03:10:15

ah ok cool, gotta try that

dvcrn08:10:10

are there some examples on using a server to fetch data besides the om-next-demo?

dvcrn09:10:48

question again about normalisation... I have a component that looks like this:

(defui RootComponent
  static om/IQuery
  (query [this]
         (let [subquery (om/get-query Person)]
           `[:app/people ~subquery])))
Person looks like this:
(defui Person
  static om/Ident
  (ident [this {:keys [name]}]
         [:person/by-name name])
  static om/IQuery
  (query [this]
         '[:name]))
my reader is very generic (for now) and simply checks if the key exists in state. If it doesn't, return :remote true. So by default it will automatically fetch remotely because my app-state is empty. The reconciler is then calling my function I specified for :send and fetches some data from my server, looking like this: '{:app/people [{:age 22, :name David} {:age 29, :name Fred} {:age 20, :name Paul} {:age 22, :name Jiyoon} {:age 23, :name Bob} {:age 28, :name Mark} {:age 24, :name Jiwon} {:age 28, :name Dude}]}' If I don't tell it to normalize, this data is being reflected in the app-state correctly, however when I add :normalize true to the reconciler to tell it to normalize, the resulting app-state is always empty. Where is my mistake?

dnolen10:10:43

@dvcrn: could be a bug, but you should isolate the issue.

dnolen10:10:57

(om/normalize RootComponent server-data true) should work

dnolen10:10:10

if it does work then it may be a bug in the Om Next sync bits

dvcrn10:10:26

let me try that

hmadelaine10:10:02

@dnolen @mhuebert Hi ! I am very interested in your conversation about recursion, could you keep me in the feedback loop if you progress on that matter ? It would be awesome simple_smile

dnolen10:10:55

@hmadelaine: will do, will need to think about it some more.

thomasdeutsch12:10:12

for a blog article i am collecting some patterns that i keep repeating in my om.next app.

(render [this]
        (let [{:keys [app/item-a app/item-b]} (om/props this)]
          (html
            [:div
             (some-> item-a
                     component-a)
             (some-> item-b
                     component-b)]))) 

thomasdeutsch12:10:47

i am unsure about this one... is there a better version for this?

dnolen12:10:13

that’s not an Om Next problem … that’s a templating choice problem

dnolen12:10:13

I don’t use those things, and since I don’t I would probably make a simple function helper

anmonteiro13:10:02

@dnolen: not sure if you're familiar with the CircleCI Om (Legacy) architecture

anmonteiro13:10:05

where they basically have a "common" component that wraps all the pages; it interprets a :navigation-point to decide which sub-component to render

anmonteiro13:10:29

was wondering how to use this notion with Om Next (haven't tried it yet)

anmonteiro13:10:58

basically my hypothetical problem is that this wrapper would have no queries (?) but would be the root component (I think this is some kind of contradiction)

anmonteiro13:10:48

or maybe it would have to dynamically get the queries of the components it would host

anmonteiro13:10:53

looking for some pointers

jannis13:10:34

Is :navigation-point like a route (e.g. "/user/5") that is mapped to a user page?

dnolen13:10:09

@anmonteiro: the root component does have a query, whatever the navigation is and whatever the subcomponents need.

anmonteiro14:10:40

Navigation-point is a keyword that's resolved to a component statically

anmonteiro14:10:05

@dnolen: so the root would have the navigation-point and get whichever component's query dynamically?

dnolen14:10:08

@anmonteiro: so put that in the query and use om.next/subquery

dnolen14:10:15

it’s covered in the docs

anmonteiro14:10:54

Alright I'll try and put together some code before I ask any questions that might not even arise

jannis14:10:37

Here's an initial version of my Kanban demo app for Om Next: http://jannis.github.io/om-next-kanban-demo/ (DND may not work on Firefox and IE, hopefully on other browsers though). I'll see about a write-up next.

dnolen14:10:22

@jannis neato! working in Safari for me FWIW

anmonteiro14:10:30

How should subquery be called if we dont set react refs? om/subquery this nil MyComponent?

anmonteiro14:10:07

Or is the ref explicitly needed?

anmonteiro14:10:42

Looking through the source it seems pre conditions won't pass if I pass null

anmonteiro14:10:59

(No access to computer atm)

jannis14:10:24

@dnolen: Sweet simple_smile It seems Firefox has a problem with how React translates :onDrag* or something but I haven't investigated yet. Glad it works with Safari.

dnolen14:10:51

@anmonteiro: think for a moment

dnolen14:10:00

how can this possibly work without refs?

adam14:10:11

Looking at the parser code, an error thrown by the :action thunk gets overwritten when :value exists. Presumably the error should be retained so we can tell that the mutation failed? https://github.com/omcljs/om/blob/master/src/main/om/next/impl/parser.cljc#L109-L111

anmonteiro14:10:23

@dnolen: I'm sorry to waste your time, will look into it when I have computer access; just didn't want to lose my thoughts

dnolen14:10:00

@adam good catch fixed in master now

adam14:10:13

@dnolen: that's great - thanks!

tyler14:10:58

very cool demo @jannis works on my machine

bbss14:10:58

The company I work for made an app called http://www.sweepboard.com as a side project a while back. It's pretty dated and alternative services like http://waffle.io are probably better but we still have people signing up naturally and some even asking for enterprise versions so we were thinking about doing a rewrite.

bbss14:10:18

You mentioned "almost more lines of css than cljs" and that might be a good argument for me to bring forth when we discuss tech simple_smile

bbss14:10:42

of course not a really strong argument but still 😄

chedgren15:10:16

Maybe someone should make a round version of the official om logo, so it looks more like the clojure one. https://avatars0.githubusercontent.com/u/10822115?v=3&amp;s=200 Like that but round.

drcode15:10:28

Hi everyone... I know a parent's iQuery function should not "muck with" the result of a child's iQuery function... but in the remote sync tutorial there is the following code:

drcode15:10:40

(query [this]
    (zipmap
      [:dashboard/post :dashboard/photo :dashboard/graphic]
      (map #(conj % :favorites)
        [(om/get-query Post)
         (om/get-query Photo)
         (om/get-query Graphic)])))

drcode15:10:12

Is it OK in this case because "conj" preserves metadata put onto the get-query calls of the children?

drcode15:10:34

Or am I misunderstanding the point of saying that "a parent needs to return its own iQuery" and "a parent should not muck with the query of its children?"

dnolen15:10:34

@drcode: union queries are special case, that component isn’t a real standalone thing, just a union component

dnolen15:10:42

and it’s not breaking the more important rule of trying to use a child query as it’s own

dnolen15:10:46

which isn’t allowed anymore

drcode15:10:13

Is the reason for this that queries are tagged with metadata that must be preserved?

dnolen15:10:39

nothing to do with metadata

dnolen15:10:07

there’s really no better explanation that queries are like a typing assignment to a tree

dnolen15:10:24

and trying to use a child’s query is lying about the types

dnolen15:10:01

when you lie about the types, the things that Om Next can assume about what’s happening breaks down

drcode15:10:03

Don't know why i'm having difficulty with this- But that explanation helps, thanks again

dnolen15:10:20

so what’s happening is this

dnolen15:10:26

your tree is actually

dnolen15:10:36

but you’re saying it’s B

drcode15:10:10

Certainly makes sense from that perspective

dot_treo15:10:47

@drcode, in the example from the tutorial it still creates a new (union) query where it uses the childs query as an (extended) subquery

dnolen15:10:00

in the union example this typing analogy is preserved

dnolen15:10:07

A -> U(C|D|E)

drcode15:10:41

@dot_treo: Yes, I do see that, essentially every component needs it's own "node" (or "nodes") in the query tree or bad things happen. And, since the "Query selector" model gives an app complete freedom to define the tree structure to fit its needs, this rule is easy to maintian

dnolen15:10:06

every IQuery component needs it’s own node

dnolen15:10:10

not every component

dnolen15:10:17

we elide non IQuery components from consideration

dnolen15:10:27

A -> -&gt; -> * -> C

dnolen15:10:32

gets collapsed in to A -> C

dnolen15:10:29

on list of todos before hitting beta

dnolen15:10:50

as they are related

jannis16:10:07

@bbss: The "almost more CSS than code" argument doesn't hold very long. I mean, this is just a really simple app with no remote syncing, multi-client conflict handling etc. etc. Nevertheless, I think apps can be written in a concise way with Om Next - and "less code ≈ fewer bugs" is a factor in management decisions. ;) The predictability (in development and issue analysis) is probably the stronger argument though.

monjohn16:10:15

I have a subcomponent that I am using to iterate over a sequence. Once I reach the end, I have my mutate function change a top level value that is used for navigation. I include the key in the “value:” pair returned from the mutate function. The root component has a query for that value, but the UI is not updating. I am guessing that this is because the transact! was on the child component, which does not have that query. Any thoughts?

jannis16:10:03

@monjohn Try passing the query key to om/transact!, e.g. by passing in [(some/action ...) :app/active-object].

jannis16:10:51

I've made a habit out of only transacting in the component that has the corresponding query though, and pass a callback down to subcomponents. Here is an example: https://github.com/Jannis/om-next-kanban-demo/blob/master/src/kanban/app.cljs#L62

jannis16:10:08

There :boards/active represents the current object being rendered and menu items use a callback to trigger a transaction in App, which is also where :boards/active is queried.

monjohn17:10:06

@jannis: So you are saying to pass the query key to the transact! function in the vector after the mutation functions?

jannis17:10:58

I'm not sure why it's needed (or why returning the key via :value doesn't have that effect) but I had the same problem and this worked.

monjohn17:10:08

Hmmm. Doesn’t seem to do the trick for me.

jannis17:10:05

Can you post your code somewhere?

monjohn17:10:10

@jannis: https://gist.github.com/monjohn/b90a18d551e7ed7403f0 I put comment on top to explain where transact! is being called

tony.kay17:10:02

@jannis: I've seen the same problem on mutate :value seeming to be ignored...had not explored it yet, but it surprised me too...possibly a bug

tyler17:10:20

@jannis if you have a second, I’m curious what the purpose is of this query definition https://github.com/Jannis/om-next-kanban-demo/blob/b1b1612cff58931925a96ab7c30098030a9c8cec/src/kanban/components/boards_menu.cljs#L10-L12 it doesn’t look like it gets used anywhere with get-query. Does om.next use it under the hood somehow?

jannis17:10:04

@monjohn: Looking at it now. It's "Strafstoß", not "Strafenstoß" by the way. 😉

monjohn17:10:18

@jannis: I will be sure to change that. 😀

jannis17:10:59

@tyler: Good point. It's not used anywhere. {:boards (om/get-query Board)} in App somehow ensures that boards are normalized properly and since the query fields of Board are a superset of BoardMenuItem it works. It doesn't feel right though. I wonder if adding {:boards (om/get-query BoardMenuItem)} to App would be valid or whether it would conflict with {:boards (om/get-query Board)}...

jannis17:10:22

@monjohn: I've noticed you return values like :value [:word-list :correct] even though there is no :word-list at the top level of the app state. I'm not sure but I don't think :value is interpreted relative to the component's query. You might reuse the same mutation in another component with a different query, where :word-list does not exist, for instance.

jannis17:10:46

But I'm not sure exactly how :value works yet.

jannis17:10:22

Anyway, adding :current-list to the mutation updates the UI (because that's what changes when you increment). Might also want to pass :correct to the transaction.

tony.kay17:10:35

If you look at the indexer, it indexes all components that rely on a particular key...does not matter the query depth. The components that ask for a "key" will end up in the index

tony.kay17:10:34

which is why you should probably namespace the keywords...otherwise you end up associating a query keyword with components that ask for different things

monjohn17:10:01

@jannis That works! Thanks. Yeah, I can’t get my head around :value and how it gets match up with queries.

tony.kay17:10:01

@jannis: so, when you returns keys in the :value, it just looks up the components that are in that index at that key. See my prior comment lead-in

tony.kay17:10:52

e.g. not just tied to root

jannis18:10:16

@tony.kay: Ok. But if read returns nothing for :word-list, returning {:value [:word-list]} will not find any corresponding components, I assume?

tony.kay18:10:36

Om doesn't look at your read result...you're responsible for mucking with the state. The list of keywords goes into a list of "things that changed". The indexer is then used to look up those components. I don't think read has anything at all to do with it (how could it?). The indexer indexes components as they are mounted...this includes the keywords that are listed in their query. I have a section called "The Indexer" in https://github.com/awkay/om/wiki/Om-Next-Overview that might help.

tony.kay18:10:08

(the indexer is still evolving...my info might be slightly out of date)

jannis18:10:27

Right, I don't understand the indexer well enough yet to talk about this, so I'll try to refrain from adding extra confusion 😉

tony.kay18:10:56

Yeah, it is really helpful to just pprint the state of the index...that cleared a bunch of questions up for me.

tony.kay18:10:16

cause then you see how it indexes those keywords that mutate is supposed to be returning

tony.kay18:10:25

(e.g. what components is associates with them)

tony.kay18:10:55

it's just maps...so trivial to read (except for the query index...which is a zipper)

monjohn18:10:04

@tony.kay: Just want you to know your write up about om.next was really helpful

tony.kay18:10:34

oh, thanks. While we're exchanging that kind of thing: I was soooo excited to see @jannis kanban demo...gave me some great ideas

jannis18:10:20

Sweet simple_smile Glad to hear that. Any of those ideas ready for sharing? 😉

tony.kay18:10:18

Oh, I just really appreciated seeing how someone else is structuring the app. Nice and clean. I sometimes forget OO layout for components can be nice in CLJS too. E.g. I was tending to put functions in a let (of render) instead of just making them members of the class.

tony.kay18:10:31

Also had not played with html5 DnD...nice to see that working

jannis18:10:55

I used to put those extra functions outside components but I like to keep things closer together. Perhaps it's because I'm new to functional programming and am used to the "global functions/variables/etc. are bad" mantra from the OO world. 😉

tony.kay18:10:57

right, if it isn't reusable, no need to pollute the global space. I think we all agree on that. Thus the tendency to "let" things...but in the case where what you're writing decomposes nicely on OO lines, I see nothing wrong with leveraging techniques that everyone understands clearly...methods on a class. Gives nice localized reusability

Lambda/Sierra18:10:16

Global state is bad. Referentially-transparent global functions are fine.

jannis18:10:38

@tony.kay let would be fine as well, except it would clutter render a bit more... perhaps. Move a few definitions out of the way and it becomes more readable / understandable. I think either way is good. :)

tony.kay18:10:14

@jannis: That's just it...the let is "fine", but I found the OO technique in the context of a React class just reads better.

dnolen19:10:01

@jannis transactions just return the list of keys that changed in :value

dnolen19:10:44

that this doesn’t change anything seems non-sensical until you think about actual remotes

dnolen19:10:02

a mobile client may not care about keys that it can’t or won't display

jannis20:10:53

@dnolen: I may have to wait for a few more examples or try to understand the code before I understand what exactly happens with :value after it is returned. When I started working with Om Next, I thought that if a mutation returned {:value [:foo] ...}, all components that ask for the key :foo in their queries would be rerendered. After working with it more, it turns :foo has to be appended to the transaction via (om/transact! this '[(do/something ...) :foo]) for that to happen. That leaves me confused even after your explanation.

dnolen20:10:26

:value result of transactions are thrown away

dnolen20:10:56

it’s just there as information to the developer

dnolen20:10:00

a la HATEOAS

dnolen20:10:27

as the writer of the client you can decide which of those keys you’ll actually want to read

dnolen20:10:49

again it doesn’t make that much sense for client local only

dnolen20:10:57

but it makes sense if there’s a remote thing

dnolen20:10:05

different clients will want to read different things

dnolen20:10:33

this way you don’t force clients to download a bunch of stuff they don’t want

jannis20:10:00

If :value is thrown away, how does its presence or content have any effect on what clients download?

dnolen20:10:23

because it’s in-band it means a human doesn’t have to read out-of-band docs

dnolen20:10:36

my current acronym for this is RTFAPI

dnolen20:10:53

Om Next puts all this stuff in band

dnolen20:10:59

again same as HATEOAS

noonian20:10:59

so how to force a render for a component that depends on data that changed from a transaction but isn't the component initiating the transaction?

jannis20:10:15

Ok. So if I'm a developer and I want to perform the mutation 'do/something, I can look at its implementation and :value in there to see what keys I could pass to om/transact! because I'm interested in them. {:value [:foo :bar]} means (om/transact! '[(do/something ...) :baz]) makes no sense.

dnolen20:10:36

it does make sense if you think it about it a little more

dnolen20:10:47

mobile client doesn’t have the space to show some subview list

dnolen20:10:51

desktop client does

dnolen20:10:00

transaction says subview list changed

dnolen20:10:06

but mobile client never showed it

dnolen20:10:49

ipad developer comes along and says

dnolen20:10:56

I can make subview list work, yadda yaddda

tony.kay20:10:16

Hm. that definitely helps. I was misunderstanding this as well.

dnolen20:10:30

again all this stuff is covered in Relay docs & Falcor docs

dnolen20:10:35

not one original idea here on my part

tony.kay20:10:04

I'm feeling anxious about code that is ignored...really does lead to misunderstandings. But...opinions are like...

dnolen20:10:37

however much time people have spent thinking about this … I guarantee Relay & Falcor have spent 10X longer

jdubie20:10:26

this makes a ton of sense. i was trying to figure out why my the keys specified in my server side :value weren’t getting re-read. client has to specify them in the transact! call

dnolen20:10:40

in nearly every problematic case in Om Next the answer was already handled elsewhere

dnolen20:10:40

I’ve gone out of my way to narrow in on the things that actually require innovation

dnolen20:10:38

Om Next is far far far more conservative then it may seem 😄

monjohn20:10:54

I think some of the confusion arises in that there aren’t examples in the quickstarts that showed passing the queries to transact! so we assumed that that :value must signal the repaint. Or I should just speak for myself.

dnolen20:10:58

@monjohn: there are two problems here

dnolen20:10:26

no Om Next remote example showing the value of doing it this way (even though this concept is well covered in Relay & Falcor)

dnolen20:10:34

components are a special case

dnolen20:10:54

they are implicit keys to be re-read on the transaction out of convenience

dnolen20:10:33

so everytime you do (transact! c ‘[(do/it!)])

dnolen20:10:37

that’s actually

dnolen20:10:49

(transact! c ‘[(do/it!) ~c])

dnolen20:10:26

assume back tick there of course

monjohn20:10:24

@dnolen Of course the remote example is the one I missed searching 😊

bones22:10:31

@dnolen just out of interest why is Om.Next authored in both .cljs and .clj ?

davewo22:10:12

@bones macros aren’t possible in cljs

davewo22:10:35

defui is a macro

noonian22:10:36

cljs macros are written in clj, and om.next is useable from Clojure as well

bones22:10:52

Ah! Thanks

bones22:10:40

Oh right, because by the time the "defui" is in the browser it's now js - all these macros happen at author time (om/clojure newbie if you hadn't guessed)

steveb8n22:10:39

@bones: also it’s used on the server side to provide a service endpoint for a client parser e.g https://github.com/swannodette/om-next-demo/blob/master/todomvc/src/clj/todomvc/parser.clj

bones22:10:41

Oh right, thanks again