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
@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
thank you!
i see this one doesnāt have the support-viewer or the new ui stuff, iām guessing it predates those
by ui i mean these goodies https://untangled-web.github.io/untangled-ui/
Ahh yup, admittedly I haven't used the UI components much yet or the support viewer. The form support is very interesting though.
@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.
yeah, iām looking into those now
i have mucked around with omn before, but found the parser stuff to be quite heavy. keen to see Untās take
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 š
just looking for something with batteries to get stuff built
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.
happily we understand our data tremendously well; itās 4 years old š
weāve lots of fixed-scope tools to build
which each focus on a small part of the whole
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
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
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.
We've found that once the data model solidifies into something that makes sense, change becomes much easier and more reliable.
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?
@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.
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
you mean the stuff shown in https://untangled-web.github.io/untangled-ui/guide-css.html ?
and the components guide
the former is pure css, the latter is more wrappers that emit dom +css
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 š
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?
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
server-side rendering
not adding more deps for ppl
the current syntax doesnāt bother me š
so, basically what you said: it is easy for you to do differently
ah š it bothers me, heh. iām all for concision in naturally verbose code. html is just one of those.
yep. happy to deal with it my own way!
overhead as well. didnāt want to add possible performance problems
really nice work btw. i can see a huge effort here. whether we end up using it or not, kudos!
thanks
@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
.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.
Not every level of map nesting needs to be normalized
kinda like only using :avet in datomic when you genuinely need to
I see. The normalization really helps with things like āforms supportā
e.g. I could not easily write something like that without normalization
it would be a lot more complex (youād have to be able to declare ātype ofā nesting, I think)
So that only works on flat maps with no nesting?
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
In general, if it is editable, Iād recommend normalizing it.
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
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
is defrouter covered in any of the docs or videos?
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
in Routing
this is a talk?
http://untangled-web.github.io/untangled/guide.html#!/untangled_devguide.M15_Routing_UI
thank you!
that last one is the in-the-large video on ui routing
@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.
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.
if you donāt use them, you end up querying everything that could possibly be on screen every frameā¦which can be horribly slow.
@gardnervickers Nice. Cookbook?
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
OH, devcards wonāt work for that. You cannot change the URI or devcards pukes
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.
ok, cool.
@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.
iām sure that it will, thank you! i have that video queued
There really are not very many core concepts. I think it gets ultra-simple once youāve got those bits down.
i have lots of om.previous and rum experience, so i understand dealing with the normalised database vs ui tree graph mismatch quite well
just need to become familiar with the idioms and overall organisation, and build that first something
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.
@gardnervickers the other bit about normalization: you canāt easily relocate a UI component that isnāt normalized, because the mutations become non-portable.
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 š)
nice
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.
@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.
got it. Yeah, I just wanted to point out the important aspects of normalizationā¦more for whoever might be lurking and not speaking š
it feels like overkill for the toy app (root + list + item), but i can see the value for non-trivial apps
@tony.kay The first two video links after the routing guide link where not public links - will you share those again?
@robert-stuttaford it does feel like overkill for small thingsā¦but nothing stays small š
nothing useful š
@donmullen ooops
help me outā¦which ones in particular?
oh, I seeā¦
https://www.youtube.com/playlist?list=PLVi9lDx-4C_T_gsmBQ_2gztvk6h_Usw6R
thatās the whole playlist
Great - thanks.
thatās the routing one
those work?
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.
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
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 š
@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.
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".
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.
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).
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
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.
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.
I'm thinking that perhaps I can compute everything on the fly if I use virtualization like https://github.com/bvaughn/react-virtualized
@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
OR use a root (link) query and just keep shared lists in the root node
e.g. [ [:my-list '_] ]
in a query anywhere in the app pulls in the edge from the root node
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)}]
to get the entity, then pull out the edge from those propsā¦all sorts of possibilities
@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.
Sill, your post gives me ideas. I'll continue playing around with it