untangled

NEW CHANNEL: #fulcro
tony.kay 2016-09-16T05:10:25.000319Z

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

tony.kay 2016-09-16T05:34:30.000321Z

OK, recipe complete, everything working.

gardnervickers 2016-09-16T12:33:19.000322Z

Very cool, we were just talking about how to manage CSS on our components.

gardnervickers 2016-09-16T12:51:48.000323Z

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.

gardnervickers 2016-09-16T12:53:23.000324Z

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.

tony.kay 2016-09-16T15:41:16.000325Z

@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.

anmonteiro 2016-09-16T15:42:23.000326Z

if that additional data is common to every component, you could also maybe solve that by using links

anmonteiro 2016-09-16T15:43:20.000327Z

@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

tony.kay 2016-09-16T15:44:05.000328Z

@anmonteiro we should talk about that sometime. I'd be interested in brainstorming about the use-cases and approaches

gardnervickers 2016-09-16T16:16:51.000329Z

Combining the data together into objects in the DB tables is the approach we went with which has worked out quite well.

grzm 2016-09-16T17:56:40.000330Z

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.

grzm 2016-09-16T18:00:33.000331Z

seems like every time I move components around I break something πŸ˜•

tony.kay 2016-09-16T18:01:02.000332Z

@grzm Are you using InitialAppState? ...that helps

grzm 2016-09-16T18:01:35.000334Z

That's one more thing to check. Thanks for the reminder

tony.kay 2016-09-16T18:01:37.000335Z

Running arbitrary queries is simple. Basically use db->tree on your app state

tony.kay 2016-09-16T18:02:42.000336Z

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

tony.kay 2016-09-16T18:03:30.000337Z

We've found the co-location of initial app state in the components saves us a ton of work. I highly recommend it.

grzm 2016-09-16T18:04:07.000338Z

Yeah, that's what I've been doing.

tony.kay 2016-09-16T18:04:35.000339Z

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.

grzm 2016-09-16T18:04:45.000340Z

(om.next/app-state (:reconciler @myapp.main/app))

tony.kay 2016-09-16T18:05:01.000341Z

something like that, yeah

tony.kay 2016-09-16T18:06:00.000342Z

I'd write a helper function and put it in the user.cljs file

tony.kay 2016-09-16T18:06:34.000343Z

something that pprint's the output of db->tree against the Root query

grzm 2016-09-16T18:06:35.000344Z

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

grzm 2016-09-16T18:06:52.000345Z

Yeah, that makes sense. The log-app-state helper is very helpful

tony.kay 2016-09-16T18:07:38.000346Z

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

grzm 2016-09-16T18:08:41.000347Z

Don't have any link queries, so that's likely not it.

tony.kay 2016-09-16T18:09:05.000348Z

I want to make some kind of Chrome tool Om DB debug tool

πŸ‘ 1
tony.kay 2016-09-16T18:09:23.000349Z

like, click on a component and see it's composed query, view app-state tables, run arbitrary queries

tony.kay 2016-09-16T18:10:42.000351Z

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)

tony.kay 2016-09-16T18:11:11.000352Z

@adambros did some proof-of-concept on that one

anmonteiro 2016-09-16T18:11:59.000353Z

@tony.kay I’ve also been thinking about that

anmonteiro 2016-09-16T18:12:04.000354Z

especially after seeing this: https://github.com/cyclejs/cyclejs/tree/master/devtool

tony.kay 2016-09-16T18:12:42.000356Z

Yeah, all sorts of exciting things to do πŸ™‚

grzm 2016-09-16T18:13:59.000357Z

you guys are amazing and inspiring πŸ˜‰

tony.kay 2016-09-16T18:14:10.000358Z

Do you happen to know if anyone has written a Chrome plugin with cljs?

grzm 2016-09-16T18:14:18.000359Z

I'm happy when I get a form to have good validation πŸ™‚

tony.kay 2016-09-16T18:15:56.000361Z

And duh, I use this one: https://github.com/binaryage/cljs-devtools

mahinshaw 2016-09-16T18:16:22.000364Z

I was just about to say that

anmonteiro 2016-09-16T18:17:41.000365Z

if only days had 48+ hours

anmonteiro 2016-09-16T18:17:42.000366Z

πŸ™‚

anmonteiro 2016-09-16T18:17:51.000367Z

we could all shave more yaks

tony.kay 2016-09-16T18:19:19.000368Z

or pick more low-hanging fruit

tony.kay 2016-09-16T18:19:46.000369Z

some of this stuff is dead simple

grzm 2016-09-16T18:41:03.000370Z

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.

grzm 2016-09-16T18:42:00.000371Z

I'm really hazy on the distinction between some-data and app-state-db. How are they different? (I'm not doubting they are)

grzm 2016-09-16T18:42:20.000372Z

actually, that's more of an om question, isn't it

grzm 2016-09-16T19:08:53.000373Z

(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)))))

grzm 2016-09-16T19:09:07.000374Z

There's my helper function. Seems to work.

grzm 2016-09-16T19:09:49.000375Z

And it's confirming for me that something's wrong πŸ™‚

tony.kay 2016-09-16T19:18:51.000379Z

yep. that looks about right

tony.kay 2016-09-16T19:19:10.000380Z

the some-data part is because the alg is recursive and can pass itself subsets of the db, if I remember correctly

grzm 2016-09-16T19:20:33.000381Z

Yup. Some gentlemen in #om helped me attain a bit more enlightenment, at least momentarily πŸ™‚

tony.kay 2016-09-16T19:22:03.000382Z

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.

grzm 2016-09-16T19:22:55.000383Z

There's so much goodness around all of this, and it's just so hard for me to find πŸ˜•

tony.kay 2016-09-16T19:23:08.000384Z

e.g. you give it a path, and it trims all the cruft except that path

tony.kay 2016-09-16T19:34:20.000385Z

Try this:

tony.kay 2016-09-16T19:34:56.000387Z

@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)

grzm 2016-09-16T19:35:22.000388Z

Thanks, man πŸ™‚

tony.kay 2016-09-16T19:35:48.000389Z

just pass it a class. The other indexer functions can be helpful too

tony.kay 2016-09-16T19:36:00.000391Z

Just read the Om code near class->any

tony.kay 2016-09-16T19:38:57.000392Z

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

tony.kay 2016-09-16T19:39:19.000393Z

If you namespace your props well, then that one can be quite useful

tony.kay 2016-09-16T19:41:30.000395Z

@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

tony.kay 2016-09-16T19:41:49.000396Z

combine those into more helpers and you should be debugging like a champ

grzm 2016-09-16T19:45:10.000397Z

Nice

tony.kay 2016-09-16T19:45:52.000398Z

I should integrate those into the templates πŸ™‚

tony.kay 2016-09-16T19:53:21.000399Z

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)}))

tony.kay 2016-09-16T20:29:52.000400Z

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]

tony.kay 2016-09-16T20:30:25.000401Z

not sure how well the options work, but I've at least reasonably tested the default client generation

adambros 2016-09-16T21:03:08.000402Z

ping me for untangled-template bugs/questions/w.e as i wrote most of it