I went ahead and split these functions out and put them in a library. All the tests pass, but I have not written an app specifically with the library just yet: https://github.com/untangled-web/om-css
OK, recipe complete, everything working.
Very cool, we were just talking about how to manage CSS on our components.
We have a component hierarchy, think list, item, sublist, that is used in many different places across our app to display this data in different ways. Each instance where we use it However also requires extra data on top of the common set. Is it right to try think about each usage instance as a different set of data being queried for, since there is extra data on top of the common set? Or should we try "subclassing" here? I'm interested to hear if anyone has any thoughts on this.
I can see a couple different ways to achieve something like inheritance, but they seem messy. Maybe just defining the common query bits as regular data and merging the common bits into each components query with their unique bits.
@gardnervickers It's kind of up to you on the data front. You could combine the data together in to the objects in the db tables, and just query for what you need where you need it. If the "other" data is in a different format that requires more complex joins you might consider querying both in the component and doing the combination via helper functions in the UI. Lots of approaches. The trade-offs really depend on the data.
if that additional data is common to every component, you could also maybe solve that by using links
@gardnervickers @tony.kay I also think that problem touches a little on some thoughts Iβve been having. it seems to me thatβs a very nice use case for the existence of mixins
@anmonteiro we should talk about that sometime. I'd be interested in brainstorming about the use-cases and approaches
Combining the data together into objects in the DB tables is the approach we went with which has worked out quite well.
Is there a way to get at the parser/reconciler for an untangled client app? I'd like to run some arbitrary queries against it in the repl, trying to figure out why data isn't propagating to components.
seems like every time I move components around I break something π
@grzm Are you using InitialAppState? ...that helps
That's one more thing to check. Thanks for the reminder
Running arbitrary queries is simple. Basically use db->tree
on your app state
there is an app-state function, I believe, for untangled client. The "parser" is just db->tree
with minor add-ons to deal with things that happen for UI refresh queries that don't run from root
We've found the co-location of initial app state in the components saves us a ton of work. I highly recommend it.
Yeah, that's what I've been doing.
The only place you get bitten is on server data that you don't integrate into the db correctly, and using untangled-spec to verify those functions work based on sample data is recommended as well.
(om.next/app-state (:reconciler @myapp.main/app))
something like that, yeah
I'd write a helper function and put it in the user.cljs file
something that pprint's the output of db->tree against the Root query
Yeah, that part of the app hasn't changed. I added a leaf component, updated the former leaf query to use get-query on the new leaf
Yeah, that makes sense. The log-app-state helper is very helpful
The only other gotcha that I've seen is if a component ONLY has a link query (e.g. [ [:a '_] ]
) then you MUST have something in the app state where that component lives (e.g. an empty map) or db->tree
won't go into the component at all
Don't have any link queries, so that's likely not it.
I want to make some kind of Chrome tool Om DB debug tool
like, click on a component and see it's composed query, view app-state tables, run arbitrary queries
just haven't found the time. Figwheel has that nice "jump to source" feature that would be fun to leverage on component clicks, too (imagine a kb shortcut like CTRL-J that turns on click detection for UI components, then jumps your editor to the source)
@adambros did some proof-of-concept on that one
@tony.kay Iβve also been thinking about that
especially after seeing this: https://github.com/cyclejs/cyclejs/tree/master/devtool
Yeah, all sorts of exciting things to do π
you guys are amazing and inspiring π
Do you happen to know if anyone has written a Chrome plugin with cljs?
I'm happy when I get a form to have good validation π
Ah: https://nvbn.github.io/2014/12/07/chrome-extension-clojurescript/
And duh, I use this one: https://github.com/binaryage/cljs-devtools
I was just about to say that
if only days had 48+ hours
π
we could all shave more yaks
or pick more low-hanging fruit
some of this stuff is dead simple
db->tree
(om.next/db->tree query some-data app-state-db)
Given a query expression, some data in the default database format, some application state data in the default database format, denormalize it. This will replace all ident link nodes with their actual data recursively. This is useful in parse in order to avoid manually joining in nested relationships.
I'm really hazy on the distinction between some-data and app-state-db. How are they different? (I'm not doubting they are)
actually, that's more of an om question, isn't it
(defn app-state [] (om/app-state (:reconciler @main/app)))
(defn app-query
([query]
(let [state @(app-state)]
(pprint (om/db->tree query state state))))
([query path]
(let [state @(app-state)]
(pprint (om/db->tree query (get-in state path) state)))))
There's my helper function. Seems to work.
And it's confirming for me that something's wrong π
yep. that looks about right
the some-data part is because the alg is recursive and can pass itself subsets of the db, if I remember correctly
Yup. Some gentlemen in #om helped me attain a bit more enlightenment, at least momentarily π
there's also a nice function in Om called focus-query
I think. You can use that to prune off bits of the query that you don't care about, which could make it a bit easier to read your result.
There's so much goodness around all of this, and it's just so hard for me to find π
e.g. you give it a path, and it trims all the cruft except that path
Try this:
@grzm This will give you the focused query to a specific class (if it has multiple paths it won't work right, but often useful)
Thanks, man π
just pass it a class. The other indexer functions can be helpful too
Just read the Om code near class->any
ref->any
is handy if you can target by some keyword that is in the component's query:
(om/ref->any (:reconciler @app) :label)
will return some component that has :label in it's query
If you namespace your props well, then that one can be quite useful
@grzm This one lets you do (dump-query-kw :user/name)
(and works well if you know only one such component is on screen that queries for that kw
combine those into more helpers and you should be debugging like a champ
Nice
I should integrate those into the templates π
for completeness:
(defn dump-query [comp]
(let [component (om/class->any (:reconciler @app) comp)]
(om/full-query component)))
(defn dump-query-kw [kw]
(let [component (om/ref->any (:reconciler @app) kw)]
(om/full-query component)))
(defn q
"Run the query of the given UI class and return the result as a map of the query that was run and the data that was returned.
NOTE: If the component is mounted in several places on the UI, you may not get the expected result. Be sure to check
the QUERY part of the result to see the query used."
[ui-class]
(let [query (dump-query ui-class)
state @(om/app-state (:reconciler @app))]
{:QUERY query
:RESULT (om/db->tree query state state)}))
(defn qkw
"Find a component that uses the given keyword in its query, then run that query against the app database and show
the result. NOTE: If more than one component matches, your results may vary. Be sure to examine the query that
was used."
[query-kw]
(let [query (dump-query-kw query-kw)
state @(om/app-state (:reconciler @app))]
{:QUERY query
:RESULT (om/db->tree query state state)}))
I've pushed version 1.0.0 of our untangled template to clojars. You should now be able to do:
lein new untangled dirname
to generate an untangled project.
Options to include extra stuff:
lein new untangled $projectName -- [:devcards] [:server] [:all]
not sure how well the options work, but I've at least reasonably tested the default client generation
ping me for untangled-template bugs/questions/w.e as i wrote most of it