Fork me on GitHub
#om
<
2016-02-16
>
geraldodev00:02:40

It's a nested query, that is updating it's params. As you see its params are supposed to be binded with the qry. the fn is right, offset 6 is the next one, that is supposed to be fetched from the server, problem is that this is not updating the components query.

geraldodev00:02:50

ClienteGrid composes with CreditoApp, so I thought that putting the root key :grid on the re-read list would trigger an update of :grid an since ClienteGrid's query is part of root app query, it would be updated.

cjmurphy01:02:39

'Even with key :grid on the list of values that must be re-read' - I believe this is just for documentation purposes.

cjmurphy01:02:43

The important for re-reading is in the call om/transact! call itself.

geraldodev01:02:28

@cjmurphy: I've discovered today that set-query has a 3rd parameter which is the read keys. update-query! which I'm using does not expose this lists. That's why I've used {:value {:keys [:grid]}}, and I'm checking here, I've also hardcoded the :grid key on: (om/transact! this `[(app/next-page) :grid])

cjmurphy02:02:44

My understanding is that from a mutate defmethod, that returns a hashmap, the value of the :value key is not relevant.

cjmurphy02:02:30

Wish I knew more about re-querying - just getting to that sort of thing myself now...

cjmurphy02:02:16

Hard coding the :grid key as you do should do something.

cjmurphy02:02:00

All the components that have :grid in their query should re-read, re-render.

cjmurphy02:02:35

Not sure about lower down ones thou...

geraldodev02:02:08

Maybe I should mess with ast and set-query of the root ?

cjmurphy02:02:11

But their re-render will cause the props to go through - and others below to re-render??

geraldodev02:02:32

look at the picture

geraldodev02:02:14

I have ClienteGrid which is nested which have offset 6 (coz it has been incremented) and its root CreditoApp unchanged.

cjmurphy02:02:34

I have managed to stick to basic stuff so far. Never done setting query as you are doing.

cjmurphy02:02:34

(not needed to yet)

cjmurphy02:02:44

But I know what to say in the absence of the others being here - I think you've got to get a minimal case going.

geraldodev02:02:59

I see. thx for your help @cjmurphy. I'm trying to paginate. It's very simple. I hope to advance on this tomorrow. Now it's 00:14 am here.

cjmurphy02:02:17

Okay good luck simple_smile

marianoguerra09:02:53

how do you force a component to fetch the query keys from the remote?

marianoguerra09:02:32

I've seen discussion about touch! on the issue tracker but it seems it's not implemented yet

marianoguerra09:02:59

I have 2 cases, one, the first query fails because user is not authenticated, then some keys are never fetched

marianoguerra09:02:35

the second is a key that I want to fetch periodically because it's status may change but there's no mutation on my side that will say the key changed

marianoguerra09:02:12

I just did a hack, a dummy mutate that receives the key I want to "refresh" and returns it in {:value {:keys [k]}}

marianoguerra09:02:06

I think I can pass them to mutate on navigation...

doddenino14:02:40

Has anyone tried to use om next with a mongodb backend?

marianoguerra14:02:54

@doddenino: how would it be different that any other backend?

marianoguerra14:02:09

I'm using it against SQL and it works simple_smile

doddenino14:02:34

@marianoguerra: I guess I formulated it wrongly... What I meant was: without datomic. I'm just starting to go through the tutorials, and it seems like it's heavy geared towards datomic and pull syntax

danielstockton14:02:11

The query syntax is like datomic pull syntax, so there is less to do if you have datomic

marianoguerra14:02:30

@doddenino: not at all, you just have to translate the reads and mutates into mongodb queries

marianoguerra14:02:53

this would be a little more "indirection", but nothing crazy given how much you get for free with om.next+transit

marianoguerra14:02:43

I have to do the same with sql but it's not that much work, I just have a protocol for the data access and then implement one for the sql backend

doddenino14:02:38

@marianoguerra: I see. I was getting a little worried. Is the code you're writing open sourced?

marianoguerra14:02:19

@doddenino: nope, but I'm planning on writing an end to end todo list or similar and you may be the trigger that push me to do it 😄

marianoguerra15:02:47

I will try to have something this afternoon, ping me in the following days if I don't post something here

doddenino15:02:01

@marianoguerra: Eheh that would be cool! Maybe I just have to think a little more about it, but it seems like having to write a pull syntax <-> mongo query translator is just a little "crazy" 😄

danielstockton15:02:10

i should add that datomic is very interesting, you should try it if you have no special reason to avoid it

danielstockton15:02:21

i'd really like to see an open source project with the same goals and design

doddenino15:02:39

I have to use an existing db, so datomic is not an option for this

akiva15:02:47

@doddenino, I have’ve done Om work with Monger but I did it ‘traditionally’ with the front-end making calls through a back-end API via compojure.

akiva15:02:14

After working recently with clj-rethinkdb, I don’t think I could ever go back to Monger.

doddenino15:02:37

@akiva I'm not a big fan of mongo either, but I have no options for this project 😞

akiva15:02:04

I’m fine with Mongo itself; I just think that, especially compared to clj-rethinkdb, Monger is overly complex and fussy.

marianoguerra15:02:30

@doddenino: my example will expose the database as a protocol so changing it to use mongo or whatever else should be a matter of implementing the protocol for your database

grzm15:02:55

I'm working on a simple "add item to list" app. I can see that my mutate function is updating the state as I expect (adding the list item), but the change is not being updated in the UI. I'm getting the following error: `Invariant Violation: Objects are not valid as a React child (found: :not-found)

marianoguerra15:02:41

can you share that part of the code?

grzm15:02:45

I'm not sure where to look where the root cause is. I suspect the indexer, or perhaps the queries attached to the components (though their initial render looks as expected)

grzm15:02:46

@marianoguerra: which part would be most useful? I've been trying to reduce it to a simple use case, but it ends up being pretty much the whole app 😕

doddenino15:02:41

@marianoguerra, cool I'm looking forward to it simple_smile

marianoguerra15:02:18

@grzm the mutate call, the mutation handler

marianoguerra15:02:49

I think something is setup wrong since the :not-found should be handled by the reconciler afaik

marianoguerra15:02:17

shouldn't you return :value {:keys [<list of modified keys hier>]} ?

marianoguerra15:02:24

if you want the changes to be reflected

marianoguerra15:02:47

also you can add them at the end of the mutate call, not sure what's the priority there

grzm15:02:02

that's likely. My newness is appalling

marianoguerra15:02:58

try adding :value {:keys [:observations/by-id :observations]} to the map that has :action

grzm15:02:12

@marianoguerra: I've added the :value {:keys [:observations :observations/by-id]}

grzm15:02:37

That's probably part of it, though I'm still getting the same error. What's the next step in the chain after mutate?

grzm15:02:49

i.e, what should I inspect next?

marianoguerra15:02:10

can I see the query? you are running that runs cue/observe?

marianoguerra15:02:27

also the implementations of read for the keys you are returning

marianoguerra15:02:34

or if you have a :default implementation

grzm15:02:29

That read is not being called, however. I'm not seeing that println output in the console

grzm15:02:53

the default read (above) is being called

grzm15:02:10

so, the :value {:keys [:observations]} should cause the read to dispatch on observations, shouldn't it?

anmonteiro15:02:43

:value {:keys [:observations]} in the mutate function doesn't cause anything

anmonteiro15:02:53

that's just for code documentation

anmonteiro15:02:14

if you want something to be re-read after transact! add a list of keys after the transact! call

marianoguerra15:02:07

@anmonteiro: good info, didn't know why it was required in two places simple_smile

anmonteiro15:02:23

it's more of a server-side thing, really

anmonteiro15:02:42

so that client side developers that look at the server code can go "oh right, so this is what I should re-read when I do X mutation"

anmonteiro15:02:13

(or for frontend stuff done by different people)

anmonteiro15:02:15

alas, documentation

grzm15:02:44

@anmonteiro: thanks for the pointer!

grzm16:02:05

at least I'm getting a new error now simple_smile

jlongster17:02:56

I saw some people talking about a pattern for tabs a while back, but can't remember much about it. I have a root component with a tabbed interface, and I'm trying to avoid "over-querying". Basically, with a naive version, when I switch tabs it will run all the queries again for all tabs. Does anyone have any examples of how they implemented a tab interface? Possibly using set-query! to dynamically switch out the query?

jlongster17:02:24

The other problem is I don't really want to lose the state of other tabs if they've already been clicked on. So they should only be queried once, or something like that

thomasdeutsch17:02:22

@jlongster if you have one value in your state that says {:active-tab :tab1} a reader for the data that is shown for that tab can check if this value is set

thomasdeutsch17:02:56

and if tab1 is not selected, to return a not-found value

jlongster17:02:36

@thomasdeutsch I actually implemented something like that in my latest version, and it works ok. In my case I think I basically want a "placeholder" area for each tab that, when shown, runs its query once and will never run it again, and I swap out the areas with display: none.

jlongster17:02:01

because when I switch back to a tab it still re-runs the whole query for the tab, which could make sense but it's a complicated/slow query and I don't need it again

marianoguerra17:02:16

@jlongster: when I switch tabs I put the keys needed for that tab in the transact! like `[(ui.section/set {:value section}) @refresh-keys]

marianoguerra17:02:35

I have a map with sections and each section says which keys it needs to read

marianoguerra17:02:20

@jlongster: you are running something against a remote?

jlongster17:02:34

yep, each tab loads stuff remotely

marianoguerra17:02:36

maybe on the send-api you could do some throtling of some keys?

marianoguerra17:02:48

just ideas, I was thinking about the same today

thomasdeutsch17:02:49

@jlongster: i know that problem. well, my solution was to implement a check in my reader if the query needs to be executed

jlongster17:02:34

my complicated query runs db->tree on the client-side (assuming the data is loaded), and I sort of want to avoid even that because it's a bit of work

thomasdeutsch17:02:47

(defmethod read :q/customer-picker [{:keys [state query ast] :as env} key params] (let [st @state] {:value (if (empty? (:customer-edit st)) (om/db->tree query st st) :not-found)}))

jlongster17:02:22

what does :customer-edit represent?

thomasdeutsch17:02:11

it is a root value in my state.

hueyp17:02:41

anyone done stuff like

static om/Ident
  (ident [_ _]
    [:user/todos '_])

hueyp17:02:50

is it legal to use the ‘_ in idents like this?

jlongster17:02:10

@thomasdeutsch: right but why do you return a value when it is empty?

thomasdeutsch17:02:12

@jlongster: only demonstrating the logic that you can easily use a check in your reader to avoid a costly db->tree. in your case it simply can be a (if (= :tab1 (:selected-tab st)...

jlongster17:02:55

oh ok, thanks

jlongster17:02:09

@hueyp: I don't think so, link-style idents are only valid in queries

marianoguerra18:02:13

say you have a list of todo items, then use ident to make them by id in the state

marianoguerra18:02:35

now you want to add one new item, in the state you have to insert it in two places now, how do you avoid that?

marianoguerra18:02:05

(first time I use the identit and normalization thing)

marianoguerra18:02:38

also I'm getting this error "Error: No queries exist for component path (tudu.ui/UI tudu.ui/NewTodoItemUI)" when I pass a component to transact! instead of the reconciler

cjmurphy18:02:29

I'm not sure you can avoid it - see the Kanban demo mutates.

marianoguerra18:02:17

@cjmurphy: are you refering to the error in my last message?

marianoguerra18:02:52

or the mutate in two places

cjmurphy18:02:49

@marianoguerra - the mutate (not the error). In Kanban demo if the mutate is to add a card to a lane there is lots to be done in that mutate. Not just the vector of idents in the default db state, but the by-id map part of the state as well. And then only a card has been added. Also the card needs to be added to the lane. He uses om/get-ident (not sure the exact name) to get the identity of the lane component that the mutate comes from.

jlongster19:02:38

set-query! and update-query! can be used to load in more data over time and change the query structure. is it possible for a query to be empty? so a component wouldn't query anything at first, but later on it would use set-query! to load stuff

jlongster19:02:21

the main problem is that you can't compose it up to the root. what is a good way to load more data over time, in components that might not be initially rendered?

jlongster19:02:33

@anmonteiro: problem is, I have this tab interface and one of the screens produces a semi-complex query that db->tree takes ~20-30ms to do, so I get slow warnings in the console and I don't even need to re-run the query at all. My tabbed interface just shows/hides the areas with display: none so they keep their state. But I can't figure out at all how to make it so that switching back to that tab does not re-run the complex query

jlongster19:02:06

I don't know if that makes sense, but clicking on the tab has lag when I should somehow just be able to set display: block to instantly bring it back up

jlongster20:02:56

I tried using component local state but it appears set-state! also re-runs the queries, unless I read something wrong?

dnolen20:02:26

the trick here would be to use memoization around db->tree + path optimization

dnolen20:02:41

don’t have time to explain the details at the moment though, sorry

jlongster20:02:42

@dnolen: yeah, I suppose I could memoize db->tree calls. I do already use :pathopt true which greatly helped updating small pieces of state, but not sure how it would help here

jlongster20:02:54

no need to explain though, maybe I'll try memoizing

dnolen20:02:21

yeah memoizing should work @jannis did both and got his query time down to a couple of ms or something from >100ms

jannis20:02:00

(def db->tree (memoize om/db->tree)) should work fine. 150ms -&gt; 20ms in my case.

jlongster20:02:26

that definitely would solve it, didn't want to do that before for some reason but I didn't have good reasons

anmonteiro20:02:34

@jannis: have you tried memoization after the patch that introduced recursive union queries?

anmonteiro20:02:40

did it have the same speedup?

jannis20:02:55

I haven't tried it in a while.

jlongster20:02:19

how significant do you think the memory usage will be? any reason to not memoize by default?

jlongster20:02:46

I suppose it's better to be clear about that (make the user do it)

anmonteiro20:02:04

@jlongster: might not be of significance for every use case

anmonteiro20:02:20

I don't think introducing more memory footprint is something that Om wants to do by default

jlongster20:02:43

@anmonteiro: I'm still on alpha30 and seeing children re-rendered when they shouldn't be, just saw https://github.com/omcljs/om/commit/b09898846081ca1ca41a2bd3d26f0a981478ba35 thanks simple_smile is that on a new alpha by any chance?

jlongster20:02:59

also memoizing should make shouldComponentUpdate work better, too!

anmonteiro20:02:38

alpha30 is the latest alpha

jlongster20:02:00

k, I need to learn how to install local deps with Clojure

anmonteiro20:02:06

I'm really not confident wrt memoizing shouldComponentUpdate

anmonteiro20:02:17

oh. No hassle at all

anmonteiro20:02:19

clone the repo

anmonteiro20:02:23

cd into it and lein install

anmonteiro20:02:32

then require alpha31-SNAPSHOT as a dependency

jlongster20:02:46

huh, neat thanks!

jlongster20:02:40

about sCU, wouldn't calling db->tree (un-memoized) generate new data each time so children couldn't just use referential equality?

anmonteiro20:02:45

I'm dumb and slow and memoizing sCU should work

jlongster20:02:50

not at all, typing through computers is just limiting! thanks

alpheus20:02:35

Is it surprising for a component to update its IQueryParams during render? I'm looking at the AutoCompleter in this tutorial: https://github.com/omcljs/om/wiki/Remote-Synchronization-Tutorial. I see how it works, but it seems counter to the stateless components theme.

dnolen20:02:13

@alpheus: there is no stateless components theme in Om

alpheus20:02:34

Sorry for my mis-conception. Still very new at Om.

dnolen20:02:21

the theme is instead - "how can you do everything you did before conveniently in an OO UI system while gaining reasoning power about non-trivial state transitions"

alpheus20:02:30

I seem to have dreamed up that idea for no good reason. Thanks for the explanation.

dnolen20:02:58

stateless components are a popular idea in React

dnolen20:02:43

it’s not like they aren’t useful or good practice in Om, just that it’s not the essence of idea

marianoguerra21:02:04

how/where do I denormalize the response from the server?

marianoguerra21:02:53

I use ident for a component to transform a list to a tree with the id as keys, it works with the local init data, but when adding the remote and it replies with a list I don't know where and how to denormalize it

jlongster21:02:57

@marianoguerra: should automatically be denormalized when you call the merge function. if it's not it must not be matching the query structure of your components (so it's not finding the Ident implementation)

marianoguerra21:02:25

@jlongster: thanks, will check that

grzm21:02:51

@marianoguerra: did you find an answer to your No queries exist for component path error? I'm seeing the same error in a similar situation.

jlongster21:02:28

if you're sending sub-queries to remotes you may need :query-root and process-roots

marianoguerra21:02:41

@grzm: nope, ended up passing the reconciler for now 😕

grzm21:02:51

@marianoguerra: I believe so. There's a reproducible case

grzm21:02:51

The queries on BirdsList and ObservationsList really don't do anything (they're not referred to in the root), so maybe part of it is there as well?

anmonteiro21:02:34

@grzm: not just part of it. They are the sole problem

anmonteiro21:02:57

queries must compose to the root. How else could Om know what query & component paths are in your app?

hueyp21:02:01

is there a way to get a transact! to not remove something currently not mounted? e.g. I want to modify :user/todos from a state where the root-query does not have :user/todos mounted

grzm21:02:09

Yeah, that's where my head's been headed as well. Display hasn't been a problem, from what I've seen, so I'm unclear how to proceed.

grzm21:02:28

@anmonteiro: Given that the "window" for a list of data belongs to the list as a whole, I think those params should go on the list components, but I haven't been able to figure out how to include those in the Dashboard query.

grzm21:02:39

Any suggestions more than welcome.

anmonteiro21:02:56

@grzm: have you gone through the tutorials?

anmonteiro21:02:09

The unions tutorial has something similar?

anmonteiro21:02:00

no, not that one, one second

grzm21:02:01

@anmonteiro: a couple times at least, both on the wiki and @tony.kay 's . Learning stuff each time. I'm finding it tough going.

hueyp21:02:50

@grzm I might not have all the context, but you don’t get-query on the list item in [(:birds {:start ?start :end ?end})]

hueyp21:02:11

[({:birds (om/get-query BirdListItem)} {:start ?start :end ?end})]

hueyp21:02:22

oops that is wrong

grzm21:02:17

@hueyp: Tried that. Maybe my expectations of what the root query should look like is wrong.

hueyp21:02:32

I’m not 100% sure the syntax, I just vaguely remember thinking it looked weird in that params were last simple_smile

hueyp21:02:57

but without that in the query, when om indexes the root it won’t be able to connect the list item to the root component

grzm21:02:58

I say maybe. Ha! Even that is too much confidence at this point in my learning 😕

grzm21:02:47

You guys have been very patient with me today simple_smile Thanks for all your help!

hueyp21:02:59

you can put (om/full-query this) directly into BirdListItem and it would throw I wager

anmonteiro21:02:21

(defui BirdListItem
  static om/Ident
  (ident [this {:keys [bird/id]}] [:birds/by-id id])
  static om/IQuery
  (query [this] '[:bird/id :bird/text]))


(defui BirdsList
  static om/IQueryParams
  (params [this] {:start 0 :end 10})
  static om/IQuery
  (query [this]
    `[({:birds (om/get-query BirdListItem)} {:start ~'?start :end ~'?end})]))


(defui Dashboard
  static om/IQuery
  (query [this]
    `[{:birds/list ~(om/get-query BirdListItem)}]))

anmonteiro21:02:30

@grzm: omitting the render methods for simplicity

anmonteiro21:02:50

you need one more level

grzm21:02:26

@anmonteiro: thanks. I was wondering why the examples had what looked like unnecessary levels. Is there an way to think about when to add them?

anmonteiro21:02:25

just that your queries must compose to the root should be enough

anmonteiro21:02:31

you can't skip levels

grzm21:02:05

Should the Dashboard query be `[{:birds/list ~(om/get-query BirdsList)}])) ?

anmonteiro21:02:32

your data structure would need to change

anmonteiro21:02:35

there's one more option

anmonteiro21:02:47

which is not having a query in your list component

grzm21:02:14

@anmonteiro: In that case the params would be in the Dashboard component?

anmonteiro21:02:16

and parameterize in the Dashboard directly

anmonteiro21:02:21

this would be simpler

anmonteiro21:02:49

BirdList could either be a defui component or even a normal function

futuro21:02:06

@anmonteiro: I'm curious: why'd you quote the queries?

anmonteiro21:02:22

because of ?

anmonteiro21:02:47

the dashboard query doesn't need to be quoted

anmonteiro21:02:03

parameters are symbols, so they must be quoted

anmonteiro21:02:08

because they don't resolve to anything

futuro21:02:14

that makes sense

futuro21:02:18

thank you simple_smile

grzm21:02:59

@anmonteiro: thanks. Moving the params to the Dashboard component worked.

grzm21:02:26

@anmonteiro: yeah, I have. a couple of times. I was looking at the union tutorial, but I didn't recognize the importance of the "extra" levels they added. It just looked to me like they were making the data structure overly nested.

grzm21:02:14

which just shows that I haven't internalized enough of this stuff yet. Need more practice.

hueyp22:02:56

given the https://github.com/omcljs/om/wiki/Thinking-With-Links%21 tutorial, if I wanted to transact! :current-user from ‘Item’ … would I transact [:current-user _] or :current-user as the read key ?

currentoor22:02:09

I read through the Om.next caching tutorial but it still doesn't make complete sense. You can make a url from hashing the "static" part of a query.

clj
(let [query (p {:state app-state} (om/get-query Dashboard) :static)
      json  (t/write w query)
      hash  (.substring (sha-256 json) 0 16)]
    (str "/api/" hash))
;; "/api/02e397cc1447d688"
But then what? How does this help performance?

currentoor22:02:28

Do we just set a large max-age in the Cache-Control policy? If so why is the hashing needed?

currentoor22:02:00

Can't we just separate the static and dynamic parts, and cache the static part?

currentoor22:02:14

Not sure why the hash is needed.

dnolen22:02:20

@currentoor: because you need a unique URL for that request

dnolen22:02:14

it’s the key into the cache

currentoor22:02:20

@dnolen: is the query also send along?

currentoor22:02:07

wait if the request contains the query (in the query string) won't that be able to serve as the cache key also? for example /api?query=... instead of /api/02e397cc1447d688?query=...

currentoor22:02:41

since the hash depends only on the query

grzm22:02:46

@currentoor: does the URI include GET params, or is it a POST?

dnolen22:02:03

putting it in the url also introduces URI encoding discrepancies and other oddities

dnolen22:02:22

so not interested

currentoor22:02:44

i see, i didn't know HTTP caching worked with a POST

currentoor22:02:03

@dnolen: that makes sense

grzm22:02:22

@currentoor: I'm not sure if what I asked makes sense. I'm learning here, too

currentoor22:02:06

@grzm: i think the idea is to fetch data via a POST, the hash in the URL provides a key for each query and the actual query is in the body of the POST request

currentoor22:02:47

i think query strings have some size limitations also

grzm22:02:14

@currentoor: yeah, that's what I thought as well

currentoor22:02:46

cool, yeah i was confused because i thought we were only doing reads with GETs

grzm22:02:39

@currentoor: I don't know if om.next distinguishes between reads and updates: they're just queries (maybe?)

currentoor22:02:56

aside from being implemented in two different multimethods i guess you're right

currentoor22:02:26

but yeah they are similar and you plugin whatever you want in the methods

hueyp23:02:13

not sure if this is a bug, but {[:current-user '_] [:email]} gets indexed as [:current-user _] where [:current-user ‘_] gets indexed as :current-user in prop->classes

hueyp23:02:23

this leads to data inconsistencies

hueyp23:02:35

it doesn’t re-render components if they make it a join, etc

hueyp23:02:03

think this is something to submit an issue on, or expected behavior, or don’t do this behavior?

hueyp23:02:42

also, anyone know how transact! / transact* causes a reconcile? I see it add to :queue but not the link from that to when a reconcile! happens

anmonteiro23:02:10

@hueyp: happy to look at a minimal repro of the first

hueyp23:02:13

okie, I have one here based off of thinking with links! with a mutate, lemme trim it down to minimal

anmonteiro23:02:40

transact! causes a reconcile! because reconcile! is ran on requestAnimationFrame

hueyp23:02:07

ah, so it reconciles, but it might just have an empty queue

anmonteiro23:02:45

empty queue = root render, which doesn't do anything if shouldComponentUpdate returns false

hueyp23:02:56

:thumbsup: