Fork me on GitHub
#om
<
2015-11-10
>
jannis01:11:40

@dnolen: It's not pretty but here's a (probably buggy) hack I came up with for the transact! reads thing we discussed earlier: https://gist.github.com/Jannis/66ebbcd791ad9b6e24a4 - just in general, was this the kind of "automation" (assuming it was built into transact!) you were thinking of or is this a really bad idea?

dnolen01:11:23

@jannis heh no, I didn’t really have anything in mind yet

dnolen01:11:27

need to think about it some more

jannis02:11:11

Sure. simple_smile I couldn't sleep thinking of it, so I hacked it up.

jannis02:11:49

I can already see a problem with it - parameter substitution. Although that could be solved using bind-query. Whatever, take your time with it all, I just couldn't wait trying something. simple_smile

jannis02:11:03

Actually, parameter substition seems to happen at a later stage, so it's already working with that hack. Sweet!

dnolen02:11:15

Definitely a fine line that needs to walked here between convenience and something that’s straightforward to reason about.

mike06:11:05

I keep bumping to om reviews that it's complicated.

mike06:11:21

I'm trying to choose between om and reagent.

mike06:11:35

what are the benefits of om?

dvcrn06:11:57

hi, short question: how can I execute something after my component rendered?

dvcrn06:11:24

didMount only works once it renders initially. willReceiveProps happens before the render

dvcrn06:11:30

@mike it depends. you probably mean old om. New om is something completely different and has close to nothing in common with reagent. It's more a hybrid of react + falcor + graphql

dvcrn06:11:05

I recommend watching dnolens talk from euroclojure (https://www.youtube.com/watch?v=ByNs9TG30E8) and reading through https://github.com/omcljs/om/wiki/Quick-Start-%28om.next%29 if you're interested

mike06:11:08

@dvcrn: do I have to use graphql?

dvcrn06:11:34

it is not literally graphql, just the idea behind it. It changes the way your components query your (local or remote) app state

mike06:11:56

how production ready is it?

mike07:11:02

any chance of future backward incompatibility, is it's happening now?

dvcrn07:11:32

next is still apha but I believe a beta is coming soon (correct me if I'm wrong @dnolen). I recommend reading through the resources and decide based on that if you need this approach. If you think that's a overkill for what you are trying to do, use reagent

dvcrn07:11:00

Backwards compatibility I don't know, but it will be very future proof simple_smile

mike07:11:12

does om wrap react.js, or does it implement react?

mike07:11:21

does om directly leverage closure library, or just indirectly through clojurescript?

dvcrn07:11:52

that I don't know. I think indirectly but don't want to say anything wrong

mike07:11:13

thanks a lot dvcrn!

mike07:11:03

thought by escaping javascript jungle and refuging to clojurescript I don't have to make difficult decisions anymore. turned out I'm wrong. there is om, om next, reagent + re-frame,.. and the whole javascript jungle growing here too simple_smile

dvcrn07:11:32

it's a more beautiful jungle 😉

dvcrn07:11:40

but yeah, there are a lot of react wrappers out there

dvcrn07:11:27

found it. componentDidUpdate what was I was looking for

mike07:11:52

did you guys know that slack is built with jquery?

mike07:11:48

dvcrn: what' the difference between good.dom and om.dom?

dvcrn10:11:38

little self advertisement but my minimalistic markdown editor in om next just managed to become the highest trending clojure project for this week 😛 https://github.com/trending?l=clojure&amp;since=weekly

dnolen12:11:48

@mike directly, it’s pretty unusual to see a ClojureScript library that doesn’t use some functionality from GCL

dnolen12:11:50

@mike assessing things on whether something does or doesn’t look “complicated” is rarely a good idea.

dnolen12:11:13

Either you see that it reduces complexity for the kinds of systems that you have built in the past or you cannot. If you cannot it’s really not a big deal … people are effective with all kinds of software systems.

txus14:11:43

I’m seeing a problem in advanced compilation with om next (using 1.0.0-alpha19, cljs -170):

#error {:message "No queries exist for component path (#object[RA \"function RA() {React.Component.apply(this,arguments);this.state=null!=this.ra?this.ra():{};return this;}\"])", :data {:type :om.next/no-queries}} 

txus14:11:03

it happens when doing om/transact!

txus14:11:10

only in advanced compilation

txus14:11:25

anyone has encountered this?

dnolen14:11:37

@txus not that people have tried advanced so you’re like to encounter some issues

txus14:11:53

hm better stick to simple then right?

dnolen14:11:23

better to file a report identifying the problem

dnolen14:11:46

otherwise it will never get fixed because I won’t know to look for

dnolen14:11:07

which is all to say, I thought I’d found all the cases but obviously I haven’t simple_smile

txus14:11:48

no problem simple_smile I’ll try to make a small reproducible snippet

txus14:11:02

issues go in GitHub right?

dnolen14:11:05

thanks! yes please

dnolen16:11:39

this is a good talk to review in the context of Om Next https://www.youtube.com/watch?v=ROor6_NGIWU

kovasb19:11:13

So we are having some confusion about putting a list component inside another component

kovasb19:11:34

We just want to get the list elements and pass them to the child

kovasb19:11:52

doing (query [this] (get-query ListComponent)))

kovasb19:11:55

doesn't work

kovasb19:11:25

does one need to invent an intermediary key (doesn't feel right)?

dnolen19:11:16

you cannot do that

squeedee19:11:22

but why! 😄

squeedee19:11:33

Actually somewhat apparent. But also painful.

dnolen19:11:49

not if you think about it as long as I have

squeedee19:11:03

I dont want to go through that, you know this 😄

dnolen19:11:23

so don’t do it and eventually it’ll become clear why it’s undesirable

kovasb19:11:52

Right now we are just adding a read method and recurses to the target key but seems brittle

dnolen19:11:28

the rules are very very very simple

dnolen19:11:36

do not try to guess what they are

dnolen19:11:56

1) every component that wants to query must have it’s OWN query and it MUST be a vector

dnolen19:11:09

2) the only ONLY exception is UNIONs

dnolen19:11:22

now no more guessing

kovasb19:11:46

Right, mostly just wondering if the implemention idea (invent an intermediary key) is the correct approach

dnolen19:11:17

“correct"

dnolen19:11:34

if you work out what has to happen if you don’t enforce these rules you see there are many more problems

squeedee19:11:08

it wasn’t that we wanted to buck the rules. I think there are just ‘react components’ that have no need for queries.

squeedee19:11:19

but they are rendered along the way.

dnolen19:11:25

then you don’t need queries

dnolen19:11:33

and you don’t need to invent keys

dnolen19:11:38

and you shouldn’t be colocating

dnolen19:11:58

again go back to 1) and 2)

dnolen19:11:01

read them over again

dnolen19:11:54

3) if you are writing a pure component you should NOT be considering 1) or 2)

kovasb19:11:13

ok i think we got it

kovasb19:11:27

so the list component is now not having its own iquery

kovasb19:11:32

just moved it up a level

tony.kay22:11:57

With regard to remotes and parsing: I'm working on a nested set of components where remotes are involved. As I walk the query in read, I'd like to call the parser recursively. This works fine with locals, since I can return {:value (parser ...)}; however, the return value of the parser on a target is a query, so I cannot exactly return { :remote (parser ... :remote) } because that is supposed to be an AST. I can use expr->ast, but then I end up with a vector of vectors (because the outer parser wants to wrap the elements of the parsed selector in a vector to make it a query). I can dance around the data structures and make it work, but all this seems kinda messy, and I'm wondering if the story is still evolving, and I should just not be trying this particular thing yet. Or perhaps I've missed a simple trick.

dnolen22:11:47

@tony.kay: ok so the problem here is that ast parsing doesn’t compose simply like parse does

jdubie22:11:56

i’ve also ran into this @tony.kay. i ended up using gather-sends. which i’m not sure is public

dnolen22:11:15

@tony.kay: it seems like there are two parts to this

dnolen22:11:25

1) thread :target via env

dnolen22:11:31

so you don’t have to do this at every level

dnolen22:11:43

2) defer ast->expr till the very end

tony.kay22:11:16

er. expr->ast?

dnolen22:11:29

no ast->expr

dnolen22:11:47

I don’t think you want to work on the raw expr

tony.kay22:11:10

OH...so don't RUN the parser in remote mode at all?

dnolen22:11:23

it’s too gnarly for stuff like parameterized joins

dnolen22:11:37

not suggesting that

dnolen22:11:46

just trying to see if 1) & 2) give you want you want

dnolen22:11:03

but I think I’m missing something here

tony.kay22:11:09

yeah, the AST is expected

tony.kay22:11:11

in the output

dnolen22:11:29

so why can't you do stuff like this?

dnolen22:11:41

(let [ast (exp->ast selector)] {:remote (update-in ast [:sel] #(parse env % :remote))})

tony.kay22:11:48

that might work...I see the general idea. Yeah, I was trying to post-convert the returned query instead of that.

dnolen22:11:32

right I think this should work and it supports recursion pretty much as well as the value oriented stuff

tony.kay22:11:45

I'll give it a shot and let you know if there is a problem

dnolen22:11:48

@tony.kay: no problem, happy to know if how it works isn’t satisfactory for some reason

dnolen22:11:55

but I think it should be sufficiently flexible

dnolen22:11:04

for people who want to use recursive calls to parse and for those people who do not

tony.kay22:11:03

I'm not sure how you'd do an app of any size without recursive parse...if you have any nesting where the top-level components are all local, and then you have nested components that need to hit a server, well, you have to compose the remote stuff to root...how would you do it without recursion (well, other than walking the sub-query manually...but isn't that a recursive parse you've just written manually???)

dnolen22:11:59

or DataScript

dnolen22:11:01

you bottom out fast

tony.kay22:11:35

I am, and you do, but there are still a couple of level to go through, and recursion seems the natural choice to get to the part that is "ready to send"

tony.kay22:11:02

but yeah, I don't have any suggestions for making it simpler...I just wasn't seeing the algorithm, and was making sure you didn't have further dev you were going to do in this area that would make is easier somehow.

tony.kay22:11:19

(about to write a significant amount of code simple_smile )

dnolen23:11:06

another thing to consider is just how much nesting is going on

tony.kay23:11:06

I think it wasn't very evident to me (and evidently others like @jdubie) because I was thinking of the AST as an opaque thing...an implementation detail to be passed around, but not messed with, so I was trying to convert it after the fact using a known function

dnolen23:11:14

while I think recursive queries are cool

dnolen23:11:30

I’m not so convinced that queries should be ever be more than 4 or 5 levels deep

tony.kay23:11:52

I agree with both statements

dnolen23:11:26

and I’m also not yet convinced that helper functions don’t help with the cases where you have to get through the first couple of levels

dnolen23:11:40

@tony.kay: still I’m too heads down to know what people are trying

dnolen23:11:22

so I could very well be wrong about this and people should show simple examples based on real world things that reveal things require lots of effort

dnolen23:11:42

so far from what I’ve seen a lot of effort in Om usually means 5 lines of code

dnolen23:11:47

but I dunno, maybe that’s a lot!

tony.kay23:11:07

in clojure: it depends on the 5 lines simple_smile

noonian23:11:00

I ran into the need for recursive queries immediately. Basically anytime a component wants to render a subcomponent that wants to query for root keys (doesn’t know its a sub-component).

tony.kay23:11:38

so far, I'm seeing a recursive use of simple read/parser calls as the simplest way to walk down a nested query that contains a mix of local/remote stuff...but things like specter also seem like possible contenders...nothing prevents me from transforming the nested query at the "top"

noonian23:11:59

but I’ve since been trying to think of the queries more as requesting what data you need and using extra parse functions to do the plumbing and not care as much what the real underlying data structure looks like in my components

dnolen23:11:19

@tony.kay: yes don’t let let my examples lead you astray

tony.kay23:11:32

I still lean towards recursion, because most people get recursive parsers pretty readily.

dnolen23:11:46

recursive parse should work, but it may very well be that other methods are better suited for different kinds of apps

noonian23:11:01

I ran into issues with normalization when using recursion in the parser

dnolen23:11:33

@noonian: there shouldn’t be any issues with that far as I know

tony.kay23:11:44

right, I've not seen normalization issues

dnolen23:11:33

@noonian the root values problem is a different thing from queries. This is why I fixed up shared.

dnolen23:11:47

if you need to push user info, internationalization etc. around don’t do that with queries

noonian23:11:31

how does shared fix the root values problem when the values are dynamic?

dnolen23:11:50

@noonian: so that’s a “it just depends"

dnolen23:11:06

for global user state etc. shared is fine, just re-render the root

dnolen23:11:32

more concrete examples of what you’re trying to do would be useful here

noonian23:11:35

For instance, if I have value :msg in my state that shows notifications to the user from different parts of the app, so many components want access to it to render it.

noonian23:11:11

not a great example since it would probably be the top-level components responsibility to render the msg but thats the type of thing I’ve ran into

dnolen23:11:35

right easier to understand if you give a real example

dnolen23:11:49

I’m having a hard time imagining replicating global dynamic information into each component

noonian23:11:17

My usecase for recursive queries was that I was trying to make a “layout” component that renders a different sub-component based on the app state (using query mutations to change its query dynamically). This is a much more complex example but in this case I desired that all of the sub-components be unaware that they were sub-components and that the “layout” component not require special knowledge about the subcomponents. So a different motivation for wanting recursion but I think the implementation issue is the same as with the :msg example.

noonian23:11:16

I’ve kind of tabled that experiment for now though and am just using a giant union of the queries at the top level. I saw some discussion in here about maybe adding delayed queries or something in the future and I think that is trying to solve the same problem as I was.

noonian23:11:42

anyway I’m just mentioning this because it sounded like you wanted to hear more use cases

dnolen23:11:01

@noonian that’s an interesting sounding example but hard for me to understand the issues without knowing more details about you actually tried and how you went about it

dnolen23:11:21

at this stage it still difficult to know if people are getting stuck on how to reformulate the problem - or a limitation of what’s there

noonian23:11:56

I also found myself using params in place of state for the component since I had to mutate the query at the same time as change the state but also have access to the (initial) state in my static query impl.

dnolen23:11:43

yeah this is a bit too abstract for me.

dnolen23:11:52

much easier to look at some small code example that shows what you tried

chris-andrews23:11:37

Is it allowed in om.next for a component to return multiple idents?

chris-andrews23:11:14

I think it probably doesn’t make sense, but I have been thinking about how to have multiple pieces of normalized data

dnolen23:11:56

you have unions if you need something to represent multiple types of things

chris-andrews23:11:32

Ok, follow-up, is it a bad idea to use two subcomponents that each have an ident, and then have their parent component utilize their data? I think I can actually work around this, but some things like WebGL can be a little tricky to break down into components, but the ident is really useful for data normalization

chris-andrews23:11:25

So essentially, each child component’s query gets used, but they just return nil in their render function, and the parent component actually uses their data because it has access to the WebGL context

dnolen23:11:43

@chris-andrews: this also doesn’t make sense to me

dnolen23:11:49

why do you need these child components at all then?

chris-andrews23:11:00

Simply because their query enables data normalization via Ident. The whole scene needs certain data, but a parent component can’t have multiple Idents, so that seemed like an option to me for working around that

chris-andrews23:11:59

I think it’s probably not a well thought out question, and it’s not a big deal. Mainly was just wondering if the mapping definitely needed to be 1 component = 1 Ident

dnolen23:11:09

just not following the logic here and I don’t see how WebGL is involved

dnolen23:11:32

instantiated components can only have 1 ident

dnolen23:11:45

N instantiated components can have the same ident

chris-andrews23:11:42

Ok, thanks for clearing that up a little. The only reason WebGL is involved is because in the past it has been a little tricky for me to render children to a scene, because you need the parent context, but that’s really not very difficult to work around

dnolen23:11:27

landed tempid support, just needs a bit more testing

dnolen23:11:18

pretty excited about it, you should be able to build up graphs of objects that don’t have remote permanence yet (and their corresponding UI elements) and batch a transaction

dnolen23:11:48

if there are tempids in the remote transaction result then the temporary ids and UI bits will automatically be migrated to the new information

dnolen23:11:39

removes a giant complexity hairball from the kinds of things you see people do in more sophisticated client apps

dnolen23:11:55

where users get to manipulate even moderately complex temporary relationships

dnolen23:11:35

the only thing left to do is cache eviction and I have a pretty good idea how to keep it dead simple for most applications (nothing for users to do)

dnolen23:11:51

this means we’re getting really, really close to beta

dnolen23:11:12

if there’s anything people would like to see changed, enhanced or tweaked now is a good time to speak up.

dnolen23:11:49

Also simple complete examples of conceptual difficulties as we’ve already discussed today are welcome - they will help determine if there are any real remaining gaps

jannis23:11:57

Cool. I already have an app that creates new temporary objects and sends them to the remote to create for real. I'll try to test the tempid stuff tomorrow.

dnolen23:11:10

@jannis cool please do, gonna give it a spin in my om-next-demo