Fork me on GitHub
#om
<
2016-05-31
>
bendlas06:05:58

Are there any good examples how to handle normalization for tree-like data, to be rendered with recursive components?

bendlas09:05:37

In this test: https://github.com/omcljs/om/blob/master/src/test/om/next/tests.cljs#L631 how does the query know to only recur up one level (to :node-value) and not to :tree ?

bendlas09:05:58

also, how is all of this implemented? grepping for ..., i can only find it in test files

bendlas09:05:37

error was, I grepped only in *.cljs, while the parser is cljc

iwankaramazow11:05:35

Has anyone got any kind of batching mechanism running in his/her backend parser?

bendlas12:05:00

@anmonteiro: dataflow / interactions between normalization / query and the parser / parser env.

bendlas12:05:03

also: what return values are allowed for parser functions and what they do

cjmurphy12:05:15

@anmonteiro: The purpose of dynamic queries: set-query! with IQueryParams. My working understanding is that they are not required, as any state can be kept in app state, and reacted to in the component, including things such as sort order etc - the things you are supposed to use set-query! for.

bendlas12:05:17

... so normalization runs just based on static queries, right? so what to do, when the queries are based on data transformed by a parser?

iwankaramazow13:05:32

@bendlas: keep in mind that queries can evolve over time

iwankaramazow13:05:46

set-query! or IQueryParams

hlolli13:05:46

defmethod read :keyword must always be defmethod read :ns/keyword?

iwankaramazow13:05:17

@hlolli: that's just a naming-convention

hlolli13:05:45

so it doesn't matter is I include slash or not?

hlolli13:05:30

ok, then my next question, why does it matter?

iwankaramazow13:05:44

It is idiomatic to use namespaces to group attributes that typically appear together. This is introduced by Datomic.

iwankaramazow13:05:30

Much clearer imo, when your app grows

iwankaramazow13:05:06

let's say you have a current logged in user that has a :name and a list that shows the name of artists

iwankaramazow13:05:21

:user/name & :artist/name

iwankaramazow13:05:35

less confusion

hlolli13:05:20

yes, could just as well be :user-name and :artist-name. I say this bit foolishly because I know the slash can give some functionality to the keyword. Once which I don't understand at all; that you can destructure the later part of the keyword in let [{:keys [name]} (om/props this)] for detmethod read :artist/name, at least that's the case for remote-synchronization tutorial where results from :search/results is destructured like this.

hlolli13:05:33

As for multimethods, I would assume just any symbol, string or keyword would be valid dispatch, getting confused by the requirement to give a :ns (or category) infront of keyword name.

hlolli13:05:46

No I made one wrong assumption, it actually looks like this (c/p): let [{:keys [search/results]} (om/props this) and results becomes a valid symbol without search/ infront of it. (NB. like always, what one can oversee is ackwardly obvious sometimes. Just figured out my problem, which was to key for the given remote key in read).

bendlas13:05:35

@anmonteiro: I'm just playing with your recursive example here https://gist.github.com/anmonteiro/2b282aa35380558a8b1d#file-composite-cljs and unfortunately, it breaks down as soon as transactions are introduced

bendlas13:05:42

@anmonteiro: here is my version: https://gist.github.com/bendlas/1a16b216fb0fbe0e02ef446711f18c4a#file-composite-cljs when clicking on the rectangles, an unrelated state key is updated as expected, but the elements vanish completely during the rerender

peeja13:05:26

Is there a recommended upgrade path to Om Next at this point? Is there a way to use Om Previous and Om Next in the same app?

peeja13:05:57

I mean, I know they should work as just React components, but is there a trick to getting the data to flow to them correctly?

peeja13:05:30

I'd love to just make the new page I'm building in Om Next, and then gradually spread it through the whole app

bendlas14:05:36

@peeja: in theory, you should be able to use the same underlying atom with a reconciler simultanously

bendlas14:05:58

you'd just have to be careful with normalization

peeja14:05:49

But do I have to om.next/add-root! instead of om.core/root?

peeja14:05:33

and then do I have to use om.next for every component that's an ancestor of the component I want to use Om Next for?

bendlas14:05:06

you'd have to keep the roots separate

peeja14:05:06

or can I have om next components inside om now components?

peeja14:05:15

Ah, yeah, that's my question

peeja14:05:23

So you can't use them in the same app

peeja14:05:32

that is, the same React app

bendlas14:05:47

if there is a way, I'm not aware of it

peeja14:05:02

Damn. That's going to make it almost impossible to upgrade.

bendlas14:05:28

but I'm not that deep into om.next, and based on current experiences, I think I'm on my way out

peeja14:05:04

Oh, interesting. What about om.next isn't working for your case?

bendlas14:05:38

I feel that its complexity budget is blown an it doesn't deliver

bendlas14:05:29

it expects to be used in a very specific way, with normalization vis a vis update semantics

bendlas14:05:11

and even that specific way isn't straightforward either

bendlas14:05:16

the parser functions promise flexibility in the underlying data format, but that flexibility doesn't realize with normalization

peeja14:05:28

Are you looking at something else in particular to move to?

bendlas14:05:52

just trying to figure that out

bendlas14:05:51

I've had a very pleasant surprise with datascript, so I might just use naked react over that, or maybe reagent

bendlas14:05:30

I've had plenty experience with om and it works ok, but cursors have always felt clunky

peeja14:05:33

Is your app purely client-side? The major strength I see in Om Next's approach is in moving the remote data fetching closer to the data store.

peeja14:05:59

I haven't seen anything else that tackles that problem well

bendlas14:05:34

well, that's the pleasant surprise I've had with datascript: so far nothing beats piping the transaction log into a datascript instance

bendlas14:05:03

sure, om.next's idea of doing on-demand fetching seems appealing ...

bendlas14:05:25

and I'd LOVE to be surprised

bendlas14:05:50

but until now, it just made me feel stupid 😞

anmonteiro14:05:53

@peeja: FWIW:

(defui Child
  static om.next/IQuery
  (query [this]
    [:dummy/key])
  Object
  (render [this]
    (let [{:keys [dummy/key]} (om.next/props this)]
      (dom/div nil (str "dummy key: " key)))))

(defn parent [state owner]
  (reify om.core/IRender
    (render [this]
      (dom/div nil "parent"
        ((om.next/factory Child) {:dummy/key "Test"})))))

(om.core parent {} {:target (js/document.getElementById "app")})

anmonteiro14:05:55

you can’t expect the dataflow to go through the parser

anmonteiro14:05:08

but you can definitely start moving your components to defui

peeja14:05:39

Yeah, I figured I could do that much at least. It's the parser I'm trying to work out… 😕

anmonteiro14:05:52

also, the IQuery implementation is just a placeholder until the next round of refactoring

peeja14:05:20

Oh, actually, I can just do it the other way around

peeja14:05:37

I can root with om.next and have it render old and new

anmonteiro14:05:40

that’s probably an option too

anmonteiro14:05:53

not sure about the cursor stuff though

anmonteiro14:05:59

but it probably works out

peeja14:05:14

Yeah, I'm just realizing: I'm not sure we ever use om.core/transact! 🙂

peeja14:05:17

so that may be fine

anmonteiro14:05:18

since cursors have map semantics, I think

peeja14:05:15

I'm pretty sure our app only swap!s the atom. Which is not great in an Om Now world, but actually maps pretty well to the Om Next world.

anmonteiro14:05:49

it only shows transact! for the instrumentation bit

anmonteiro14:05:14

(I’m assuming that’s the app you’re talking about)

peeja14:05:37

Thanks! 🙂

anmonteiro14:05:30

happy to help

bendlas14:05:01

@anmonteiro: any idea why recursive queries break with transactions?

anmonteiro14:05:20

looking into it

anmonteiro14:05:32

@bendlas: how should I run your gist?

anmonteiro14:05:56

I mean, which one is it, the composite?

bendlas14:05:05

it's your gist, with a dummy mutation handler

bendlas14:05:16

and an on-click handler that triggers it

anmonteiro14:05:28

I mean composite vs decorator

bendlas14:05:29

the mutation handler needs to swap! the atom

bendlas14:05:32

@anmonteiro: curiously, when triggering the transact! from outside, nothing happens, so my guess is that rerendering based on the query from the triggering component is broken. Also, force-root-render! doesn't restore the components when they're gone, so I'm not really sure what's that supposed to do ...

anmonteiro14:05:23

@bendlas: this might just not be a bug in Om Next, but in my example

anmonteiro14:05:34

the problem is really just the biggest square

anmonteiro14:05:52

if you stopPropagation of the click event almost everything works

anmonteiro14:05:02

(except the big square)

bendlas15:05:42

@anmonteiro: not sure about that. I'm trying to port a tree of folders and files to om.next and the bug already manifested before i had a parser for the tree nodes ...

bendlas15:05:39

@anmonteiro: but ack insofar, as i see the bug only on clicks on the outermost node

anmonteiro15:05:21

@bendlas: I don’t really have a lot of time but my suspicion is that the problem is related to full-query

anmonteiro15:05:37

whenever you do a transaction, the query that the parser receives might not be the exact same as the initial query

anmonteiro15:05:03

but instead it receives the full-query for the component, and you might need to handle that case differently in the parser

hlolli15:05:11

https://www.refheap.com/119799 I added an asynchronous http/get request in a send function of the reconciler, in a hope to be able to target html(this case svg) elements within componentDidMount. Still encounter the same problem as if this was not within :send, browser just returns app.cljs:97Uncaught TypeError: Cannot read property 'addEventListener' of null but adding (.addEventListener (gdom/getElement "node4") "mouseover" #(js/alert "hello númer 4")) to the repl, works fine. (sorry to distract the conversation currently active).

anmonteiro15:05:13

(if you wanna pursue the matter further)

bendlas15:05:15

@anmonteiro: well, it's either that or ditch om.next entirely .. but the project is already late, so I guess I might be better off using something else 😕

anmonteiro15:05:22

@bendlas: for recursive queries you might also be interested in path optimization

bendlas15:05:12

@anmonteiro: is that an actual optimization, or for getting it working?

anmonteiro15:05:23

important bits are: - L331-L333 - L396

isak15:05:39

@peeja: what i'm doing to have both om.next and om.now is to do it on a per route basis. So I keep track of which version of om is mounted, then unmount/mount according to which route i'm loading

bendlas15:05:42

@anmonteiro: ok, thanks. don't hesitate to ping me if you find out more about that click interaction

anmonteiro15:05:00

I’m not going to spend more time on it now

anmonteiro15:05:11

I’ll see if I have a bit later

mitchelkuijpers15:05:55

Thank you so much for your little rerouting example @iwankaramazow this makes me understand a lot more things

mitchelkuijpers15:05:58

Much appreciated

bendlas16:05:01

anmonteiro: catching up on that bug. your intuition was pretty good. it seems that full-query omits the join query for some reason. so instead of {:composite/item {:composite [:id :width ...]}}, it returns {:composite/item [:id :width ...]}

bendlas16:05:11

.. which in turn seems to be a bug in path

anmonteiro16:05:04

@bendlas: that’s exactly what I was talking about

anmonteiro16:05:09

also not a bug in path

anmonteiro16:05:47

path is about the data path, right? in a union all items are under the same vector, so they have the same data path

anmonteiro16:05:10

once the union branch has been decided upon, that key is not included in the path

bendlas16:05:37

you're right, i just tried removing :om-path from the metadata, but full-query still returns the wrong query

bendlas16:05:03

right now I'm in its guts to try to figure out what's wrong

anmonteiro16:05:27

@bendlas: well, probably your parsing code expects a query in the initial form but it doesn’t get that once you perform a transaction and full-query is fed

bendlas16:05:32

hm, full-query calls first on a hash-set, that can't be right

anmonteiro16:05:54

the bug is also not in full-query

anmonteiro16:05:08

AFAICT, there’s nothing wrong in Om Next code

anmonteiro16:05:14

you simple need to handle both cases differently

anmonteiro16:05:40

(probably can be solved with path optimization) — hence my suggestion that you look at this

bendlas16:05:44

yes, path opt looks like it would solve this, but shouldn't this be just an optimization?

anmonteiro16:05:58

you can also solve it without pathopt

bendlas16:05:20

look, in `(first (get-in @(-> component get-reconciler get-indexer) [:class-path->query (class-path component)]))` the argument is a hash-set

anmonteiro16:05:21

just need to account that your read method is going to get 2 types of queries

anmonteiro16:05:26

one that’s a map, other that’s not

bendlas16:05:58

no, i get the full-query in the parser, but in the rerender case, it's missing the intermediate join

anmonteiro16:05:18

can you paste the 2 different queries here?

anmonteiro16:05:21

so I can see what’s missing

bendlas16:05:31

no, you're right READ :tree/root nil (:query-root :path :pathopt :ast :state :parser :logger :shared :target :query) {:query-root :om.next/root, :path [], :pathopt false, :target :remote, :query {:file [:path {:prop [*]}], :folder [:path {:prop [*]} {:children ...}]}} vs READ :tree/root nil (:query-root :path :pathopt :ast :state :parser :logger :shared :target :query) {:query-root :om.next/root, :path [], :pathopt false, :target nil, :query [:path {:prop [*]} {:children ...}]} in the rerender case

anmonteiro16:05:49

so there’s nothing missing

bendlas16:05:56

so, ok, I get that I can fix it in the parser, but this should be handled differently by om.next, right?

anmonteiro16:05:14

this is an optimization already

anmonteiro16:05:35

so that the parser receives the query focused along the path that initiated the transaction

bendlas16:05:19

where does this fit in the query grammar?

bendlas16:05:18

IQuery/query is supposed to return a QueryRoot, except if it's a join?

bendlas16:05:40

so the parser has to handle that in the same way?

noonian16:05:07

Fwiw I think I ran into this over the weekend as well but haven’t had time to dig in. I do think there is a bug somewhere in Om though. My usecase was similar to the composite recursive union blog post and my component was getting empty props from Om (not passed in from parent) during re-render after a transaction.

bendlas16:05:07

@noonian: sounds like this, and if a query parser is supposed to handle QueryRoot as well as JoinExpr, like IQuery/query does, then it's really not an om.next bug, but in this case it really needs to be clarified

noonian16:05:00

Yeah I wouldn’t mind if its a bug in my code, but hard to know as it is 🙂

anmonteiro16:05:47

@bendlas: you’re conflating two things, this is definitely a valid query grammar

anmonteiro16:05:32

what is happening is that following a mutation, you don’t need to re-render the whole component hierarchy

anmonteiro16:05:54

but instead, only the sub-tree that changed. the root of such sub-tree is the component that initiated the transaction

anmonteiro16:05:28

full-query is meant to be the initial query, focused along the path of that component

anmonteiro16:05:41

and the new props are fed directly to that component, and not from the root

bendlas16:05:17

yeah, ok, but grammar-wise what is the :query key in the env supposed to be, and if it's not strictly a QueryRoot, then is it not at least supposed to be somewhat "stable"

bendlas16:05:53

might be that I'm conflating, if your measure is the current implementation of om.next, but I've now sunk multiple days into getting a grasp of this thing and it's not getting easier ..

bendlas16:05:08

so .. I'm thinking ease of learning

anmonteiro16:05:43

@bendlas: so the parser dispatches on the top-level keys of your query if your root query is [:foo {:bar [:quux]}] then the parser will run multimethods for :foo, with query set to nil, and :bar with query set to [:quux]

anmonteiro16:05:36

no one ever said that Om Next was easy to learn, and it's definitely not

bendlas16:05:59

yeah, but we should make it easier

anmonteiro17:05:09

and you might need more than a few days to actually understand it. especially given that there is just not sufficient enough documentation

anmonteiro17:05:39

Making it easier to learn is definitely a priority, but one that takes time

bendlas17:05:32

in this case, I'm thinking maybe the :query input for parser functions should stick to QueryRoot and in the case of join queries, there could be an extra :env key

bendlas17:05:44

or something along that lines

bendlas17:05:58

but rewind a step, according to current impl, what is IQuery/query supposed to return (`QueryRoot | QueryJoin` or QueryRoot | QueryExpr) and what are parser functions supposed to handle in their :query env key?

bendlas17:05:37

@anmonteiro: oh, so IQuery/query can return QueryRoot | UnionExpr and the parser has to handle accordingly, is that right?

bendlas17:05:00

@anmonteiro: i suppose the grammar would also allow for a RecurExpr to land in the parser, but I'd be pretty baffled if that worked right now ...

bendlas17:05:22

all that is under the assumption that the :query key in the parser is always the RHS of a JointExpr, if that's the source, otherwise nil is that true?

bendlas17:05:40

@anmonteiro: so, I tried now to handle a map query aswell as a vectory query. To no avail. The failure is slightly different though: now the component doesn't get rerendered with an empty map, but with a map wrapped by the root key.

bendlas17:05:54

I also tried to set :pathopt, to no avail

bendlas17:05:00

slightly pointed question: is anybody besides you and @dnolen able to hold this thing in their head and take charge for it?

dnolen17:05:36

@bendlas: people figure it out and generally stop asking questions 🙂

dnolen17:05:24

but I can’t really follow along with your problem

dnolen17:05:32

the backlog is just all over the place

dnolen17:05:40

instead of actually being focused on something specific

bendlas17:05:28

@dnolen: right now, I'm trying to use recursive components to render a tree of files and folders

dnolen17:05:57

@bendlas have you looked at the recursive examples in the repo as a starting point?

bendlas17:05:40

I looked at anmonteiro 's blog post

dnolen17:05:53

so probably worth looking at a second example

bendlas17:05:59

i'm also looking at the devcards

dnolen17:05:01

the devcards has a simple recursive example

dnolen17:05:40

it also has a :pathopt case in there if I recall

dnolen17:05:52

so I would start with that

dnolen17:05:57

and modify it until it works

dnolen18:05:05

and only ask questions about some simple thing that isn’t working as you hit

dnolen18:05:19

will be much less frustrating than digging into the grammar and all that

dnolen18:05:40

it’s likely that om.next will just adopt ClojureScript 1.9 to get cljs.spec

bendlas18:05:41

I mean, I'd like to skip :pathopt because i understand it even less than the rest of the parser infrastructure, but if I have to rely on an undocumented hack to get something working, so be it ...

dnolen18:05:59

and we’ll just have a sensible way to blow up when people do the wrong thing

bendlas18:05:20

yep, looking forward to that

dnolen18:05:35

@bendlas: you don’t need :pathopt

dnolen18:05:46

it’s just that recursive queries can be slow for the obvious reasons

dnolen18:05:52

:pathopt is the escape hatch

bendlas18:05:12

sure, but right now they don't work at all, on rerender

dnolen18:05:24

right so stop grabbing at straws

dnolen18:05:38

just make the smallest example that defies your expectations

bendlas18:05:31

I'm pretty much there, I'll post it into a gist ...

dnolen18:05:54

cool, much easier to look at a tiny example and sort through what’s wrong

bendlas18:05:35

@dnolen: https://gist.github.com/bendlas/6a94f7b5d4d9e3fd30bcda5bc96b2e68 when clicking a node, its data disappears because the rerender fails.

bendlas18:05:00

anmonteiro advised me to the condition at L120 where the query passed to the parser is already focussed in the rerender case

bendlas18:05:11

but with that condition, the rerender is wrapped by a root key, also the data goes into the component denormalized (other than in the initial render)

anmonteiro18:05:44

@bendlas: if your query is not a map you might just want to call db->tree. I’m hopeful that would solve the issue

dnolen18:05:48

@bendlas yeah curious why you need hand-rolled recursive parse call here

dnolen18:05:05

we’ve gone out of our way to make that unnecessary most of the time

noonian18:05:55

I believe I’m encountering the same issue with db->tree and no recursive parse. I’m at work atm though and cannot dig deeper right now, but I don’t think it will be too hard to come up with a failing test.

bendlas18:05:24

@dnolen: that's lifted from the blog, without it normalized data would slip into sub components, maybe that can be solved with a db->tree aswell

dnolen18:05:01

you shouldn’t care about what data flows through your components anyway

dnolen18:05:11

just that you get the ones that you care about

bendlas18:05:37

@dnolen: sure, I'll try to reduce it further to what I'd originally have expected. Right now it follows the pattern from anmonteiro's blog

bendlas18:05:01

but then, why would I need any parser functions, other than the :default at all?

dnolen18:05:25

@bendlas: you might not, it just depends on how you intend to structure you app

dnolen18:05:36

:parser is pluggable for a reason

dnolen18:05:53

the whole multimethod thing is just a convention that works simply enough with the helpers we provide

bendlas18:05:51

sure, I started with trying to map my own data format onto the app structure via the parser, but that didn't really work out, as soon as I needed to do cross - component updates

bendlas18:05:10

I mean, it could have worked if I took the hit of adding read keys to transacts! but what's the point of that

dnolen18:05:31

if you’re not adding read keys to transact! you’re swimming upstream

dnolen18:05:47

there’s no intention of making that any different than it is

dnolen18:05:58

and if you don’t follow the rules then you’re on your own with whatever complications it brings

bendlas18:05:30

couldn't the read keys at least be added by the mutation-fns?

dnolen18:05:03

add the read keys to your transaction - end of story

bendlas18:05:28

so I'd need to pass wrapped transact!s down the render tree?

dnolen18:05:42

I do not know what you are talking about now

bendlas18:05:17

well, component X doesn't want to add read keys for its sibling component Y

bendlas18:05:03

like any read key added by a component could just as well be in its query expression, encapsulation-wise

dnolen18:05:07

OK so you’re missing one of the om.next ideas

dnolen18:05:13

who cares about components X & Y

dnolen18:05:16

it’s irrelevant

dnolen18:05:39

if X & Y are related somehow then you assign them idents

dnolen18:05:06

only a parent can know X & Y are connected

dnolen18:05:22

so parent A handles the transaction, adding the idents as the read keys

bendlas18:05:28

I went down that road , but it gets pretty hairy in the recursive case

bendlas18:05:49

ok, I might revisit that

dnolen18:05:20

I’m more than happy to listen what you were struggling with in the recursive case here

dnolen18:05:58

:pathopt lets you start the query anywhere

dnolen18:05:09

that might not have been clear

dnolen18:05:20

like at any ident in the graph

dnolen18:05:44

so you don’t need wacky recursive parsing for this case

bendlas18:05:21

basically my tree was {:props {..} "A" {:props {..} "AA" {..} "AB" {..}} "B" {:props {..} "BA" {..}}}, right? So my idents would be the pathes, that I would need to construct while walking down.

bendlas18:05:51

at the same time, my display area would also have an ident of [:path ["A" "AB"]] or something

dnolen18:05:25

I don’t know why you need to do it that way

dnolen18:05:35

if you’re saving this in a DB it would [:node ID]

bendlas18:05:52

I didn't need to, so i rewrote my data intake

dnolen18:05:53

anything else sounds more complicated to me

bendlas19:05:12

but the natural id would be the path, the data source being a webdav response

bendlas19:05:02

anyway, I got it to render, but then ran into pretty much the same re-rendering issues on transacts as now

dnolen19:05:19

yeah I don’t know about the path thing

dnolen19:05:25

you may have even hit a bug

dnolen19:05:31

idents should be [keyword scalar]

dnolen19:05:04

like Datomic lookup refs

bendlas19:05:29

well, using the path as id is, what i do even now, in my posted example

bendlas19:05:44

I'll quickly try a gensym or something

dnolen19:05:59

so I don’t know if there is a bug here in om.next but basically that’s very likely to not be allowed

dnolen19:05:06

the second element of ident should not be a collection

bendlas19:05:36

well, collections do have value semantics if you treat them opaque ... clojure allows them as map keys

dnolen19:05:46

sure but it doesn’t matter

dnolen19:05:50

I’ve explained what I intended

dnolen19:05:21

^ the loose spec above is easier to read than the grammar stuff IMO

dnolen19:05:33

and it’s pretty clear sketch on what will and won’t be allowed

bendlas19:05:04

but no, scalar key doesn't make a difference

dnolen19:05:05

@bendlas that’s good to know, the ident support stuff is pretty tricky

dnolen19:05:02

@bendlas: I can’t dig into just now, but I’ll take a look later - I don’t think it will be so hard to make your thing work - I’ll jot some notes and hopefully that can clear some things up

bendlas19:05:30

sure, I'll keep digging into the devcards ...

bendlas19:05:42

just one thing, I'd really don't get:

dnolen19:05:31

it’s also strange to me you’re not just normalizing here

bendlas19:05:35

when you say transactors have to add appropriate read-keys, why have the mutator abstraction at all, when mutators must know which keys will be modified?

dnolen19:05:43

if you did that you could just let your queries work for you via db->tree

bendlas19:05:04

I am normalizing

dnolen19:05:16

oh sorry you are

dnolen19:05:19

mislead by comments

bendlas19:05:43

yeah, the option was there from when i had an atom

dnolen19:05:46

@bendlas: people ask that question quite a bit - we just copied Relay & Falcor here

dnolen19:05:09

the rationale is entirely about clients being able to control what they re-read rather than servers sending back random stuff

iwankaramazow19:05:56

@bendlas: also the component which initiates the transact, automatically re-reads

bendlas19:05:21

i see ... but that would be at the :remotes border in the reconciler, no?

dnolen19:05:44

you’re writing the UI you know which idents are related

dnolen19:05:54

you don’t care who updates, as long as they match those idents

dnolen19:05:04

so instead of wiring a heaping mess of event listeners

dnolen19:05:08

figure out your idents

dnolen19:05:14

add those keys to the transaction

dnolen19:05:18

get a cup of coffee

bendlas19:05:49

sure, when thinking in terms of idents, adding the read keys makes a bit more sense, but then the transaction itself should be targeted at idents as well

bendlas19:05:03

.. which it could be

dnolen19:05:06

it just depends

dnolen19:05:19

idents are kind of key you might want to re-read

dnolen19:05:51

the bigger idea is just decoupling re-renders from talking about which components need to update

dnolen19:05:57

pick semantically meaningful keys and stop worrying about it

bendlas19:05:53

I dunno, maybe it'll grow on me ...

bendlas19:05:18

I'll be sure to write that tidbit about relay and falcor into the FAQ

dnolen19:05:25

I make no claims that’s it’s for everyone

dnolen19:05:43

it’s just about the kind of programs I’m interested in writing after having tried many other things

bendlas19:05:29

regardless, I'd like to see you write the kind of programs that are for everyone, even if they don't know it yet

dnolen19:05:05

ha! I probably should have written om.next in JavaScript then 🙂

bendlas19:05:22

not in my book 🙂

bendlas19:05:11

@dnolen: I couldn't find any references to the falcor/relay rationale you mentioned, but I've put what I understood from you into the FAQ

dnolen19:05:45

you may need to watch the videos

dnolen19:05:59

the docs may just say you have to supply what you want to re-read

bendlas19:05:28

I'll do that, the next time i want to slack off. In the meantime, I'll continue to try to find holes in om.next, that you'll actually acknowledge 😉

hawkey19:05:36

hi, is there any common way how to do animations in om?

selfsame19:05:51

speaking of :pathopt, show and tell time - http://selfsamegames.com/p/4/ (needs a webkit browser)

bendlas19:05:05

@selfsame: core.js:219 Uncaught TypeError: Cannot read property 'init' of undefined

bendlas19:05:20

in current chromium stable

selfsame19:05:36

@bendlas hmm probably a figwheel dependency my bad

bendlas19:05:00

@hawkey: you're probably best off, if you can do it with CSS transitions

selfsame19:05:45

@bendlas think it's because of an adblocker (ublock maybe)

bendlas19:05:04

yeah, tracking.js is blocked

bendlas19:05:13

also it tries to connect to figwheel

selfsame19:05:44

haha that has to be a semantic thing, tracking.js is just some canvas drawing code

bendlas19:05:04

right 🙂, works now

bendlas19:05:33

impressive!

bendlas19:05:58

so there is a way to do recursive components in om.next

bendlas19:05:57

when i decrease height, the menubar and the lower breadcrumb bar get cut down

selfsame19:05:58

thanks! heh not actually using recursive reads for the outliner but it would have been the same performance because I use idents in transact

selfsame20:05:12

@dnolen thanks! performance is way up from the om.prev version 🙂

whistlerbrk21:05:02

Hi, (still) new to CLJ/CLJS/om.next; I’m having trouble finding an example of a read method which uses its params; I’m a bit lost in the woods so I’ll explain in further detail what I’m doing as it may just be the wrong approach

whistlerbrk21:05:01

I have my initial app-state which has three main portions, one is structure is a map of attributes and a vector of children. I recursively render out my structure using this, which is working.

whistlerbrk21:05:29

When I read a node w/o children, I would like to look for data, so I call a factory “leaf” passing it the current node’s id. I’d like to then use that parent-id to do something like: (parser {:state app-state} ‘[[:data/by-parent-id 3]])

whistlerbrk21:05:54

where 3 is, for example, whatever was passed in… as you can see I’m trying and failing to do this just from the REPL

whistlerbrk21:05:12

my read function looks like:

whistlerbrk21:05:18

(defmethod read :data/by-parent-id
  [{:keys [state] :as env} key id]
  (let [st @state]
    (if-let [[_ k] (find st :data/by-parent-id)]

      (if-let [[v] (get k id)]
        {:value v}
        {:value :not-found}
      )
      {:value :not-found})))

whistlerbrk21:05:42

so if I manually switch id in the (get k id) statement to 3 I’ll get my data back, but I can’t seem to use the param I’m trying to pass in

whistlerbrk21:05:45

Maybe I’m just struggling with the destructuring syntax...

selfsame21:05:16

@whistlerbrk: try using {:id 3} as the params and destructure for :id

whistlerbrk21:05:58

@selfsame: meaning: (parser {:state app-state} '[[:data/by-parent-id {:id 3}]]) and changing the read method to be:

(defmethod read :data/by-parent-id
  [{:keys [state] :as env} key {:keys [id]}]
  (let [st @state]
    (if-let [[_ k] (find st :data/by-parent-id)]

      (if-let [[v] (get k id)]
        {:value v}
        {:value :not-found}
      )
      {:value :not-found})))
? (sorry very new to the whole ecosystem)

selfsame21:05:40

so the read args are [env key params]

hlolli21:05:01

@whistlerbrk: can you post your code to refheap. If you specify a read but don't get it in your component, it could be the static query that is missing.

whistlerbrk21:05:44

@hlolli: yes, I’m going to make a reduced example

selfsame21:05:27

@whistlerbrk: the params arg is for IQueryParams or queries with the list syntax like (:foo {:params :etc}

selfsame21:05:15

think you're wanting something in (:query env) or (:ast env)

whistlerbrk21:05:34

@selfsame in my defui for Leaf or my read function for that key :data/by-parent-id ?

selfsame21:05:51

the read function

whistlerbrk21:05:52

@selfsame: sorry, I’m not following completely, so are you referring to library functions like om.next/query->ast ?

whistlerbrk21:05:52

you have the gist of what I’m trying to do on L90, that is once I hit a leaf, get the data by passing the parent-id

selfsame22:05:53

yeah the env in read will have :ast, I do see what you're trying to do. I think maybe you can get by without custom read, try (parser {:state app-state} '[{[:data/by-parent-id 3] [*]}]) ?

selfsame22:05:23

if not, try a pprint on (:ast env) it should have all the things you'd need to do what you want 🙂

whistlerbrk22:05:36

@selfsame correct me if I’m wrong, there is no default read function though, right, I must supply a read function here though

whistlerbrk22:05:01

and my current read function is faulty

selfsame22:05:02

yes, sorry - was assuming you have a default read using om.next/db->tree

whistlerbrk22:05:26

@selfsame: I could do that, I was trying to comply with the default db

selfsame22:05:56

@whistlerbrk: yeah sure - best way to learn, I guess the take away is you were confusing what the params arg is, and that env has the info you need to roll your own read methods in :query and :ast

whistlerbrk22:05:48

@selfsame thanks, I’m going to try and find more examples which use om.next/db->tree I actually haven’t found a ton yet

whistlerbrk22:05:03

so this would be valid in my case: (om.next/db->tree [:template/name] app-state reconciler)

whistlerbrk22:05:52

“Given a query expression, some data in the default database format, some application state data in the default database format"

selfsame22:05:15

(let [{:keys [query ast state]} env] {:value (om/db->tree query @state @state)})

selfsame22:05:03

^that's all you need for generic queries

bendlas22:05:10

can I ident based on something generated in a custom read?

selfsame22:05:30

@bendlas I haven't had good luck using synthetic Idents

bendlas22:05:37

me neither ^^

selfsame22:05:31

but components can share an ident, if that helps

bendlas22:05:51

do mean in the sense of [:ident (Object.)]?

selfsame22:05:42

meaning [:menu 0] with {:menu {0 {}}} in the app state, multiple components can have that ident

bendlas22:05:45

it's just, when i have data that hasn't any good natural idents, i'll have to come up with something

bendlas22:05:14

for the application, that you just posted, you probably gensymed ids, no?

bendlas22:05:22

synthetic idents from custom reads would mean, that i could key them on their path into the data structure but that seems hairy

selfsame22:05:13

@bendlas yeah you would have to write your own denormalize* fn for that I believe

selfsame22:05:58

(which I tried out and was pretty fun..)

bendlas22:05:15

right ^^ ... not going there just yet

selfsame22:05:29

but now I just make om.next normalized data and only use a single db->tree read and everything just works

bendlas22:05:24

thanks, good hint, that

bendlas22:05:34

relying on automated (de)normalization seemed pretty finicky, for the short time I've played with it

selfsame22:05:58

hmm honestly I've never used that feature

noonian23:05:29

I suspect this issue may be what I’m running into and the union and recursive stuff is just making it seem more complex: https://github.com/omcljs/om/issues/604