untangled

NEW CHANNEL: #fulcro
robert-stuttaford 2017-04-14T14:42:57.632237Z

hey @tony.kay šŸ™‚ say i wanted to get started with Untangled, using everything according to the latest understanding. is there perhaps a lein template that brings it all together? particularly interested in the UI stuff

gardnervickers 2017-04-14T14:53:59.806806Z

@robert-stuttaford I'm not sure if there is a lein template to generate a project but there is this template project which is pretty great to clone and modify for your use case https://github.com/untangled-web/untangled-template/blob/develop/README.md

robert-stuttaford 2017-04-14T14:54:53.820982Z

thank you!

robert-stuttaford 2017-04-14T14:56:03.839981Z

i see this one doesnā€™t have the support-viewer or the new ui stuff, iā€™m guessing it predates those

robert-stuttaford 2017-04-14T14:56:18.843899Z

by ui i mean these goodies https://untangled-web.github.io/untangled-ui/

gardnervickers 2017-04-14T15:06:41.015985Z

Ahh yup, admittedly I haven't used the UI components much yet or the support viewer. The form support is very interesting though.

gardnervickers 2017-04-14T15:10:52.083108Z

@robert-stuttaford if you're just getting started with Om.next and Untangled I highly recommend the Untangled video series. It was pretty crucial to my understanding of Om.next and the boundaries Untangled hooks into.

robert-stuttaford 2017-04-14T15:11:05.086703Z

yeah, iā€™m looking into those now

robert-stuttaford 2017-04-14T15:11:33.094208Z

i have mucked around with omn before, but found the parser stuff to be quite heavy. keen to see Untā€™s take

robert-stuttaford 2017-04-14T15:12:14.104623Z

my need is that we need to build a lot of db admin stuff pretty quickly and iā€™m really not interested in reinventing anything. done enough of that šŸ™‚

robert-stuttaford 2017-04-14T15:12:33.110341Z

just looking for something with batteries to get stuff built

gardnervickers 2017-04-14T15:18:03.197668Z

Yup building the parser emitters in pure Om.Next on the client side was rather cumbersome, I quite like Untangled's approach. Some things that bit us early on were not having a great idea of what our entities were in the system (data backed by an ident), and having too many different ident backed data structures. We've since taken a different approach where we use normalization/idents only where it's necessary for performance or deduplication. Keeping the count of different ident types in your application low helps reasoning tremendously.

robert-stuttaford 2017-04-14T15:18:40.207296Z

happily we understand our data tremendously well; itā€™s 4 years old šŸ™‚

robert-stuttaford 2017-04-14T15:19:00.212565Z

weā€™ve lots of fixed-scope tools to build

robert-stuttaford 2017-04-14T15:19:08.214668Z

which each focus on a small part of the whole

robert-stuttaford 2017-04-14T15:19:56.227284Z

our tooling has lagged behind our primary feature dev and operation quite a lot. so we know our data, and what we want to do to manage it, we just need a sensible, scalable toolkit to get it done

robert-stuttaford 2017-04-14T15:20:53.242925Z

literally things as simple as capturing SSO integration configs, support tools like reviewing a userā€™s profile, emails weā€™ve sent them, stuff like that

gardnervickers 2017-04-14T15:24:10.295304Z

Ah yup, that sounds like the sweet spot for Om.Next/Untangled. Developing both your UI and data-model at the same time is more difficult than other frameworks because you need to be fairly precise with queries describing your component's data dependencies. Large changes take longer because you need to not only change the UI logic but also the query layout among several components.

gardnervickers 2017-04-14T15:24:49.306110Z

We've found that once the data model solidifies into something that makes sense, change becomes much easier and more reliable.

tony.kay 2017-04-14T15:26:09.327888Z

Iā€™m interested @gardnervickers: do you choose not to use idents on things that need to update in the UI? Or are you saying that with nested data that comes from the server for pure display you donā€™t bother?

tony.kay 2017-04-14T15:27:07.343613Z

@robert-stuttaford The untangled-ui project needs a little polish, but it is usable as-is. Just add it as a dependency and use the stuff outlined in the devcards. The forms stuff doesnā€™t have any server adapters yet, but the ā€œdiff protocolā€ is sends is really easy to reason about.

tony.kay 2017-04-14T15:28:00.358330Z

Iā€™d say the very biggest problem Iā€™ve seen with new users is not leveraging union queries to keep the query low-overhead. The new defrouter macro facilitates that. If you just make one big query without that your UI performance will suck

robert-stuttaford 2017-04-14T15:28:01.358822Z

you mean the stuff shown in https://untangled-web.github.io/untangled-ui/guide-css.html ?

tony.kay 2017-04-14T15:28:16.362802Z

and the components guide

tony.kay 2017-04-14T15:28:32.367371Z

the former is pure css, the latter is more wrappers that emit dom +css

robert-stuttaford 2017-04-14T15:28:54.373242Z

cool. iā€™m going to work through all your videos, and read through all the devcards, and then try building something. i will have questions šŸ™‚

robert-stuttaford 2017-04-14T15:29:21.380705Z

i guess it doesnā€™t matter (because itā€™s easy for me to do differently), but why did you stick with the verbose om.dom rendering code?

tony.kay 2017-04-14T15:29:31.383378Z

note that the Untangled in the Large videos mention making defrouterā€¦defrouter is now a formal part of Untangled, with a slightly improved API than shown there

tony.kay 2017-04-14T15:29:47.387575Z

server-side rendering

tony.kay 2017-04-14T15:29:56.390042Z

not adding more deps for ppl

tony.kay 2017-04-14T15:30:05.392784Z

the current syntax doesnā€™t bother me šŸ™‚

tony.kay 2017-04-14T15:30:27.398978Z

so, basically what you said: it is easy for you to do differently

robert-stuttaford 2017-04-14T15:30:29.399653Z

ah šŸ™‚ it bothers me, heh. iā€™m all for concision in naturally verbose code. html is just one of those.

robert-stuttaford 2017-04-14T15:30:45.404111Z

yep. happy to deal with it my own way!

tony.kay 2017-04-14T15:30:48.405028Z

overhead as well. didnā€™t want to add possible performance problems

robert-stuttaford 2017-04-14T15:31:19.413692Z

really nice work btw. i can see a huge effort here. whether we end up using it or not, kudos!

tony.kay 2017-04-14T15:31:25.415285Z

thanks

gardnervickers 2017-04-14T15:31:43.420305Z

@tony.kay For example, if our UI tree looks like

{:users {:user/id 1 :user/phone {:phone/type :work :phone/number "4014770248"}}}
We would have a table of :user/by-id but we wouldnā€™t, for example have a table of :phone.number/by-number.

gardnervickers 2017-04-14T15:32:19.430179Z

That might be a bad example, but essentially we were finding that we were using idents/normalization where it didnā€™t benefit us all that much.

gardnervickers 2017-04-14T15:32:43.436962Z

Not every level of map nesting needs to be normalized

robert-stuttaford 2017-04-14T15:32:49.438481Z

kinda like only using :avet in datomic when you genuinely need to

tony.kay 2017-04-14T15:32:51.438960Z

I see. The normalization really helps with things like ā€œforms supportā€

tony.kay 2017-04-14T15:33:08.443570Z

e.g. I could not easily write something like that without normalization

tony.kay 2017-04-14T15:33:21.447306Z

it would be a lot more complex (youā€™d have to be able to declare ā€œtype ofā€ nesting, I think)

gardnervickers 2017-04-14T15:33:30.449510Z

So that only works on flat maps with no nesting?

tony.kay 2017-04-14T15:34:11.460364Z

the forms support lets you wrap normalized entities, because I can easily write the mutate/read code ā€œfor youā€. If you arbitrarily denormalize, it would be a lot more complicated

tony.kay 2017-04-14T15:34:21.463480Z

In general, if it is editable, Iā€™d recommend normalizing it.

tony.kay 2017-04-14T15:35:10.476297Z

and for new users, Iā€™d recommend making something with defrouter firstā€¦learn how to do that immediately. Iā€™ve seen too many people write an app that queries the ā€œwhole UIā€ for every frame, and that leads to unusable UIs

tony.kay 2017-04-14T15:35:43.486034Z

I need to make a new stater tutorial and video series that stresses that. I think it is the most common place where early failure will happen with new users

robert-stuttaford 2017-04-14T15:35:52.488290Z

is defrouter covered in any of the docs or videos?

tony.kay 2017-04-14T15:36:20.495987Z

the Untangled in the Large shows what it does (I made that while developing it). The API is a bit improved, adn the devguide covers it

tony.kay 2017-04-14T15:36:22.496590Z

in Routing

robert-stuttaford 2017-04-14T15:36:34.500072Z

this is a talk?

tony.kay 2017-04-14T15:36:52.505034Z

https://www.youtube.com/my_videos?o=U&sq=Untangled

robert-stuttaford 2017-04-14T15:36:56.506121Z

thank you!

tony.kay 2017-04-14T15:37:13.510649Z

that last one is the in-the-large video on ui routing

gardnervickers 2017-04-14T15:37:35.516615Z

@tony.kay Iā€™ll try and get a devcard together this weekend showcasing how we using an approach similar to initial-state to compose url->app-state and app-state->url serialization/deserialization for routing. We even hook into :tempid migration to automatically place the correct resource URL when the server returns the generated ID.

tony.kay 2017-04-14T15:38:25.530015Z

Basically, a Union query is one in which only a single part of the UI (and query) is visible at onceā€¦like a switching mechanism (it can also be used for hetero lists). Only one branch is evalā€™d at once, so it is a way to ensure your UI only queries for the part on-screen.

tony.kay 2017-04-14T15:38:54.537481Z

if you donā€™t use them, you end up querying everything that could possibly be on screen every frameā€¦which can be horribly slow.

tony.kay 2017-04-14T15:39:19.544434Z

@gardnervickers Nice. Cookbook?

tony.kay 2017-04-14T15:39:51.552910Z

BTW, Iā€™m combining all the recipes into a devcards project. I could give you access to that if youā€™d like to put it there. It is private while Iā€™m working on it

tony.kay 2017-04-14T15:41:04.571888Z

OH, devcards wonā€™t work for that. You cannot change the URI or devcards pukes

gardnervickers 2017-04-14T15:41:30.578805Z

Oh interesting. Let me get a gist up first, not sure if Iā€™ll have time to refine it much since Iā€™m with the family for Easter this weekend. Iā€™ll keep you in the loop.

tony.kay 2017-04-14T15:41:39.581306Z

ok, cool.

tony.kay 2017-04-14T15:43:55.616545Z

@robert-stuttaford So, if you can get a handle on the app database graph format (via the devguide) and queries, then most of the rest falls into place. Perhaps watching the https://www.youtube.com/watch?v=mT4jJHf929Q&t=1s&list=PLVi9lDx-4C_T_gsmBQ_2gztvk6h_Usw6R&index=8 video about the data lifecycle will tie things together as well.

robert-stuttaford 2017-04-14T15:44:15.621617Z

iā€™m sure that it will, thank you! i have that video queued

tony.kay 2017-04-14T15:44:55.632243Z

There really are not very many core concepts. I think it gets ultra-simple once youā€™ve got those bits down.

robert-stuttaford 2017-04-14T15:45:00.633638Z

i have lots of om.previous and rum experience, so i understand dealing with the normalised database vs ui tree graph mismatch quite well

robert-stuttaford 2017-04-14T15:45:33.642778Z

just need to become familiar with the idioms and overall organisation, and build that first something

tony.kay 2017-04-14T15:46:37.660434Z

yeah, Iā€™m really stoked with the result. Every time I go to do something Iā€™m amazed at how easily it falls out, and how little (incidental) complexity there is.

tony.kay 2017-04-14T15:47:27.673777Z

@gardnervickers the other bit about normalization: you canā€™t easily relocate a UI component that isnā€™t normalized, because the mutations become non-portable.

robert-stuttaford 2017-04-14T15:47:36.676596Z

what iā€™m hoping for is that we get one or two tools built, establishing some patterns, and then we can tear through the backlog from there. like i said earlier, we know our data well (and itā€™s all in Datomic šŸ˜Ž)

tony.kay 2017-04-14T15:47:46.679161Z

nice

tony.kay 2017-04-14T15:48:21.688580Z

Oh, also make sure you know about defmutation. Much nicer IDE support for the mutations. That is another one I show in-the-large that is now part of the lib.

šŸ‘ 1
gardnervickers 2017-04-14T15:48:33.692034Z

@tony.kay Just to clarify, we do use normalization quite a bit. We just donā€™t normalize things that are not shared but still nested.

tony.kay 2017-04-14T15:49:11.701830Z

got it. Yeah, I just wanted to point out the important aspects of normalizationā€¦more for whoever might be lurking and not speaking šŸ˜‰

robert-stuttaford 2017-04-14T16:07:20.006536Z

it feels like overkill for the toy app (root + list + item), but i can see the value for non-trivial apps

2017-04-14T16:10:55.063795Z

@tony.kay The first two video links after the routing guide link where not public links - will you share those again?

tony.kay 2017-04-14T16:12:32.089292Z

@robert-stuttaford it does feel like overkill for small thingsā€¦but nothing stays small šŸ™‚

robert-stuttaford 2017-04-14T16:12:40.091378Z

nothing useful šŸ™‚

tony.kay 2017-04-14T16:12:52.094384Z

@donmullen ooops

tony.kay 2017-04-14T16:13:46.108571Z

help me outā€¦which ones in particular?

tony.kay 2017-04-14T16:15:02.128775Z

oh, I seeā€¦

tony.kay 2017-04-14T16:15:45.140542Z

thatā€™s the whole playlist

tony.kay 2017-04-14T16:16:00.144466Z

https://youtu.be/j-_itpXEo6w

2017-04-14T16:16:03.145090Z

Great - thanks.

tony.kay 2017-04-14T16:16:04.145476Z

thatā€™s the routing one

tony.kay 2017-04-14T16:16:13.147481Z

those work?

tony.kay 2017-04-14T16:18:05.177207Z

and for anyone reading the stuff aboveā€¦the defrouter thing is important, but you arenā€™t going to understand it until you get queries/ident/db format. Those foundational pieces are necessary to build anything. But then make sure you get defrouter.

pedroteixeira 2017-04-14T17:16:10.100875Z

hello! watched a few talks about untangled today, thanks a lot for sharing the knowledge and the code! a question here.. any one knows of an example using a library such as martinklepsch/derivatives (used by rum) with untangled? I'm considering using untangled for a next project, but trying to see a solution for managing derived values in more declarative ways (i.e. instead of having to call functions on various mutations) - looking into re-frame`s reaction abstraction as well

pedroteixeira 2017-04-14T17:20:36.174352Z

i think for Om Next it would be just an impl. detail of a mutation.. but was curious if there is already such a pattern in the untangled ecosystem or someone working on a reference integration šŸ™‚

gardnervickers 2017-04-14T17:25:09.246175Z

@pedroteixeira In Om.Next, the read parsers would be what generate your ā€œderivativesā€. Untangled provides a default read parser that is backed by the app db, so you need to generate your ā€œderivativesā€ using a mutation.

pedroteixeira 2017-04-14T17:32:01.357788Z

hm, ok! starting to read some of the code, thanks for the pointer. I was curious if there were examples of expressing derived values across the app state. For ex: instead of having to call update functions in various mutations, hook up something more generic in Om.Next to listen to changes in data and apply futher changes according to rules but in the same "transact".

tony.kay 2017-04-14T18:06:19.916455Z

Right, derivatives are just represented in the graph as data. Everything goes in mutations. Of course, if it is a simple derivation, you could calculate it as part of the view.

tony.kay 2017-04-14T18:08:15.948088Z

The basic idea is that you have to make it somewhereā€¦if you do it in a parser, then you add the complexity of figuring out when (and caching for performance), and mix that into a parser abstraction with graph navigation. In Untangled, the idea is that since youā€™re mostly going to want to cache the result of your computation, why not just make that the ā€œwayā€, and elide the need for more complex mechanisms. Lifecycles of views are pretty easy to manage, in that you have mount/unmount from React, and UI navigation events (e.g. clicking on a tab to go to X).

tony.kay 2017-04-14T18:09:20.965544Z

Generate the ā€œinitial viewā€ where it makes sense (e.g. on load or view routing), update it where it makes sense. It is your data, and limiting the ways in which you have to think about it keeps things naturally simple for the most part

šŸ‘ 1
urbank 2017-04-14T20:22:57.929398Z

It's probably just me, and tony.kay has helped elucidate quite a bit already, but I find untangled a bit daunting to use for an application where everything in the database is editable all the time and editable from multiple places in the app at the same time. It kind of breaks normalization as soon as one edit component has state in the app-db.

urbank 2017-04-14T20:23:34.937783Z

On the other hand, the app is also liable to need caching at some point, so perhaps I might as well bite the bullet now.

urbank 2017-04-14T20:25:03.957809Z

I'm thinking that perhaps I can compute everything on the fly if I use virtualization like https://github.com/bvaughn/react-virtualized

tony.kay 2017-04-14T21:25:24.723560Z

@urbank I really donā€™t know what you mean ā€œIt kind of breaks normalizationā€ā€¦you mean you might have to have more than one graph edge that points to the same thing? That is still normalizedā€¦but it does mean you have to manage more than one link, but the fact is that you can still locally reason about those graph edges from that componentā€™s viewpoint. It is true that a list of idents might need an update in multiple places, but again that is a composeable task: compose together the list refreshed into the data lifecycle mutation of the mutations that post-mutate of the load

tony.kay 2017-04-14T21:25:50.728622Z

OR use a root (link) query and just keep shared lists in the root node

tony.kay 2017-04-14T21:26:18.733803Z

e.g. [ [:my-list '_] ] in a query anywhere in the app pulls in the edge from the root node

tony.kay 2017-04-14T21:27:12.743760Z

so, if it is true that you need to use the same ā€œcollectionā€ or ā€œedgeā€ from multiple places you can just do thatā€¦.or even join on some specific entityā€™s edge [{[:table id] (om/get-query Table)}]

tony.kay 2017-04-14T21:27:51.751377Z

to get the entity, then pull out the edge from those propsā€¦all sorts of possibilities

urbank 2017-04-14T21:45:29.941379Z

@tony.kay Yeah, sorry - loose choice of words. Mostly talking about components with opaque state in the app-db (like calendar) that edits some other part of the app-db via a callback, but internally updates its "local state". If the entity it targets is also edited by another component, similar to the calendar, the calendar has to be updated whenever that other component updates the entity, else it displays stale data.

urbank 2017-04-14T21:48:31.972992Z

Sill, your post gives me ideas. I'll continue playing around with it