This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2016-03-02
Channels
- # admin-announcements (9)
- # alda (21)
- # announcements (1)
- # beginners (68)
- # boot (241)
- # braid-chat (18)
- # cider (32)
- # cljs-dev (4)
- # cljsrn (11)
- # clojure (60)
- # clojure-dusseldorf (1)
- # clojure-germany (1)
- # clojure-poland (212)
- # clojure-russia (64)
- # clojure-sg (10)
- # clojurescript (212)
- # core-async (1)
- # css (14)
- # datomic (1)
- # emacs (9)
- # funcool (2)
- # hoplon (18)
- # jobs (1)
- # ldnclj (1)
- # lein-figwheel (5)
- # leiningen (3)
- # om (190)
- # onyx (46)
- # parinfer (13)
- # proton (3)
- # re-frame (7)
- # reagent (10)
- # ring-swagger (2)
- # slack-help (3)
- # specter (1)
- # yada (31)
Is it a best practice (or even a requirement?) that om next mutations be made relative to an ident?
For example in the Components, Identity, and Normalization tutorial that is the way things were done:
clojure
(defmethod mutate 'points/decrement
[{:keys [state]} _ {:keys [name]}]
{:action
(fn []
(swap! state update-in
[:person/by-name name :points]
#(let [n (dec %)] (if (neg? n) 0 n))))})
i.e: the app-state was modified by accessing the data through the [:person/by-name name]
ident.I am trying to use link in a query [[:some/link _]]
and in the render function I get (om/props this)
= {[:some/link _] right/data}
. Shouldn’t the key be :some/link
instead of [:some/link _]
. What am I doing wrong here ?
@tawus: The key is supposed to be what you’re seeing. You can access the value as (props [:some/link _])
https://github.com/omcljs/om/wiki/Thinking-With-Links!#embracing-links, :current-user
is accessed using :current-user
not [:current-user _]
and that is what is causing the confusion for me.
it might depend on if you use a join … I know with a join there were some inconsistencies that got fixed on master but not in alpha-30 yet
@anmonteiro: would like to see minimal example of that
@george.w.singer: To me just looks like decrementing :points
in the tables part of state that is in default db format. i.e. directly changing the actual data. You state does not have to be in default db format. But I think it makes life easier if it is!
But isn't it changing the automatically generated normalized data that om next produces via Ident declarations made in components?
i.e., it's not changing the actual data but generated data
Changes are made in mutations that quite often come from components via transact calls.
I said 'actual data' to mean not some reference - the tables part of default db format is where all the actual data lives.
Another related question (that might clear up some of my overall confusion here): I'm assuming that, in the same way om next reads are made relative to the root query/app-state, all om next mutations are made relative to the root app-state as well.
RE your response: so om always creates a table (behind the scenes) out of the data you give it?
I thought you had to specifically use Idents for that to occur
Idents are part of default db format, but update-in [:person/by-name name :points]
is just relying on the shape of the default db format data.
The :person/by-name
key in the app-state was generated by om next via Ident declarations
In that tutorial
If those Ident declarations weren't made, that key wouldn't be in the database
app-state*
Final question:
when you crate a mutate method for your parser
and you are mutating the :state passed in via env
that state is always going to be reconciler's app-state, right?
the full app-state
not some portion of it
I mean, that appears to be what's going on right here, for example: (defmethod mutate 'points/increment [{:keys [state]} _ {:keys [name]}] {:action (fn [] (swap! state update-in [:person/by-name name :points] inc))})
(defmethod mutate 'points/increment
[{:keys [state]} _ {:keys [name]}]
{:action
(fn []
(swap! state update-in
[:person/by-name name :points]
inc))})
in any event, thanks for your help
To me the above is just changing your state, and there is only one state. There's no env
involved.
env is first argument passed into the mutate method
which has a key called :state
the value of which I'm assuming is always just the app-state
That's what I'm confused about lol
I think you're/we're right I was just getting some confirmation
Took me a while to work things out, like when using get
it is to the refs part of default db format, and when using get-in
it is to the tables part.
What do you mean the refs part of the db?
Example:
:list/one
[[:person/by-name "John"]
[:person/by-name "Mary"]
[:person/by-name "Bob"]]
@dnolen: Any chance you can cut a release after merging PR 640? I have approval to open-source my stuff, and I want to cut a release based on the latest om alpha with that fix.
@tawus: I haven't needed to use set-query!
. But I think the ident
method is the important thing. As long as your new query includes the :id
(for by-id ident
) then things should work as before. (I'm sprouting this info - expecting to be wrong at any moment).
I think using set-query! to set Root’s query is messing up normalization for me. Now I am trying to do away with set-query! and see how things work.
You can use something to check whether it is being messed up or not: https://github.com/chrismurrph/default-db-format. That's a plug. But I wrote it to help myself because was often doing bad mutations, and rely on it now.
Hmm - root's query - that's what it is all about - normalization happens from root's query (or seems to me).
@anmonteiro: I have run into this same situation as well using routing and idents with my project...
@anmonteiro: maybe it isn't so "edge" case?
My biggest struggle when using normalization is trying to keep it in my mind at once . My mind likes to think in trees
Essentially it is the "root" of your query. In this example it would be [:transaction/by-id <id>]
Here is my problem. I am routing UserList (composed of UserItem having ident user/by-id) and UserDetails. When the routing starts with UserDetails (/user/1235), I can’t use [user/by-id 1235].
I have set id to nil in the params method...in which case the parser will return nil as well. When the param finally comes in using set-query! it will all load up. You can do some fancy ui "loading" display until you have the real parameter
You'll need to take advantage of the life cycle methods to do handle your particular (= id nil) scenario
That is what I am doing too. I think the above read method using query-root for idents might be something that solves my problem.
Yes, I will tell you the default read method won't do it for you because you'll need more than just the keyword :user/by-id to reach into the map...you'll also need the id. That is what query-root gives you.
not an atom but imagine set-query! starting with Login, so the root query will not have UserList’s query available at the time of normalization. Once I set-query! to UserList’s, data is fetched for :user/list but normalization doesn’t happen.
It may or may not be normalized depending upon how you are returning the state from your read method. IF you return it using db->tree it will be converted to tree
in my props, the data would be tree since my value key is returning the db->tree conversion
You can deref your reconciler to make sure your actual app-state is being normalized...i.e. @reconciler
I’ll try it out and may be let you know how it played out. Thanks for all the help. Really appreciate it!
Hm, can any body help. I cannot get query-root information in my send function:
(defn read [{:keys [query data state target parser ast] :as env} key params]
(case key
:current {(or target :value)
(assoc ast :query (parser (assoc env :data (get-in @state (get @state key))) query target))}
:content (if-let [c (get data key)]
{:value c}
{:remote (assoc ast :query-root true)})))
(defn sender [remotes cb]
(println "Equal:"
(:query (om/process-roots (:remote remotes)))
(:remote remotes)))
Looks like I’m marking it right, but still process-root in send function returns same query as like without itNewbie questions here: what does the AST represent and why it is returned as the remote value in the remote sync tutorial?
@artemyarulin: setting query-root only works with joins (not sure about unions) but it surely doesn’t work with properties.
When you write a mutate function, it is customary to to place the keys that will need to be re-read by om via the :value
key (inside the return value of the mutate function).
Yet it is ALSO customary to flag these keys when calling a mutation query (i.e., when making a query like [(my-mutation {:mutation params} :keys :to-be :re-read]
.
Why the need to double-book these keys which you are flagging to om to re-render?
:value is for documentation because many users of the mutation - so you can see how to do it. Pinned item for this as well.
Anyone encounter this om next error before?
Uncaught #error {:message "No queries exist for component path (app.components/Router app.components/RegisterRoute)", :data {:type :om.next/no-queries}}
So the {...:value :keys :to :be :reused ...}
within the return value of a mutate function has no effect on the program?
:re-read*
It's purely for code readability?
@tawus: Thank you. Hm, do you have any example of using query-root? Looks like I don’t understand it fully
Its explained in the pinned item which is a picture .png << @george.w.singer . Answer is yes, just for readability.
@cjmurphy: didn't know about pinned items in Slack. Thanks for re-routing me. And yes: the answer to my question was sitting in one of those pinned items
Hi. Does Ambly only support iOS devices? If yes, what is used on Android for React Native?
(defmethod read :customers/by-id [env k params]
(let [st @(:state env)]
(if-let [[_ v] (find st k)]
{:value v :remote (:ast env)}
{:value :not-found})))
Here's a snippet showing a read function. Hone in the :remote (:ast env)
part. Here we're telling the read function to send the query (:ast env)
to the remote :remote
. Why do we have to send queries in the form of abstract syntax trees to remotes? I tried sending a [:literal :query]
but when doing so, the corresponding send function isn't even called!@george.w.singer: I guess the reason is that you can return AST in many places and later on it will be composed together and then transferred to send function. If you return [:literal :query] in one place, then another literal in another - om wouldn't know how to compose this staff and create one query
According to the docs, a reconciler's send
function takes two arguments: a map with the query and remote information, and a callback. For example, here is a sample send
function taken from a blog post:
(defn send [m cb]
(let [xhr (new js/XMLHttpRequest)]
(.open xhr "POST" "/props")
(.setRequestHeader xhr "Content-Type" "application/transit+json")
(.setRequestHeader xhr "Accept" "application/transit+json")
(.addEventListener
xhr "load"
(fn [evt]
(let [response (t/read (om/reader)
(.. evt -currentTarget -responseText))]
(cb response))))
(.send xhr (t/write (om/writer) (:remote m)))))
My question though is where does that callback come from (i.e., who defines it -- the server or the client?) What's its typical use case?By "callback" I'm referring to the second argument of the send function (in this case: cb
).
@george.w.singer: IIRC, the callback cb
is just om/merge!
with the reconciler already passed for you.
Ok. So weird tho -- I can't find any documentation on this cb
.
@tawus: links are only transformed to keys when you use db->tree
@anmonteiro: My problem was that component properties were indexed with [:user 23746] rather than :user for the query [[:user 23746]]
. That turned out to be a normalization issue.
@tawus: still, if you query for '[:my-prop _]
and don't use db->tree
, you'll end up with the link in the component's props
Thanks @anmonteiro, I have passed that stage now but I am still struggling with a simple CRUD application with routing support. I am now waiting for @tony.kay’s read mutation approach. Hope that makes things easier.
@dnolen: condensed minimal devcards example and the fix in this PR: https://github.com/omcljs/om/pull/642
the fix works for the devcards example as well as for the project I was having this issue in
@anmonteiro: sweet!
@dnolen: I'm sure you've thought about it already, but I'd suggest that the release would not include the dynamic query stuff
We need to probably give it a spin for some time before incorporating it in a release
I'm sure there'll be edge cases
@anmonteiro: thanks for the fix!
Is there a way to do a local mutation when a remote response comes back? I'm trying to make a component that displays the number of pending remote queries.
keep the state in your send function, and then merge that state into the response on callbacks ?
Hmm, I tried just swap!ing the state which didn't cause a re-render, but I'll see if I can get something to work with merge.
@sebluy: After the transaction, Om implicitly updates the initiating component based on its query. However, if keys outside the component are affected, they need to be added to the transact!
query expression explicitly.
Alright, it seems to be working when I transact! on the reconciler to increment the count in my send function, and then merge in a decremented count on the response. Kind of hackish but whatever.
incrementing/decrementing a counter shouldn't feel 'hackish' 😛
@jlongster: how's the performance of virtualized list with set-query?
if I write #js {:foo 1}
with transit and read it back out I get {"foo" 1}
. is there any chance I can customize it to read back out keywordized clojure maps?
I vaguely remember something about :keywordize true
or something similar
@iwankaramazow: it works well with rerendering itself, if that's what you mean. I've have good experiences with the component. I'm currently having other perf problems unrelated to it (working with lots of data)
@jlongster: not supported, also IMHO keywordize is not a good idea
@dnolen: I'm trying to avoid extraneous work on a big set of data. I'm getting JS objects back from a SQL database, and trying to pump them out the to UI as quickly as possible. I've optimized the server to just write the JS objects directly, but the frontend gets {"foo" 1} now which doesn't work with Om Next.
I may just have to implement custom everything to handle this data (normalize it, etc). Really not sure the best way to handle it yet.
Not sure if there is a good way
I'm currently stressing myself with 'only'30000 items
there's some 'lag' here, i.e. a user on a monday morning my get cranky from it
but my app is so centered around this data (basically a big spreadsheet to interact with it) I'm not exactly sure anymore if Om Next is the right fit for it. Will still play around with it though.
Are there other technologies that qualify for this use case?
nothing much better, really, at some point you have to implement what you want yourself. I'll still probably use Om Next for general UI, but might just implement custom stuff for the core part of it
because I don't really need normalization etc for this core part (it's one big list of homogeneous data, somewhat easy to keep track of)
@jlongster: also worth look at the source of transit-cljs
oh sure, I was wondering if I can override the custom "map" handler and do it myself? still, not sure I need to do that anymore if I manage this data all myself
@dnolen: hm I don't see :mapBuilder
anywhere in here? https://github.com/cognitect/transit-clj/blob/master/src/cognitect/transit.clj (I see the map-builder
function)
@jlongster: transit-cljs
@jlongster: I was assuming the backend was JavaScript
@jlongster: so I guess the DB thing you’re using returns JSON so you can’t control that part
do you filter server side ?
@iwankaramazow: yes, although that may change, I'm considering loading everything in the client up front but I'm not sure yet.