This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2015-10-16
Channels
- # announcements (1)
- # aws (11)
- # beginners (3)
- # boot (63)
- # cbus (1)
- # cljs-dev (4)
- # clojure (96)
- # clojure-dev (5)
- # clojure-germany (2)
- # clojure-japan (43)
- # clojure-poland (2)
- # clojure-russia (38)
- # clojure-sg (2)
- # clojurescript (138)
- # clojurex (1)
- # cursive (3)
- # datomic (16)
- # docs (6)
- # emacs (3)
- # events (2)
- # ldnclj (42)
- # off-topic (6)
- # om (384)
- # onyx (122)
- # spacemacs (6)
Starting to feel the need to try out Om Next with React Native. I’m especially interested in what it might mean for queries in the mobile context (too many hand-crafted / optimized REST-based data fetches in my apps currently).
@mfikes I support that effort. I’ve given it a couple of attempts but I can’t figure out how to mount the root component.
I have dabbled but I don’t have enough experience with react native to know my way around
I used (om/root widget app-state {:target 1})
in http://cljsrn.org/ambly.html
I tried with a very early version… also I haven’t dug into it so its very possible I was neglecting something trivial
I think I got {:target 1}
from reading React Native JavaScript source, trying to piece together something that would work.
I might take another stab at it over the weekend. I’ll let you know if I have any luck the second time around
Is it possible to use om (or any cljs react framework) with react-native just as-is? In other words, compiling straight to (say) index.js?
I believe it was the target bit that was tripping me up. Also the version of om.next I was using didn’t allow you to just implement a render fn (without a query) so that was tripping me up as well trying to get a simple component on the screen
@zentrope: This project appears to do something close to that, with Reagent: https://github.com/chendesheng/ReagentNativeDemo
mfikes: It seems like a while ago you were saying no, based on the need for "require" (if I remember correctly).
I ended up taking that project and making it work with Ambly. See http://blog.fikesfarm.com/posts/2015-07-20-reagent-react-native.html
mfikes: What I'm really interested in is not having to do anything extra with the ObjC / Xcode part of the project.
Looking at the ReagentNativeDemo, it seems like he just runs lein to build to main.js, then runs and deploys ReactNative as normal.
In other words, the only difference is having to run that extra build step beyond what you’d normally do for a pure ReactNative project.
reminder to everyone that if you are using the latest Om releases and you have devcards in your deps, you are going to want to add an exclusion for the older transitive dependency on React: [devcards “0.2.0-4" :exclusions [cljsjs/react]]
@dnolen: I’ve just completed the tutorials (in Cursive, that screenshot was gold!) and it all worked great. I hit one issue with needing to be in the correct namespace but it was easy to suss out. That might catch cljs noobs though. If anyone else had a similar issue, maybe mention it here so we can see a pattern. Thanks everyone for the feedback, it helped me getting through as well.
@steveb8n: can you please repost the screenshot from davids repl config?
thank you @steveb8n
I'm currently knocking my head against om.next and don't understand a few things. Hope someone here can give me the hint I need
I'll just dump my question in here. If someone knows what I'm doing wrong, please ping me.
Sooo, I have a Component called CodemirrorComponent. It requests for :codemirror from my state.
(defui CodemirrorComponent
static om/IQuery
(query [this]
'[:codemirror])
...
Next up, I have my RootComponent that tries to mount codemirror. Now the way I understand the query system is, that rootcomponent queries my codemirrorcomponent for what data is requesting, composes a query and retrieves data from my reader. I read through the om.next wiki and created something like that:
(defui RootComponent
static om/IQuery
(query [this]
(let [subquery (om/get-query CodemirrorComponent)]
`[{:app ~subquery}]))
The state atom looks like this:
(defonce app-state (atom {:app {:window {:w 0 :h 0} :codemirror nil}}))
None of my component requests :window
(yet), however when I check what's inside (om/props this)
inside render, I get the entirety of :app
, including :window
. Is that how it is supposed to work or did I mess up somewhere in between?I have a super simple reader right now:
(defmulti read om/dispatch)
(defmethod read :app
[{:keys [state] :as env} key params]
(println "reading")
(println key)
(println params)
{:value (@state key)})
though there are no parameters (like the subquery) in there which I could use to filter the state furtheri think that the CodemirrorComponent is expecting a reader named :codemirror or a :default reader
though :codemirror
is available as subquery inside the RootComponent. I think my problem is also that I don't quite understand what
[{:app ~subquery}]` exactly is doing
short: `[...] is a template and ~ will resolve a symbol inside that template. See this example: https://clojuredocs.org/clojure.core/unquote look for "unquote macro" for more infos
that's what I suspected. Though how can I access // filter on that inside my reader and not just give the entire content of :app?
Ok I found a "workaround" if you can call it like that. I flattened out the data and merged the subqueries into 1 flat vector app-state:
(defonce app-state (atom {:codemirror "codemirror" :window {:w 0 :h 0} :foo "bar"}))
The RootComponent is now collecting the subqueries, merges them together and passes the entire collected state to the child
(defui RootComponent
static om/IQuery
(query [this]
(let [subquery (om/get-query CodemirrorComponent)]
(into [] (concat subquery [:foo]))))
Object
(render [this]
(codemirror (om/props this))))
ah crap, this results in No queries exist for component path
for some reason. Well it's getting late, I'll let this rest and look at it with new eyes tomorrow
i could need some help in solving a simple om.next/datascript task. rendering one of two components - dependent on what component is selected. I created a simple gist. could need some help to solve this the om.next way. https://gist.github.com/ThomasDeutsch/f8c056b498efd22c5e0c
@thomasdeutsch: I haven’t had time to document it in a tutorial yet but read about subquery
also made a task for myself, https://github.com/omcljs/om/issues/421
@thomasdeutsch: I ran pretty much into the same issue. any progress on your end?
working on it - looking for a nice idieomatic (om.next) way to solve this. The set-query! function will not be right at this point. I think that @dnolen quessed it right, and subquery is just what i need. https://github.com/omcljs/om/blob/48489aac9c1f00bbaaeb4deb0cb0e2a8a25890c3/src/main/om/next.cljs#L459 Damn, i could need a very small example.
from what I gather from subquery it doesn't work in my case cause the ref not being a single element but a collection
in my gist, i could read the whole entity with this query:
'[{:selected-menu [{:selected [*]}]}]
and then decide what to render based on the data. Is that the om.next way to solve this? i do not know 😉@zentrope: In chendesheng’s ReagentNativeDemo, the React Native Packager is packaging the concatenated (`:whitespace`) output of the ClojureScript compiler. With that model, development involves
1. Change ClojureScript source
2. Compile ClojureScript source (perhaps via auto)
3. Hit Cmd-R in the simulator
4. The app in the simulator talks to the packager and gets a new copy of the bundle (React Native JavaScript along with the embedded ClojureScript compiler output)
5. This stuff is loaded into JavaScriptCore
6. UI is re-rendered
7. Go to 1
That’s what I mean by “model that involves the React Native Packager”. This approach has pros and cons of course. Some of my thoughts on this are at https://github.com/reagent-project/reagent/issues/119#issuecomment-109643876@mfikes: Is there a path where we could hook into the app running in the simulator so that finishing the cljs compile will trigger #4 automatically?
@donmullen: I bet React Native's “Live Reload” would work.
@maria: With respect to directly consuming React Native libraries (without using the React Native Packager), do you recall ever seeing a construct that categorically prevents consumption for now—my guess would be the use of Facebook's proprietary module format with @providesModule
—or is it still an open question and an area of research in your opinion?
k sorted through Om Next advanced compilation issues, working on a Dynamic Queries tutorial now
is alpha3 currently working? I thought it was my code but even the tutorial throws errors
it’s important to follow the instructions exactly, do not bring in any 3rd party things or change any of the configuration … at all
@dvcrn: if you use figwheel, try to run
`(reset-autobuild)
in the figwheel promptwithout `
@thomasdeutsch: so looking at your problem I’m not sure why you are having trouble
@thomasdeutsch: having two menu components seems gratuitous
I’m not going to write a tutorial on this particular example as this seems like a UI modeling issue
@thheller: while I’m not going to look at your code in it’s current state, it’s too long. I’m more than happy to actually talk through the trouble you are having.
it does not seem related to @thomasdeutsch’s issue
okay, I cleaned everything and tried again. The first few examples are working but as soon as I add the reconciler with mutate
and read
, it gives me Cannot read property 'render' of undefined
. Problem appears at this step - https://github.com/omcljs/om/wiki/Quick-Start-%28om.next%29#naive-design.
I tried rolling back to the alpha1 snapshot that I used before and it seems to be gone.
I have a CMS with several object types. I get a list of "most recently changed objects" from the server. I want to render this list with each item being rendered by its corresponding om.next component
so in order for Om Next to do its thing it needs to understand the data about to presented so it can normalize it.
but that’s the fundamental issue, we can’t normalize the data based on upfront information.
If anyone is interested in my problem, I pushed the project in it's current state to github - https://github.com/dvcrn/dmedit/tree/develop. The code in question -- basically the tutorial -- is here https://github.com/dvcrn/dmedit/blob/develop/src_front/dmedit_om/core.cljs
when I launched the figwheel script via terminal the right react got installed but when I ran it through cursive somehow the old react was on the classpath (you can check that on the first line when launching the repl via cursive.
hmm no I don't launch it through my editor. I use the embedded figwheel nrepl and point the editor to that
I removed the dependency from my project structure view and ran the action "synchronize leinigen dependecies" which used to be default when launching but now is not available.
lein clean
lein run -m clojure.main script/repl.clj
installs the right react for you? You can just paste that one in, it shouldn't overwriteYeah I figured it could but cursive + jetbrains editors have enough to satisfy my needs.
I deleted my compiled folder, cleaned the build but after running figwheel it is still react 0.13.3
Possibly confusing dependencies found:
[sablono "0.3.6"] -> [cljsjs/react "0.13.3-0"]
overrides
[org.omcljs/om "1.0.0-alpha3"] -> [cljsjs/react-dom "0.14.0-0"] -> [cljsjs/react "0.14.0-0"]
and
[org.omcljs/om "1.0.0-alpha3"] -> [cljsjs/react "0.14.0-0"]
roaaar...thanks a lot @bbss! I spent hours scratching my head on this. Never suspected the react version to be the culprit
Hahah, yeah me too! I had some weird issues that I still don't get. But I am pretty new to clojure so.
But you could have found out that it was react by looking at the stacktrace in the devtools.
@dnolen: i think my problem is the same as @thheller described. I can not tell what sub-component will render, only after i got the data i am able to determine the right sub-component. i am not able to build a query for it upfront. Maybe i should just wait for the dynamic queries tutorial.
@thheller: OK I have a nice solution to your problem It’s a good case for the “Dynamic Queries” tutorial. And when thinking about the server side bits made me realize that code sharing between Clojure(Script) is just freaking awesome.
@thomasdeutsch: OK, it just that the UI components involved are not a good way to talk about the problem.
agreed
me, too. thanks!
what exactly is the difference between
(defui RootComponent
static om/IQuery
(query [this]
(let [subquery (om/get-query SomeOtherComponent)]
'[(:app {:query ~subquery})] ))
(used inside https://github.com/omcljs/om/wiki/Quick-Start-%28om.next%29#changing-queries-over-time to filter a query) and
(defui RootComponent
static om/IQuery
(query [this]
(let [subquery (om/get-query SomeOtherComponent)]
'[{:app ~subquery}] ))
like written in here https://github.com/omcljs/om/wiki/Components%2C-Identity-%26-Normalization#adding-reads
The first one passes the parameters to the reader, but what about the second one? I couldn't follow where the subquery is being used or what happens to itthe 2nd one declares which subkeys of :app you want
do I need a specific reader to make use of that? Right now it just returns the entire :app
object even though the subquery specifies keys, that's why the question
yep, but I believe it'll not return every key in the :app object once it connects to e.g. datomic
Oh right, I wanted to do that too! Thanks @anmonteiro
go ahead, that's why I uploaded it
so I guess if I only work with local state it is smarter to use prop + params and write my own reader that filters the state?
I think if you only work with local state you don't need queries (?)
you meant local as opposed to remote
i get it now
prop + params is intended to be used with IQueryParams, you get that ye?
hmm. what would be the optimal way to get something like the cursors from om legacy back?
i think queries will do that
@dvcrn: @anmonteiro: not possible
@dvcrn: like, if you only query :x and :y, the component's props will only have :x and :y I suppose
@dnolen: please correct me on this one
@anmonteiro: right, but the root will get the world and pass whatever pieces it wants down
so the parent is in control, however it does have all the pieces all children will need
ahhhh that makes sense ok
@bbss: that 'join + params' contains a typo (that were in the slides), it should be [({:some/key [:sub/key]} {:arg :foo})]
also curly braces in join + selector
nope, meant like this: [{:some/key [:sub/key]}] ;;join + selector
so given that, how would my rootview/component actually orchestrate all the queries that the childs want without dumping the same piece of state into all components?
Say my compA defines [:foo :bar]
, compB wants [:baz :moo]
.
My rootview collects it and then? Do I just construct a big map and pass it to my reader that then knows how to pick that from the state atom?
@dnolen: I was trying to make a "reusable component" that could receive other components (w/ arbitrary data) but I don't know what to query for; is this the 'Dashboard' issue you were talking about above? that reading the upcoming dynamic queries tutorial will give me a notion on how to handle it?
@anmonteiro: it’s important not over think things
@anmonteiro: the tutorial may cover the actual problem you are trying to solve, you’ll have to read it and see for yourself.
So I should not think of queries as a "I'm going to filter the piece of state that my parent gave me as parameter"?
so this error message should be for something completely different? "{:message "No queries exist for component path (deathrow.offender/Offender deathrow.common/GenericPanel)", :data {:type :om.next/no-queries}}"
children never receive more than was handed down by a parent as requested by the child query
@anmonteiro: I have no idea what you are trying to do.
@anmonteiro: wait for the dynamic queries post and see if it solves your problem.
will do
makes total sense
though isn't the parent using the query information to provide the child with the information in the exact same format it needs to operate?
btw is the :send option in the reconciler something to use or is it deprecated since it doesn't appear in the documentation (yet?)
@anmonteiro: something to use, haven’t gotten around to describing that yet
yep that's where I saw it!
but that got me into another trouble though; 2 actuallly. I'll try to describe them succintly
1. somehow I got the impression that it expects a datomic-like response; so if you query for :foo datomic will return {:foo {..other stuff..}}; but if we're communicating say with a JSON api that'll return a response object then ALL keys in the object will be merged in to our app-state; is there a way to define a custom merge that "assocs" the response into the key we queried for?
Isn't that demonstrated in the identity tutorial, where one of the persons has info on age but it's not included after normalisation?
this is something different (and related to remote responses, I'd say): say you query for :foo
which will be a map e.g. {:a 1 :b 2}
; my problem is that instead of getting {:foo {:a 1 :b 2}
in the app-state, I'll get {:foo nil :a 1 :b 2}
(if I had declared :foo
to be nil previously; otherwise I'll just get {:a 1 :b 2}
)
as in, the response object isn't merged into the app-state atom, its contents are
my current workaround is to return a map with the queried key as key and the response object as its value; then everything works great
@anmonteiro: that’s not how it works
if you’re going to talk to a remote service that service will return the same shape of thing that a parser does
@dnolen: OK that makes sense, and I can live with it; then to my 2nd problem: how to handle an error? what should I pass to the callback?
@anmonteiro: error handling beyond the simple stuff that is in place needs to be sorted out
if I pass e.g. {:queried-key :not-found} merge-with
will throw
@dvcrn: you can snag a selector specified in a query via the env param. so if you’re going from ParentComponent with query [{:key ~subquery}]
, the read handler for :key will have that subquery at (get env :selector)
what we end up with largely depend on what people want. Based on the various demands I’ll pick something that works for enough cases.
Absolutely uninterested in making everyone happy with regards to the error handling story.
@anmonteiro: also been looking closely at Relay & Falcor for ideas
@dnolen: totally understand; just reporting stuff that I've encountered hoping that it helps shape things up
I am hoping there will be a story for syncing automatically, say if connections fail or when you move from offline to online
@bbss: that sounds like an extension to me, like https://github.com/swannodette/om-sync was to Om Legacy
@bjr I'm just trying to understand the ideal workflow. Say I have a root component that mounts 2 other components.
- root wants the window size so it asks for [:window]
, right?
- componentA wants :window
as well and :foot
- componentB wants :html
and :bar
All this information is locally and not retrieved from a server (yet)
Do I now:
- Just retrieve all that stuff in root and pass it to the components as parameters or
- Let the childs declare queries and read them inside root.
If 2nd option, then how do I orchestrate this? Do I use '[(:app {:query ~subquery})]
to pass that query to my reader which then filters for me based on the parameter? Or do I use '[{:app ~subquery}]
which I still don't quite understand as it always gives me the entire :app
@anmonteiro: right like that 😄
@dvcrn: in the latter example of the 2nd option, you’d have to implement the reader to use the subquery selector
I think they’re the same, just passing the selector in a form the parser recognizes as the selector vs as a param and interpreting it yourself
@dvcrn seems like you'd want something like [(om/get-query componentA) (om/get-query componentB)]
and so on
@dvcrn: you can always query more than one thing inside the vector
What's the best place to learn about pull syntax: http://docs.datomic.com/pull.html ?
@bbss: that's what I read
@anmonteiro: ok that makes sense. Now my RootView has the subqueries. What I did before is, I merged them into 1 big vector and returned that as IQuery from my rootview. The problem is that with this approach I will receive 1 big map back that matches my vector. Can I somehow avoid that componentA gets :html
or componentB gets :window
? Since I query all this stuff at the same time I can't really differentiate which component queried what
Could I do something like (:app {:componentA ~a-subquery :componentB ~b-subquery})
and then use my reader to map it into different keys?
Then retrieve these keys inside render
and pass only :componentA
to component A and so on or is that the wrong approach?
@dvcrn: "retrieve these keys inside render
and pass only :componentA
to component A and so on " --> I think that's the way to do it
so something like (select-keys (om/props this) (om/get-query ComponentA))
etc
mine too, but that's what every tutorial does
so I assume it's the way to go about it
np we're all trying to grok something very new to all of us 😛
i hope that more people will be looking into datalog. datascript/datomic is a really fun environment to code with.
@dnolen: I've got a followup to #417 do you prefer a new issue or should I just post a gist here?
@anmonteiro: before I look at a gist just explain the perceived problem
the problem is the same, the query will throw an odd error
now after proper normalization
without params, it works as expected, adding params breaks
I've omitted any Object methods in the gist because this happens before render: https://gist.github.com/anmonteiro/1551f72d8df9cc98d836
@anmonteiro: add that complete example with the error to a GH issue, will have to look at it later
@anmonteiro: just write "problematic query" for title, and include a description of the steps, thanks
What's the expected way to reference components from a REPL? Found class->any but it expects a reconciler as its first argument. Am still playing with fairly simple components with no global state, so needing to configure a reconciler seems like a big dependency before being able to use the REPL. My first attempt at this succeeded in referencing a component, but (because it's my first time configuring a reconciler) had the side effect that components no longer re-render when their state changes.
@ericlavigne: there is no other way, you need to supply a reconciler
React JS programmers can't refer to their components in the REPL? That's crazy! 😮
Okay, so reconciler absolutely necessary. Guess I need to figure out what went wrong that this reconciler broke my application.
Already had component called ExampleSelector created with defui. Old code to use it was: (def example-selector (om/factory ExampleSelector)) (js/React.render (example-selector) (gdom/getElement "app")) New code using reconciler was: (def app-state (atom {})) (def reconciler (om/reconciler {:state app-state})) (om/add-root! reconciler ExampleSelector (gdom/getElement "app")) After that change, I could reference components from REPL, but changes to ExampleSelector's internal state no longer resulted in re-render. (I could verify the state changes because I could now see in REPL.) What did I do wrong with the new code? Do I need to explicitly create reader/parser configuration for what Om was previously doing automatically?
@bjr @ericlavigne that’s actually not a strict requirement, the idea is that you could write an Om application without those bits
I am trying to pass an Om component to a React component as props, is there any way to parameterize the component before I pass it? Tried using an om/factory
and passing props before passing to the React component, but React complains about getting an Object instead of a class/function.
Found a workaround until I can get reconciler working correctly: specifically create my own indices for the components that I'll be inspecting from REPL. top-level: (def example-selector-index (atom nil)) first line of ExampleSelector render: (reset! example-selector-index this) So now in REPL I can use: (om/get-state @example-selector-index)
@ericlavigne: I recommend going through all the tutorials I wrote to avoid having to do that
@dnolen: By "all" you mean these six Om Next tutorials? https://github.com/omcljs/om/wiki#om-next I am slowly working my way through the "Quick Start" at the moment, and writing small example components as I go. There's a lot to absorb, and I thought REPL would help with the learning process. Reconcilers are complicated and much further into the tutorial. Tried skipping ahead and copy/pasting but the resulting reconciler breaks my other code. Several of my components use "getInitialState" which isn't mentioned in the tutorial, so probably the tutorial reconciler just doesn't work with stateful components.
@ericlavigne: you should go through the first 2
@ericlavigne: read everything and go in order is my advice
Okay, will do.
i'm trying the first om-next tutorial and figwheel-sidecar is not reloading the browser (chrome 46.x). i upgraded sidecar to 0.4.1
(it does not rebuild either, bit when i force a rebuild, the browser still does not reload)
@lobo so this is not really a Om issue if you are really following the instructions. I would ask in #C03S1L9DN bhauman
My app state looks like this: (defonce app-state (atom {:counter 0 :list [1 2 3 4 5]}))
on every Interval I'd like to increment the counter... and increment the number in the list by one corresponding to the position indicated by (mod counter (count (list)))
(om/transact! data :counter #(do (println (string/join " " ["js/setInterval ctr:" %])) (+ (int %) 1)) :update)
@dnolen: any thoughts on how to make an app “reloadable” according to the figwheel definition? e.g. the identity tutorial resets state on every save. I could try applying the latests state snapshot on ns load but wonder if there is a best practice for this?
@steveb8n: there are some hooks for this but it’s low priority for me at the moment to explain this
@steveb8n: I think it should work fine if you just use defonce
for the app-state atom and the reconciler
@dnolen: fair enough. I’m looking forward to the remote stuff as well so I’m happy that it is a priority
@thheller: your problem is a bit more subtle than anticipated, I probably won’t get around to it today, too swamped
it will require some modifications to the query language, Relay handled this with Unions
yeah understandably. I meant hard for me. can't even wrap my head around the rest of queries yet.
oh yeah, if you really want to help out or contribute to Om Next, GraphQL & Relay & JSONGraph will give you big head start on Om Next
@dnolen: with regard to figwheel, are you planning to include local state in the reconciler snapshots so that history walking can include transient state changes?
@dnolen: hardly any original idea? queries as data structures form Datalog instead of strings, identity indexing for components, are the first two that come to mind that are not GraphQL/Relay/JSONGraph related
Hm. I guess that seems antithetical to app state. I guess if there is anything we want to track that needs to be visible in history we just put it into app state and have the mutate methods differentiate between persisted and transient data. Simple enough.
so there’s protocols in place for writing components that put their state somewhere else
I would agree. We are building a "support viewer", and it is nice to be able to see what the user did over time. I'll continue reading the source and see how to go about it
A VCR player when someone sends a support request that includes things like server log timestamps and such