This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2016-03-19
Channels
- # beginners (15)
- # boot (99)
- # braid-chat (1)
- # business (1)
- # cider (109)
- # cljs-dev (1)
- # cljsrn (3)
- # clojure (249)
- # clojure-brasil (2)
- # clojure-canada (2)
- # clojure-madison (4)
- # clojure-russia (3)
- # clojure-sanfrancisco (1)
- # clojure-taiwan (1)
- # clojure-uk (1)
- # clojurebridge (1)
- # clojured (1)
- # clojurescript (15)
- # clojurewest (1)
- # cursive (9)
- # datomic (12)
- # editors (16)
- # euroclojure (1)
- # hoplon (45)
- # off-topic (22)
- # om (181)
- # onyx (2)
- # parinfer (3)
- # pedestal (17)
- # re-frame (2)
- # ring-swagger (1)
- # spacemacs (34)
- # testing (2)
- # untangled (2)
- # yada (1)
@thiagofm: What's the problem ? 😛
@iwankaramazow: I can't wrap my head around the tutorial in the wiki, more like my problem, haha
@thiagofm:
1) define a send
function
2) return {:remote true}
in your parser
3) experiment
Ask yourself: Do I want to filter out the static & dynamic part of a query and send it off to different remotes?
If you're starting out with remotes, start with one remote.
In the tutorial the static and dynamic are two different remotes.
I guess this is where I'm having issues, mostly with terminology. What is a static or dynamic part of a query? Is a remote an ajax request?
:static
and :dynamic
are two arbitrary chosen names for two remotes.
Example use case: You have a dashboard where all users see the same items. However you can customize the dashboard a bit, so every user sees for example another background (which he chose at some point)
You could send of the static part of the dashboard (for every user the same) to an endpoint that is http cached for performance
the dynamic part of the dashboard ( the customization ) gets send to another endpoint which resolves for example the background color (unique to each person)
A remote isn't an ajax request
it's an endpoint
for example: http://www.youromsite.com/api
A place you can send your queries to
If you return {:keyword-for-your-remote true}
in your parser, Om will queue all remote sends together and send them through the function you have to declare at :send
in your reconciler
In that function the ajax stuff happens
example:
(defn transit-post [url]
(fn [{:keys [remote]} cb]
(.send XhrIo url
(fn [e]
(this-as this
(cb (t/read (t/reader :json) (.getResponseText this)))))
"POST" (t/write (t/writer :json) remote)
#js {"Content-Type" "application/transit+json"})))
(from Om's todomvc)
(example of a send function)
Exactly
thiagofm: I went through everything in this project https://github.com/swannodette/om-next-demo/tree/master/todomvc to get oriented with how the back end talks to the front end
@thiagofm: the request arrives at the remote (your backend), you resolve the query, send the stuff back and the cb
provided by Om will merge the novelty back in
Haven't used datascript, you might need to declare your own merge
(defmethod mutate 'todo/update [{:keys [state ref]} _ new-props] {:remote true :action ;; OPTIMISTIC UPDATE (fn [] (swap! state update-in ref merge new-props)) })
It just updates the state with the new props, the remote true will make sure that the send function is invoked
But is the send function is invoked with what? How does it knows the endpoint and so on?
I've wanted to do something that just gets some information from github api and saves it in a datascript key, so I can query later
you declare (with the above send function) at your reconciler :send (transit-post "/url-of-your-endpoint")
I need to make like 3 reqs to the github API for me to get the information I need(I have to go through the links there and so on)
(def reconciler (om/reconciler {:state (atom {}) :normalize true :parser (om/parser {:read p/read :mutate p/mutate}) :send (util/transit-post "/api")}))
You have to write your own send function which knows what queries to send to different parts of the github api
that hardcodes it to one endpoint, your send function could decide which endpoint to call
@thiagofm: my first remote was a classic rest api, in my send function I checked what the query was, with cond
I determined the exact url of my rest api, and used that to send it off
Now I kind of get it, I guess. The query in my read function can be accessed from the send function, then I can decide on what to do
Exactly
Separation of concerns
Wouldn't it be a good idea to treat the data you query in a datascript db the same way you would a data that is ajax requested?
I don't fully understand your question I think
I see the kind of data I get from a database the same kind of data I could be getting from an endpoint. Even some databases have rest endpoints
I expected things to be in the same way in om, but it's not. I guess I have to find out why it's not that way
There are a few problems with rest, for example: too many request, when you have an endpoint which receives just queries
you can ask exactly the data you need
(given a backend that can interpret those queries)
That part is okay, I guess. I thought om worked like this: You have a couple of query/read functions, as an example: :users(list of users). You specify in every component it's own queries(could be querying the local state database, or a remote, doesn't matter). I can then do some funky mutation which changes the attribute of an user, and then I send like, hey, re-render the user listing component, and it would find out the queries related to that and expire their cache and re do it if needed
My read repositories from github could be something like (defmethod read :github-repos ...) and I could just query and handle it the way I've wanted
-> co-located queries on components: descriptions of the data it needs -> parser: interpret those co-located queries -> mutations trigger a re-read -> you have to implement that specific read function in a particular way if you want to expire your 'cache' (aka local database) or send it of to a remote
Okay, so I write inside the read whether if I want to expire it's value or return it?
(defmethod read :github-repos
[{:keys [query state]} key _]
(let [st @state]
(if (nil? (get st key))
{:remote true}
{:value (get st key)})))
Okay, so using datascript I would have to verify if I want to expire it, if not, I make a query to datascript looking for the the key that has saved the value I want
indeed
it doesn't
oh wait
it knows the query
I think there's a re-read happening when the results arrive from that query, based on that query
So... (defn transit-post [url] (fn [{:keys [remote]} cb] (.send XhrIo url (fn [e] (this-as this (cb (t/read (t/reader :json) (.getResponseText this))))) "POST" (t/write (t/writer :json) remote) #js {"Content-Type" "application/transit+json"}))) The cb would return the github repos json/map
the cb
is a callback provided by Om which merges things
I think with datascript you'll have to do some stuff you can't do out of the box
Datascript integration isn't 100% ready yet
I think I can try to pass the reconciler and do a mutation query to save the key, in a generic way I can use also in other places
sounds like a plan
haven't looked quite at datascript
are there any good dev tools i should know about besides figwheel and cljs-devtools? I seem to recall coming across a list but I forgot to bookmark it
enlighten mode?
yea I'd just create a reconciler file that sets the parser / state up and import that from both files
I'm not that used to holding information about which file gets loaded before in my mind as clojure has, cool
Assert failed: Circular dependency detected, haxlife.components.code -> haxlife.data.query -> haxlife.reconciler -> haxlife.data.query
@iwankaramazow: okay, so it seems when I set {:remote true} as response of a read, the reconciler sends to my send function this: {:remote [:remote/github-repositories]}
@cmcfarlen: where do I send this?
If the key in the query that you send to the remote contains params, they will be sent along. So if you reader returned {:remote true}
for :remote-items, then {:param 1}
would also be sent to the server.
@cmcfarlen: okay, makes sense, let me see
@cmcfarlen: okay, it worked
@cmcfarlen: can IQueryParams be dynamic? I would like for example to pass the reconciler for example(because of... reasons)
@thiagofm: yes, these can be dynamic. (om/set-query! component {:params {:param 42}})
@cmcfarlen: where can I use this function? In the component? In the read?
In the component. If you need to add/change params from read, you can modify the ast of the query. You see, {:remote true} can be used to send the query as is. But you can also give a modified ast and it will send that instead.
The idiomatic way though is to use set-query! and then substitute into the query with the ?param symbols
But how would it work? Let's say I have a component called "Code". Inside that component, I do (om/set-query! this {:language "haskell"}), but then to render this component, the query is already supposed to be made, no? So I'll never see the language as " haskell" @cmcfarlen
(defui Code static om/IQueryParams (params [this] {:language "ruby"}) static om/IQuery (query [this] '[(:remote/github-repositories {:language ?language})]) Object (render [this] (let [github-repositories (:remote/github-repositories (om/props this))] (om/set-query! this {:language "haskell"}) ; (dom/div nil github-repositories))))
Why do I still get {:language "ruby"} ? @cmcfarlen
@cmcfarlen: Still prints
reconciler.cljs?rel=1458408916674:11#object[cljs.core.async.impl.channels.ManyToManyChannel] reconciler.cljs?rel=1458408916674:11(:remote/github-repositories {:language "ruby"}) reconciler.cljs?rel=1458408916674:11{:remote [(:remote/github-repositories {:language "ruby"})]}
might make sure this
is actually a Code
component
@thiagofm: hmm, you might also try to call it outside of the call of the render fn. So put the set-query! call in an event from a button or something. render is meant to be pure
@matthavener: how do I get the component?
oh nevermind I see the code, I agree.. dont set-query!
in render
@thiagofm: you can call it from componentDidUpdate.
Object
(componentDidUpdate [this prev-props prev-state]
(om/set-query! this {:params {...}}))
(render [this] ...)
Hey all. I just pushed a small client-only example to github. We had a small meetup session talking about om next and this is the result: https://github.com/noonian/om-next-counters-example
@cmcfarlen: thanks!
@cmcfarlen: still didn't work 😞 I'll try more tomorrow, thanks a lot for your help
@tomjack: Initially I just had counters, but someone asked a question from the context of re-frame, and how they would do something with a re-frame subscription, and it kind of grew out of that. Sorting in the render should work fine also, but you’d want to read the :other/counters
key instead of the :sorted/counters
key in the mutating transactions.
I think I still don't understand the rereads -- I expected :count
there. which seems to work as long as the :sorted/counters query includes :count
ah.. it looks like you don't need to reread :other/counters if you do the sort in render. but rereading nothing does not work with :sorted/counters
I thought I needed to re-read :sorted/counters
in order to get om to re-render the root component.
oh, yeah, you're right -- with sort in render you do need to reread :other/counters. otherwise the counters update in the sorted list, but they aren't resorted...
re-reading :count
also seems to work though, and it must be that om is smarter than I thought and knows that the RootView needs to be re-rendered
What is the idiomatic way of communicating errors back to a component that initiated a mutation in om next? Errors could occur in the mutation function (e.g. parameter validation failure), the send function (e.g. 500 error for remote mutations), or the merge function. I have not been able to find a clean way of communicating the possible errors back to the component that requested the mutation.
yeah. part of my question is how stuff like :sorted/counters can break this smartness
(by depending on stuff which is not necessarily in the query used -- though in your case :count is there)
I guess the :sorted/counters read function will just be broken if :count is not in its query
Yeah, it means any mutating transactions have to know to cause the sorted list to rerender
this doesn't seem as bad as I thought given that in the sort-in-render alternative we still need to reread :other/counters
er, I forgot that :count apparently works. so if we reread :count we don't need to reread :other/counters. maybe...?
Anyone using datascript with alpha31 and merging remote state? I got a new error...
I think it tries to update queues-sends in state, is that new?
@danielstockton: not related to sends
that was introduced in this PR: https://github.com/omcljs/om/pull/631
It's a known issue and will only be solved once we move queries out of the app state
Ok, thanks. Most things seem to work, despite the error.
I just have to steer clear of queries
set-queries*