Fork me on GitHub
#om
<
2016-02-29
>
jlongster00:02:29

@tony.kay: ooh yeah, I've had to do that. how is your data normalized then? I didn't think you were doing that part differently

tony.kay01:02:08

@jlongster: data is normalized as you would expect. tables at the top, some top level data structures with lots of ident references. Not sure what you’re asking

tony.kay01:02:31

queries are still trees, response is still a tree, using built-in merge, which in turn uses tree->db

tony.kay01:02:52

thus the desire to build the queries with defui, even if there isn’t UI in the defui

tony.kay01:02:53

the trick is the callback in send accepts a query to use for normalization…so you tell it to use that instead of the default (which is the root UI query)

jlongster01:02:02

oh, I thought you were saying that was a bad thing that your technique does not make you do

jlongster01:02:36

huh interesting, I'll have to come back to this when I re-think my data fetching, which I've been meaning to clean up

jlongster01:02:15

my parse function is small and I generally rely on tree->db and db->tree already

tony.kay01:02:37

yeah, so the helper puts the desired query (which has metadata for norm) into a “ready to load” queue. The send gets called, pulls the query off, shoots it to the server, gets the response, then gives the response and query to the callback.

tony.kay01:02:54

Done. Detached queries from the actual (or just current) UI

tony.kay01:02:09

really useful for prefetching too, where the current UI may not even have the stuff you want to fetch

jlongster01:02:51

neat I'll have to ask you some questions about that later

jlongster01:02:24

I have that sort of working with process-roots but I haven't really fleshed out that story yet

jlongster01:02:13

@tony.kay: this is my read function so far, read-remote looks for specific keys to make remote https://gist.github.com/jlongster/cd5df87a046c6505f8ed

jlongster01:02:27

the UI will ask for those and if they aren't loaded yet they just won't exist. what I really need to do is implement "paging" where I request an initial amount of transactions and load more on demand, but shouldn't be hard

artemyarulin08:02:19

Can anybody help. I’m trying to update AST for remote fetch, ended up with following minimum code that doesn’t work:

(defn read [{:keys [ast] :as env} key params]
    {:remote (assoc ast :query [:content "remote-id"])})
((om/parser {:read read}) {:state {}} [:content] :remote)
Last line returns [:content] while I’ve expected to get updated AST like [:content "remote-id”] so I can use it in my send function. Am I doing something wrong?

acron09:02:59

I'm seeing a "No queries exist for component path" error when going three levels deep on component nesting, after performing a transact!. The mutation succeeds but then this error. Is there something obvious I could look at? Using the routing advice here - http://anmonteiro.com/2016/02/routing-in-om-next-a-catalog-of-approaches/ - and then once of the routes has multiple components and it's one of those components that is causing the issue.

acron10:02:02

Ok, so it seems related to when I pass state to the components

acron10:02:08

I've resolved the issue; seems like passing in state cuts the component off from the reconciler, so I improved the 'logic' in a couple of the reads, which allows me to nest queries (as I'd kind of expect anyway) but this means calling the parser inside a read. Is that kosher?

george.w.singer11:02:09

Question motivated from a clojurescript/om issue: How does one refer to external/pureJS react components within clojurescript that were written in JSX/ES6 syntax?

denik14:02:37

hi channel! What are the trade-offs of storing defrecord instances in om.next’s app-state?

denik14:02:20

(currently this is not supported)

tawus14:02:05

If I understand correctly, http://anmonteiro.com/2016/02/routing-in-om-next-a-catalog-of-approaches/, routing leads to unnecessary nesting . e.g. using Unions we have :route/data {:routing-key {actual-query}}. What if I would like the data to be available directly under the actual-query keys e.g. for {:auth/login [:some-key]}, I would like to have data present under :auth/login directly. Can I do that ?

danielstockton14:02:47

@tawus: There are several approaches mentioned in that article, union queries is just one of them. A more popular choice seems to be set-query! which would insert the actual query where you want it, as you described.

acron15:02:09

@tawus: what @danielstockton said; I've ended up 'riffing' on the implementation suggestions in that article

cjmurphy15:02:56

If you write a mutation that doesn't work as intended then this library will let you know about the problem - in your browser as soon as it happens. Also useful for getting your initial state normalized:

tawus16:02:46

@danielstockton: this is still one :route/data ... May be recursive-remote can play a part here

anmonteiro16:02:25

@tawus: if you're going to do routing, you'll have to aggregate your routes under a common key

anmonteiro16:02:33

there's really no way around that

anmonteiro16:02:53

regardless of the approach you end up choosing

anmonteiro16:02:59

recursive parsing surely helps there

tawus16:02:27

The problem is when I want to access data from the routes as links.

anmonteiro16:02:25

what's the problem you're having?

tawus16:02:28

@anmonteiro : If X and Y are two components for routing. X has a query`[{:x-data [:a :c]}` and component Z, which is nested inside Y, needs :x-data. It can only access it using links. but if X Y are under :route/data, :x-data is not top level and can’t be accessed as a link.

anmonteiro16:02:04

@tawus: so what's stopping you from moving :x-data to the top-level and querying with a link in X and Z?

tawus16:02:39

Hmm… that seems to be a good idea and proves that I have to go back to the tutorials again 😞 I’ll check out this approach. Thanks, really appreciate it!

jgdavey17:02:02

Any thoughts on how to idiomatically handle db->tree, but also have parameterized queries in parts of the query?

jgdavey17:02:33

Specifically, how would one handle something like this: https://github.com/omcljs/om/wiki/Remote-Synchronization-Tutorial#building-a-simple-auto-completion-widget, but for a collection auto-completion widgets.

jgdavey17:02:11

That is, how would you handle children with parameterized queries, and keep context so you know how to merge in novelty?

artemyarulin18:02:17

May I ask again, maybe somebody can help. I’m trying to update AST for remote fetch, ended up with following minimum code that doesn’t work:

(defn read [{:keys [ast] :as env} key params]
    {:remote (assoc ast :query [:content "remote-id"])})
((om/parser {:read read}) {:state {}} [:content] :remote)
Last line returns [:content] while I’ve expected to get updated AST like [:content "remote-id"] so I can use it in my send function. Am I doing something wrong?

jeremyraines18:02:53

What’s the shape of the response from a server-side mutation supposed to look like? I have a bare bones example, that sends back a response that looks like this:

{message {:keys [:message], :result {:message Changed by mutation on server!, :description Description set on server, initial load., :sender Server guy}}}

jeremyraines18:02:27

that whole thing gets merged into my client side state, instead of the :message, etc keys getting updated. So component doesn’t render with any update

jannis19:02:02

@jeremyraines: Why :result and not :value?

bnoguchi19:02:20

@artemyarulin: try (assoc ast :query [:content] :params {:id "remote-id"})

jannis19:02:21

Om Next expects :keys (optional) and :value to be returned.

jeremyraines19:02:23

I’m not sure, here’s my mutation fn on the server side

artemyarulin19:02:53

@bnoguchi: Thanks, I’ll try!

jeremyraines19:02:58

(defmethod mutate :default
  [{:keys [state] :as env} key params]
  {:value {:keys [:message]}
   :action
   (fn []
     (swap! state assoc :message "Changed by mutation on server!")
     (swap! state assoc :sender "Server guy"))})

jannis19:02:56

@jeremyraines: Are you sending just the mutation(s) or are you also querying for keys to be read again after the mutations have been applied?

jeremyraines19:02:21

just the mutation. But it’s from inside a component where those keys are part of the component’s query

jannis19:02:22

(I suppose Om adds :result = whatever :action returns.)

jeremyraines19:02:09

clojure
(defui HelloWorld
  static om/IQuery
  (query [this]
         '[:message :description :sender])
  Object
  (render [this]
          (let [props (om/props this)
                msg (:message props)
                desc (:description props)
                sender (if-not (= (:sender props) :not-found)
                         (:sender props)
                         "Unknown")]
            (dom/div #js {:onClick (fn [_] (om/transact! this '[(message)]))}
                     (str msg ": " desc " -- " sender)))))

bnoguchi19:02:22

@artemyarulin: actually don't set query. try (assoc ast :key :content :params {:id "remote-id"})

bnoguchi19:02:39

Unless query is part of a join specified by an existing :key

jannis19:02:13

@jeremyraines: Hmm, that looks about right.

jeremyraines19:02:17

thanks. I’ll try to isolate it further — I’m using compojure-api so maybe something there . . . Everything else works fine though; remote reads, client side mutations, server side mutations; just not merging that state from the server back to the client

bnoguchi19:02:24

@artemyarulin: you could also just do (assoc ast :key [:content "remote-id"])

jeremyraines19:02:09

@jannis: do you know of any online examples that talk to a server besides david’s todomvc?

jannis19:02:16

@jeremyraines: In my copaste experiment, I explicitly add read keys in transactions that include remote mutations. IIRC, Om will not automatically re-run queries against the backend unless this is asked for explicitly. However, that I don't currently know what it does with results returned from the server-side :action.

tony.kay19:02:25

The server side results of action appear under a :result key

jeremyraines19:02:45

yeah — my results appear there but om doesn’t seem to do the right thing with them

jeremyraines19:02:32

I tried explicitly listing the affected keys in the mutation query exp:

(om/transact! this '[(message) :message :sender])
still no luck. It doesn’t even seem to send anything different to the remote. This is the edn of the request before it’s changed to transit & sent:
{:remote [(message)]}

tony.kay19:02:58

@jeremyraines: Are you asking about returning the result to the client from the result of action?

jeremyraines19:02:19

Maybe? It’s getting returned. I don’t know if it’s the right format. I don’t know what Om expects but what I’m doing is pretty bare bones.

jeremyraines19:02:47

this is what I get back from the server

jeremyraines19:02:51

{message {:keys [:message :sender], :result {:message Changed by mutation on server!, :description Description set on server, initial load., :sender Server guy}}}

jeremyraines19:02:54

that whole thing gets merged into my client side state

jeremyraines19:02:06

rather than the contents of :result

artemyarulin19:02:19

@bnoguchi: Thank you very much, will play with it tomorrow!

jeremyraines19:02:36

wondering if I need to provide a :merge-tree function to my reconciler or something. that would suck

tony.kay19:02:53

so, mutation results in general are not meant to be merged

jeremyraines19:02:05

I’m not doing any merging

tony.kay19:02:17

they are a response that you can process into your app state if need be

tony.kay19:02:24

you're calling the callback in send, right?

tony.kay19:02:30

that callback is merge

jeremyraines19:02:33

I give the server reponse directly to OM’s callback

tony.kay19:02:01

yep...that callback can take data and an optional query (for normalizing)

tony.kay19:02:21

if you want to merge in :result, then do (callback (:result data))

tony.kay19:02:40

or (callback (:result data) some-query)

jeremyraines19:02:42

won’t that break reads?

jeremyraines19:02:52

do I need to have different enpoints for reads & mutations?

tony.kay19:02:58

Oh, I see...you're asking if send gets to just be 2 lines of code: no

jeremyraines19:02:13

OK, thanks. I can probably figure this part out but: where/how do you branch that? different ajax calls for reads and write — to the same endpoint but having different callbacks?

iwankaramazow19:02:30

do you use clojure on the backend?

tony.kay19:02:33

in send, do something like detect that :result is present

tony.kay19:02:44

put your custom logic under that branch

iwankaramazow19:02:11

(def server-parser
  (om/parser {:read parser/read :mutate parser/mutate}))

(defn api-handler [req]
  (println "incoming: " (:transit-params req))
  (res/response (server-parser {} (:transit-params req))))

iwankaramazow19:02:19

some inspiration...

iwankaramazow19:02:58

the parser is the same story on the backend,

tony.kay19:02:25

I have some vague memory that the callback normally filters out mutations from client app-state merge...but I'd have to revisit the source to remember.

jeremyraines19:02:11

I’ll just have to remember to never have a piece of state called :result . . .

iwankaramazow19:02:05

Yea evade it to, in my case: when transacting something with datomic, clojurescript can't handle the return response

iwankaramazow19:02:18

or I am doing something severely wrong

jeremyraines20:02:56

hoo boy:

(defn transit-post [url]
  (fn [edn cb]
    (println "Sending: ")
    (println edn)
    (.send XhrIo url
           (fn [e]
             (this-as this
               (let [resp (t/read (t/reader :json) (.getResponseText this))
                     resp-keys-set (-> resp vec first second keys set)]
                  (if (resp-keys-set :result)
                      (let [result (get (-> resp vec first second) :result)]
                        (cb result))
                      (cb resp)))))
           "POST" (t/write (t/writer :json) edn)
           #js {"Content-Type" "application/transit+json"
                "Accept"       "application/transit+json"})))

jeremyraines20:02:09

simple made some kinda somthin'

jeremyraines20:02:23

that works though

iwankaramazow20:02:58

I know the feeling 😄

tony.kay20:02:43

@jeremyraines: Um...I separate my networking logic from send itself...makes it a bit cleaner

jeremyraines20:02:53

yeah. Still seems pretty bad to have the result stashed in a map which is the value of a map whose key is . . . I guess the mutation “key”? Also fun is that if you quote the mutation query with ` instead of ‘, that key changes

tony.kay20:02:31

responses are always keyed by the thing that asked the question...in this case a symbol, yes

jeremyraines20:02:24

OK, even if it makes sense, you’re not going to know what that key is when you’re handling an arbitrary response

jeremyraines20:02:59

I’m mean i’m not arguing that it’s wrong. I’m clearly missing several things

jeremyraines20:02:26

it’s just frustrating because the docs don’t really mention this. The example on remote sync is pretty complex

tony.kay20:02:29

you won't be handling an arbitrary response...since you sent the question

tony.kay20:02:47

yeah, alpha means underdocumented simple_smile

jeremyraines20:02:18

the component sent the question (mutation). The send callback doesn’t know that

iwankaramazow20:02:47

@jeremyraines: have you seen @tony.kay 's tutorial yet? that one is currently overdocumented 😁

jeremyraines20:02:46

I don’t think I have . . . & my whole first Google result page of “om.next example” is purple simple_smile will check it out

jeremyraines21:02:35

I’m trying to understand what you’re doing in your send function. Specifically with om/process-roots. When I give that either my edn payload (what’s going to the server) or the response back from the server, it blows up

jeremyraines21:02:50

was misreading the printed out :rewrite function as an error

jeremyraines21:02:06

but, now that I’ve got that, these are the same:

jeremyraines21:02:48

_ (println "rewrite")
_ (println ((:rewrite (om/process-roots edn)) resp))
 _ (println "resp")
 _ (println resp)

anmonteiro22:02:48

@dnolen: I think I've found an edge case in db->tree

(db->tree '[[:my-route _]] {:my-route :something} {:my-route :something})
=> {:my-route :something}

whereas:

(db->tree '[[:my-route _]] {:my-route '[:something :else]} {:my-route '[:something :else]})
=> {:my-route nil}

anmonteiro22:02:42

db->tree will try to resolve the ident in the latter case, but I don't really want it to?

anmonteiro22:02:54

because that might not even be an ident

dnolen22:02:33

@anmonteiro: right this is known problem

dnolen22:02:46

since there isn’t a ident type that we use

dnolen22:02:10

oh hrm, yeah open a issue - should think about that

dnolen22:02:16

maybe there is a simple heuristic we can use here

anmonteiro22:02:44

will do, but one doesn't occur to me right away

anmonteiro22:02:08

@dnolen: suggestion for the issue title appreciated

dnolen22:02:06

maybe something like "db->tree naively resolves idents"

anmonteiro22:02:28

this case is not ident resolution, I think

dnolen22:02:08

I think we have some logic for resolving a ident and if get another thing that looks like an ident we resolve again

dnolen22:02:47

rather what I mean is a query “link” resolves to an ident

dnolen22:02:53

and that gets looked up

dnolen22:02:06

maybe it’s misfeature? But it seemed convenient at the time

anmonteiro22:02:19

yep you're right

anmonteiro22:02:29

I'll look into it a bit

anmonteiro22:02:11

@dnolen: right so I was looking in the wrong place

anmonteiro22:02:18

there's even a comment stating what it does

anmonteiro22:02:26

;; support taking ident for data param

jeremyraines23:02:29

I’m still struggling to understand how to handle the response from the server after a server-side mutation. I now understand what the pieces of the response are, and how to find the :result piece which has the updated state I want to merge, but the means of detecting and grabbing it that I’m using (see if the vec’d response’s first entry’s second entry is a map with a :result key) seems really smelly. But it seems like that’s what this commit by @anmonteiro does, on david’s om-next-demo repo. https://github.com/swannodette/om-next-demo/commit/6cd3f894594967d37aa68331902adae69432c967 (That, and removing datomic keys). I just want to make sure that’s just how it is and there’s not some transformation fn or other technique that people use for this that I don’t know about