This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2016-02-16
Channels
- # admin-announcements (14)
- # announcements (1)
- # aws (1)
- # beginners (105)
- # boot (609)
- # braid-chat (4)
- # braveandtrue (3)
- # cider (24)
- # cljs-dev (13)
- # cljsrn (2)
- # clojure (142)
- # clojure-berlin (7)
- # clojure-ireland (7)
- # clojure-japan (10)
- # clojure-nl (4)
- # clojure-poland (76)
- # clojure-russia (198)
- # clojure-sg (4)
- # clojure-taiwan (1)
- # clojurebridge (1)
- # clojured (4)
- # clojurescript (73)
- # conf-proposals (11)
- # cursive (10)
- # datomic (32)
- # devcards (1)
- # dirac (22)
- # editors (5)
- # emacs (3)
- # events (4)
- # funcool (19)
- # hoplon (18)
- # job (1)
- # jobs (3)
- # jobs-rus (16)
- # keechma (25)
- # ldnclj (33)
- # lein-figwheel (10)
- # leiningen (4)
- # luminus (1)
- # off-topic (19)
- # om (255)
- # onyx (51)
- # overtone (1)
- # parinfer (206)
- # perun (5)
- # proton (2)
- # re-frame (3)
- # reagent (2)
- # remote-jobs (13)
- # ring-swagger (7)
- # slack-help (4)
- # yada (7)
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.
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.
'Even with key :grid on the list of values that must be re-read' - I believe this is just for documentation purposes.
@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])
My understanding is that from a mutate defmethod, that returns a hashmap, the value of the :value key is not relevant.
Wish I knew more about re-querying - just getting to that sort of thing myself now...
Maybe I should mess with ast and set-query of the root ?
But their re-render will cause the props to go through - and others below to re-render??
look at the picture
I have ClienteGrid which is nested which have offset 6 (coz it has been incremented) and its root CreditoApp unchanged.
I have managed to stick to basic stuff so far. Never done setting query as you are doing.
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.
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.
how do you force a component to fetch the query keys from the remote?
(in om.next)
I've seen discussion about touch! on the issue tracker but it seems it's not implemented yet
I have 2 cases, one, the first query fails because user is not authenticated, then some keys are never fetched
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
I just did a hack, a dummy mutate that receives the key I want to "refresh" and returns it in {:value {:keys [k]}}
I think I can pass them to mutate on navigation...
@doddenino: how would it be different that any other backend?
I'm using it against SQL and it works
@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
The query syntax is like datomic pull syntax, so there is less to do if you have datomic
@doddenino: not at all, you just have to translate the reads and mutates into mongodb queries
this would be a little more "indirection", but nothing crazy given how much you get for free with om.next+transit
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
@marianoguerra: I see. I was getting a little worried. Is the code you're writing open sourced?
@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 😄
I will try to have something this afternoon, ping me in the following days if I don't post something here
@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" 😄
i should add that datomic is very interesting, you should try it if you have no special reason to avoid it
i'd really like to see an open source project with the same goals and design
@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.
After working recently with clj-rethinkdb, I don’t think I could ever go back to Monger.
@akiva I'm not a big fan of mongo either, but I have no options for this project 😞
I’m fine with Mongo itself; I just think that, especially compared to clj-rethinkdb, Monger is overly complex and fussy.
@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
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)
can you share that part of the code?
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)
@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 😕
@marianoguerra, cool I'm looking forward to it
@grzm the mutate call, the mutation handler
I think something is setup wrong since the :not-found should be handled by the reconciler afaik
shouldn't you return :value {:keys [<list of modified keys hier>]} ?
if you want the changes to be reflected
also you can add them at the end of the mutate call, not sure what's the priority there
try adding :value {:keys [:observations/by-id :observations]} to the map that has :action
@marianoguerra: I've added the :value {:keys [:observations :observations/by-id]}
That's probably part of it, though I'm still getting the same error. What's the next step in the chain after mutate?
can I see the query? you are running that runs cue/observe?
also the implementations of read for the keys you are returning
or if you have a :default implementation
That read is not being called, however. I'm not seeing that println output in the console
so, the :value {:keys [:observations]}
should cause the read
to dispatch on observations, shouldn't it?
:value {:keys [:observations]}
in the mutate function doesn't cause anything
that's just for code documentation
if you want something to be re-read after transact!
add a list of keys after the transact!
call
https://github.com/omcljs/om/wiki/Om-Next-FAQ#why-is-my-component-not-rerendered-after-transact
@anmonteiro: good info, didn't know why it was required in two places
it's more of a server-side thing, really
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"
(or for frontend stuff done by different people)
alas, documentation
@anmonteiro: thanks for the pointer!
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?
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
@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
and if tab1 is not selected, to return a not-found value
@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
.
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
@jlongster: when I switch tabs I put the keys needed for that tab in the transact! like `[(ui.section/set {:value section}) @refresh-keys]
I have a map with sections and each section says which keys it needs to read
@jlongster: you are running something against a remote?
maybe on the send-api you could do some throtling of some keys?
just ideas, I was thinking about the same today
@jlongster: i know that problem. well, my solution was to implement a check in my reader if the query needs to be executed
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
(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)}))
it is a root value in my state.
@thomasdeutsch: right but why do you return a value when it is empty?
@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)...
say you have a list of todo items, then use ident to make them by id in the state
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?
(first time I use the identit and normalization thing)
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
@cjmurphy: are you refering to the error in my last message?
or the mutate in two places
@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.
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
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?
@jlongster: no empty queries
@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
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
I tried using component local state but it appears set-state!
also re-runs the queries, unless I read something wrong?
@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
yeah memoizing should work @jannis did both and got his query time down to a couple of ms or something from >100ms
that definitely would solve it, didn't want to do that before for some reason but I didn't have good reasons
@jannis: have you tried memoization after the patch that introduced recursive union queries?
did it have the same speedup?
how significant do you think the memory usage will be? any reason to not memoize by default?
@jlongster: might not be of significance for every use case
I don't think introducing more memory footprint is something that Om wants to do by default
@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 is that on a new alpha by any chance?
alpha30 is the latest alpha
I'm really not confident wrt memoizing shouldComponentUpdate
oh. No hassle at all
clone the repo
cd into it and lein install
then require alpha31-SNAPSHOT
as a dependency
about sCU, wouldn't calling db->tree
(un-memoized) generate new data each time so children couldn't just use referential equality?
I'm dumb and slow and memoizing sCU should work
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.
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"
it’s not like they aren’t useful or good practice in Om, just that it’s not the essence of idea
how/where do I denormalize the response from the server?
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
@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)
@jlongster: thanks, will check that
@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.
if you're sending sub-queries to remotes you may need :query-root
and process-roots
@grzm: nope, ended up passing the reconciler for now 😕
@grzm: same problem?
@marianoguerra: I believe so. There's a reproducible case
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?
@grzm: not just part of it. They are the sole problem
queries must compose to the root. How else could Om know what query & component paths are in your app?
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
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.
@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.
@grzm: have you gone through the tutorials?
The unions tutorial has something similar?
no, not that one, one second
@anmonteiro: a couple times at least, both on the wiki and @tony.kay 's . Learning stuff each time. I'm finding it tough going.
@grzm I might not have all the context, but you don’t get-query
on the list item in [(:birds {:start ?start :end ?end})]
@hueyp: Tried that. Maybe my expectations of what the root query should look like is wrong.
I’m not 100% sure the syntax, I just vaguely remember thinking it looked weird in that params were last
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
(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)}]))
@grzm: omitting the render methods for simplicity
thanks @anmonteiro !
you need one more level
basically
@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?
just that your queries must compose to the root should be enough
you can't skip levels
your data structure would need to change
there's one more option
which is not having a query in your list component
@anmonteiro: In that case the params would be in the Dashboard component?
and parameterize in the Dashboard directly
this would be simpler
BirdList could either be a defui
component or even a normal function
@anmonteiro: I'm curious: why'd you quote the queries?
because of ?
the dashboard query doesn't need to be quoted
parameters are symbols, so they must be quoted
because they don't resolve to anything
@anmonteiro: thanks. Moving the params to the Dashboard component worked.
@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.
which just shows that I haven't internalized enough of this stuff yet. Need more practice.
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 ?
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?Do we just set a large max-age
in the Cache-Control
policy? If so why is the hashing needed?
Can't we just separate the static and dynamic parts, and cache the static part?
Not sure why the hash is needed.
@currentoor: because you need a unique URL for that request
@dnolen: is the query also send along?
got it
thanks!
nicely done
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=...
since the hash depends only on the query
@currentoor: does the URI include GET params, or is it a POST?
i see, i didn't know HTTP caching worked with a POST
@dnolen: that makes sense
@currentoor: I'm not sure if what I asked makes sense. I'm learning here, too
@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
i think query strings have some size limitations also
@currentoor: yeah, that's what I thought as well
cool, yeah i was confused because i thought we were only doing reads with GETs
@currentoor: I don't know if om.next distinguishes between reads and updates: they're just queries (maybe?)
aside from being implemented in two different multimethods i guess you're right
but yeah they are similar and you plugin whatever you want in the methods
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
think this is something to submit an issue on, or expected behavior, or don’t do this behavior?
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
@hueyp: happy to look at a minimal repro of the first
okie, I have one here based off of thinking with links!
with a mutate, lemme trim it down to minimal
transact!
causes a reconcile!
because reconcile!
is ran on requestAnimationFrame
exactly
empty queue = root render, which doesn't do anything if shouldComponentUpdate
returns false