Fork me on GitHub
#untangled
<
2017-03-08
>
tobias00:03:03

Is there any reason why client.data-fetch/load-data doesn't allow post-mutation-params?

tony.kay01:03:02

@tobias It was the earliest API call for data fetching, and has been superceeded by load. It can technically do things in a single call that load cannot, but I was leaning towards deprecating it, so it has not been upgraded with options that load has.

tony.kay01:03:14

Let me say that in a different way: load should do everything you need. load-data has a different, but more finicky API.

tony.kay01:03:18

load-field is a special (and common) case of loading some sub-portion of the UI tree from the perspective of a component that is on-screen. It writes the more complicated load that you would need

tony.kay01:03:11

ultimately you're always wanting to load some portion of the graph (as a tree). Technically, load can accomplish anything you'd want to do from that problem statement. load-field is a useful specialized case. load-data was just the first attempt at load.

tony.kay01:03:39

but I don't like changing API out from under people, so instead of "fixing" load-data and breaking people, I added load

urbank14:03:18

How do you handle a case where the initial state of some component is derived from another component. So for example: the todo list component's initialState adds a list of todos to the database. Now we want to add a component with an initialState that is a function of the TodoList's initial state. How would this be done?

urbank14:03:55

I hope it's somewhat clear what I'm asking, perhaps I have to refine the question, but I've gotta run

gardnervickers15:03:46

You compose initial-state calls up the component tree just like om/get-query

tony.kay16:03:26

@urbank Perhaps you mean: how do I make some component elsewhere in the tree depend on the state in another, where the component might not be on the same branch? Well, often you just query for that other component's data and initialize the component as normal (e.g. if you want the todo list item count anywhere in the app, query for the whole table and count the entries in the UI code: [ [:todo-items/by-id '_] ]. That way your data is still "dry". Another thing you might like to do is show some sub-list, where you'd perhaps like to pre-sort/filter into a "view" of data elsewhere in the app. In that case, do the transform in initial-state of the secondary component, and just use initial-state of the primary to get the data to work on:

(defui MySortedSublist
   static uc/InitialAppState
   (initial-state [cls params] {:my-items (filter-and-sort (get-initial-state TodoList))})
etc.

tony.kay16:03:10

what @gardnervickers said is always true: you have to compose children initial state into parents...but any other kind of normal information composition is up to you and is otherwise unconstrained.

sihingkk16:03:32

hey, is it possible to have hot-reloading with changing initial-state? I have to reload app every time

tony.kay16:03:33

To preempt the question "why get-initial-state instead of initial-state?": Server-side rendering is done in Java VM, and initial-state won't be available there. get-initial-state is how you call the static protocol method on both client/server

tony.kay16:03:26

@sihingkk Not built in. but you could have figwheel trigger a function that called your initial state on root and installs it as the "new app state"

tony.kay16:03:52

but the point of hot-code reload is to be able to work on something without your app reverting to original state ๐Ÿ˜œ

tony.kay16:03:13

i.e. working on tuning up a dialog, you wouldn't want the app hot-code reloading back to login

tony.kay16:03:37

So, you'd be better off with a REPL function you could call...but then why not just hit reload ๐Ÿ™‚

tony.kay16:03:08

I guess might get a few secs of speed.

tony.kay16:03:28

It would also be interesting to do a "targeted" re-init of state...e.g. just some subtree

tony.kay16:03:55

you could use merge-state! for that

tony.kay16:03:34

(merge-state! @app Component (get-initial-state Component {})) should work

wildermuthn16:03:28

Hi there. Thanks for the work on Untangled! I have a question about IQueryParams. Is this disabled in the client?

wildermuthn16:03:33

Since thereโ€™s, as far as I understand it, no reader that we define in the client?

tony.kay16:03:41

IQueryParams works fine...it's just that you cannot currently plug into the parser to do anything about property params.

tony.kay16:03:45

on the client at least

tony.kay16:03:17

So, you could use dynamic queries to change the query, but the model in untangled is not to use property/join params on the client side.

tony.kay16:03:29

I made a big comment about this a few days ago...you might look in archives.

tony.kay16:03:50

around time 18:27:53

urbank18:03:07

@tony.kay Thanks! Although it seems that with the composition of the initial state the MySortedSubList component needs to be manually updated if a mutation changes the TodoList's data.

urbank18:03:46

So it wouldn't stay in sync. I imagine that could get a bit hairy if there were lots of components like MySortedSubList - finding them and keeping them all consistent

tony.kay19:03:52

@urbank It is true that if you have a global concern that you have to have global logic.

tony.kay19:03:59

This is a trade-off between stock Om Next and Untangled. In Om Next you can "customize" the query for the sublist, and then write a parser emitter that "does the right thing". This fixes the sync issue. However, it also means that now: 1. you have to write/maintain parser emitters for each component. That is less composable since your parser is tied to your UI structure (e.g. reusable components get harder) 2. You run that logic on each re-render (possibly getting quite slow on frame refresh, which leads to caching and memoization code you have to add)

tony.kay19:03:35

Note in (2) that you now have the out-of-sync problem again

tony.kay19:03:09

so, Untangled takes the approach: Make a global mutation that knows how to update derived views when tables change in some global way. Trigger those as post mutations on loads.

tony.kay19:03:33

Your cache invalidation, normally a hard problem, is now simpler: when you load, update views

tony.kay19:03:09

You also don't have arbitrary costs on UI queries (e.g. large computations on each render frame)...only when the data actually changes

urbank19:03:40

@tony.kay Yeah, that makes sense. I can definitely see the upside. So essentially a lot of the heavy lifting is done in mutations. Still in the process of absorbing the philosophy of untangled. I suppose you could still always query the tables from the root of app-db, and let components transform the data on the fly... though that might not always be the best idea.

tony.kay19:03:44

@urbank the problem with the link query on a table is you lose the metadata, and should then prob not render it with a component

tony.kay19:03:01

and you've added computational overhead into the rendering itself

tony.kay19:03:32

This is kind of a technicality, but if you render a component that has a query, the data that is passed to the component gets "marked up" via the query. In general the rule is: If you render a stateful component, you must pass it data that came from the query of that component.

tony.kay19:03:52

this is part of the UI refresh internals

tony.kay19:03:15

So, if you pull in an entire table, it is kind of a hack

tony.kay19:03:43

and you lose the "local reasoning" and refresh model to some extent

tony.kay19:03:54

so I probably shouldn't be telling ppl they can do it ๐Ÿ˜œ

urbank19:03:48

Right, so the table is for keeping the specifics of 'entities' in once place, whereas the structure that components query should be elsewhere in the db, built from references to the tables.

tony.kay19:03:25

Right. The graph is about two things: Your UI structure and entity relations.

tony.kay19:03:02

entity relations are more constrained (evolve in sync with server and the schema there)

tony.kay19:03:14

UI structure is up to your specific app usage of that data

tony.kay19:03:04

You could think of the graph being colored: one color for the "server-based schema" and one for UI-specific concerns. Perhaps another where there is overlap

tony.kay19:03:37

The ui-specific ones have a UI-specific lifecycle.

tony.kay19:03:50

and that has to be coded somewhere/somehow.

tony.kay19:03:02

kind of distilling it down to base concepts.

tony.kay19:03:44

MVC -> It goes in custom UI models and controllers Om Next -> It can go in the parser emitters Om Next/Untangled -> Represent it as information in the graph with pure functions that move you from one graph state to the next

tony.kay19:03:46

So now you entire application boils down to app-state -> app-state -> app-state (over time)

tony.kay19:03:59

whereas in the parser emitter case, you do have app-state -> app-state as more of a pure model concern, but there is also an app-state -> ui transform at each step for render.

tony.kay19:03:29

so another way to say it is that we eliminate the app-state -> ui transform because we saw the graph as an easier thing to maintain and reason about

tony.kay19:03:06

I have an Untangled feature issue on client where I do plan to add in parser composition support, so you could do the app-state->ui step if you want.

tony.kay19:03:10

not sure it is doable, but would be a nice add

urbank19:03:39

Thanks, that cleared up a lot of things for me

tony.kay20:03:09

@wildermuthn Actually time stamp 21:32:38 is a better spot. Technically you could use query params to change the query...more clarity there, hopefully, Feel free to ask more questions

wildermuthn20:03:29

Thanks! set-query! works, but doesn't automaticall requery the remote in my setup. Looks like I need to call one of the load fns?

tony.kay20:03:08

So you're coming from stock Om Next

tony.kay20:03:08

set-query! does technically do that under the covers via gather-sends, but Untangled doesn't model remote loads how (stock) Om Next does.

tony.kay20:03:23

because then you have to write a parser for the whole mess

wildermuthn20:03:31

Understandable. I think the right path is to use the :ui states as params?

tony.kay20:03:08

:ui/... keywords are automatically eliminated from queries

tony.kay20:03:11

when remoting them

tony.kay20:03:14

as a convenience

tony.kay20:03:22

no need to see ui-only query portions to the server

tony.kay20:03:41

nothing at all to do with params

tony.kay20:03:03

So, it seems you have several things going on in your head at the moment...we could tease them apart ๐Ÿ™‚

wildermuthn20:03:45

Still working my way through the Untangled docs. Just thinking on how to get the same kind of functionality that we see in Om Next's wiki example for the autocomplete

tony.kay20:03:05

In general, set-query! will have nothing to do with loads in Untangled. You could use it to change the query dynamically.

tony.kay20:03:09

Ah, ok, specific problem

tony.kay20:03:47

Yes, on a debounced UI event (e.g. field is changing) you'd trigger a load (which you can embed in a mutation via load-action).

wildermuthn20:03:06

Ok, that makes sense.

tony.kay20:03:28

then use a post-mutation on the load if you needed to "fix up" the UI graph to display the results

wildermuthn20:03:29

Behind the scenes these are mutations that get passed to the server as load events?

wildermuthn20:03:38

I'll dig into the docs and videos more. Most of my questions are uninformed. :) Great videos, btw!

tony.kay20:03:31

Here's the realization about loading on-the-fly: In Om Next, to get the UI experience you want, you're always doing this: 1. Mutate the state to put a marker in that your parser can read to trigger loads 2. Write the parser bits to see that marker 3. Make sure you somehow overwrite that marker to say you're done so you don't accidently re-trigger the remote interaction So, in Om Next remote reads are always some kind of mutation (e.g. even set-query! is really setting something that causes gather sends).

tony.kay20:03:07

So, with this realization, we decided: why not make a "global spot" for the load markers we want, that we can manage as data? Get the parser out of the business.

tony.kay20:03:24

So, load is a mutation that adds something to this internal app-state queue.

tony.kay20:03:57

and it is a "remote" mutation, but when our network stack sees a load mutation, instead of sending the load, it pulls the read items off the queue and sends those instead.

tony.kay20:03:14

in a nutshell

tony.kay20:03:16

Once you get to the network layer there is no diff between mutations and reads. It is all just query notation, and the merge behavior is the same for both (call the callback you're given, which is just merge!)

tony.kay20:03:43

Glad you like the videos. Sometimes when coming from raw Om Next it can help to see what we've decided to do. Technically Untangled is Om Next, it's just one possible way of setting it up.