Fork me on GitHub
#fulcro
<
2017-11-03
>
tony.kay00:11:43

Daily snapshot push of 2.0. The new load marker stuff is in, but largely undocumented…and not integration tested (though the code itself has good tests). You can now do stuff like:

(load this :root/thing Thing {:target (df/multiple-targets (df/prepend-to [:table 2 :thing]) [:table 3 :thing] (df/append-to [:table 4 :thing]))})

tony.kay00:11:12

I also added the support for better load markers. You can now name them, and they just appear in a top-level table

tony.kay00:11:30

As I coded it, I realized that you need to be able to query for specific markers against potentially many UI components, so querying for the entire table of markers seems like the best bet. It doesn’t come out quite as nice as I’d hoped (you’d have to use dynamic queries for that, and that seems overkill), but it is still a lot better. The default :marker is still “true” which maintains the old behavior (markers overwrite state).

tony.kay00:11:10

I also found/fixed a few more rendering issues.

tony.kay00:11:27

If anyone is looking to contribute, I have some stuff that could be optimized 🙂 You’d have to be comfortable with recursive queries

tony.kay04:11:21

So, React 16 seems to work, but I am finding a number of libraries that are not ready for 16 yet. As such, I’m going to leave Fulcro defaulting to 15. If you want 16, you just exclude react from fulcro, and include your own.

wilkerlucio10:11:06

@tony.kay for the 15, are we still getting that createClass warning?

rastandy10:11:13

hello everyone.

rastandy10:11:19

I have a problem understanding how to build queries in the current case:

rastandy10:11:05

I’m using html5 routing and some routes have a routing parameter containing an entity id

rastandy11:11:22

now when I’m going to write a query for the components that make up the landing screen for that route, I don’t know how to specify that I need that specific id in the query

rastandy11:11:50

I’m sure I’m reasoning the wrong way…

rastandy11:11:13

any help would be really appreciated

claudiu13:11:01

as far as I know. You send the bidi-match (handler & route params) to a mutation. And in that mutation you have to add the router params to the state where you need them and then call update-routing-links to change the screen.

rastandy13:11:58

@claudiu thanks for the pointer, I’m taking a look at it right now

rastandy13:11:18

@claudiu wow, a om/transact! call within a mutation? I thought it wasn’t allowed there

wilkerlucio13:11:55

@raspasov it's not really recommended, but it's valid

wilkerlucio13:11:15

just try to don't abuse it, if you can do in a single request, better 😉

rastandy13:11:17

ok, advice taken 😉

rastandy13:11:39

I was really trying to understand IQueryParams to do what I need to do

rastandy13:11:32

because I already found myself putting indents vectors here and there in the app state in the routing code and it seemed not the canonical way to do things

tony.kay13:11:40

@wilkerlucio @rastandy calling a mutation inside of a mutation is a bad practice. I would not call it even “valid”. You’re better off writing functions on state as a map and composing those.

wilkerlucio13:11:48

but sometimes I really don't see other way, on this case for example, I might need an extra call IF the route needs data, but I can only know that after doing some processing that requires environment information (so I start on a transaction)

wilkerlucio13:11:10

maybe I could by pre-fetch that by looking at the reconciler, but I'm not sure if that is more clear

tony.kay13:11:23

@rastandy The short answer to your question is that the parameter ends up targeted at state, not queries. An ident is [TYPE ID]…so the parameter in this case is the ID.

rastandy13:11:31

@tony.kay thanks for the advice, but what if I need to load from remote?

tony.kay13:11:45

load-action and load-field-action

tony.kay13:11:54

they are the composable bits to put in a mutation

tony.kay13:11:14

@wilkerlucio well, let’s think that through. Perhaps I’m wrong.

rastandy13:11:31

@tony.kay oh I see, thanks.

tony.kay13:11:27

The method in question should be using load and load-action.

rastandy13:11:33

@tony.kay the problem is that I have the id in a routing parameter and the every query for a screen should use the id from the routing param

wilkerlucio13:11:05

the thing is, that method builds queries dynamically, how would I do that on a load?

tony.kay13:11:18

load-action

wilkerlucio13:11:38

I feel like we had this discussion before 😛

tony.kay13:11:48

OH, I see your problem.

rastandy13:11:56

in transactions?

rastandy13:11:09

@tony.kay sorry too much questions 🙂

tony.kay13:11:45

@rastandy It’s ok. You’re better off debouncing the events, not the mutation. IMO…but if it makes sense to you, then I’m not opposed to it

rastandy14:11:55

I need to debounce the calls to the server, without losing the instant optimistic updates

rastandy14:11:45

think for example about pagination. Users click fast on various page numbers and only the last page is really loaded and queried to the server

claudiu07:11:59

@rastandy i used for autocomplete the gogl debounce. Went with debounce of a function that did transact. Can configure debounce time or not to debounce from the ui, the transact is unaware & reusable.

rastandy19:11:44

right, this also keeps the actions on the ui brain dead simple

tony.kay13:11:22

@wilkerlucio You need a version of load that lets you modify the query dynamically…hm, dynamic queries…. 😜

wilkerlucio13:11:17

yeah, I try to use load and load-* as much as I can, but when the query doesn't match the exact one on the components, I have to fall back to the direct load call

tony.kay13:11:43

I’d have to study your example more, but what about your query doesn’t fit into load with params?

tony.kay13:11:52

are you nesting a param deep in there somewhere?

rastandy13:11:59

so IQueryParams is not for doing dynamic queries?

tony.kay13:11:49

@rastandy It is, but in Om Next it has some issues you’d need to understand. Fulcro 2.0 is addressing them.

wilkerlucio13:11:56

in my case I might have to change the query, because I want to load a page that comes from an ident

wilkerlucio13:11:05

if I just get the query, I don't have the parent context

rastandy13:11:39

@tony.kay oh, too bad. I should stay away from them for now then

wilkerlucio13:11:46

so, part of the transformation is to use the component ident + query (that also needs extra data to be built, to figure the ident value), but that has to be injected on the current page query

tony.kay13:11:44

I don’t know what you mean by current page query…it isn’t a union

rastandy13:11:46

@wilkerlucio I ended up putting manually idents in my state wherever I needed

tony.kay13:11:38

that just looks like (load-action env [:topic/by-slug slug] TopicPage)

tony.kay13:11:38

I mean, it’s OK to pass the component class as a parameter to a mutation as well, if that’s what you mean

wilkerlucio13:11:53

it's derived from the route

wilkerlucio13:11:56

the route is the begin of this

wilkerlucio13:11:03

from the route the component must be inferred

wilkerlucio13:11:21

then I have to load that into a specific place (not on the direct target, to avoid flickering)

wilkerlucio13:11:52

maybe I can just load and send the path as a param on the post-mutation

tony.kay13:11:09

I don’t mean to frustrate you, but we still haven’t distilled it down to a problem that needs a transact inside of a mutation

wilkerlucio13:11:27

we seem to have floated to a differnt problem, with the load 😛

tony.kay13:11:45

yeah, and if load is missing capabilities, I’d want to fix it…

wilkerlucio13:11:19

but going back to the chian of transacts, I see that if I extract all from the reconciler, I could make some pre-processing before the mutation

wilkerlucio13:11:31

but in any way, this code does change the state, that's why it is a mutation in the first place

wilkerlucio13:11:53

but there is a branching in the middle of it, because a follow-up read might be required, depending on that specific route needs

wilkerlucio13:11:17

so the option would be to pre-process before the mutation, and maybe call a series of then depending on the pre-processing results (to know if a server load is required)

wilkerlucio13:11:35

seems possible, but I'm still not sure if that is more clear than just calling from the inside

wilkerlucio13:11:44

do you see a different path?

tony.kay13:11:15

So I would distill this down to this problem: You’d like to customize a query that is loaded. It is the same problem if you’re doing it from the UI or within a mutation, right?

tony.kay13:11:28

I’ve only given you a way of doing that from the UI (e.g. calling transact with load as a mutation), and in this case, you’d like the logic to be in a mutation

wilkerlucio13:11:36

sometimes that might be no query at all

wilkerlucio13:11:30

there are 3 different branches there: 1. route is pure local, no load request needed (this don't cause an extra load) 2. route needs data and can fetch as-is, no query transformation 3. route needs data and customization, process the query and send

claudiu08:11:13

@wilkerlucio have you thought about going about the routing issue, with a observable that would track the loads and change the screen

wilkerlucio09:11:49

not sure if I understand your question, you mean to observe url changes?

claudiu13:11:39

nope, was thinking of listening on the db atom and after page data has loaded & if the screen is the same to trigger the page display in the ui. Just something I was thinking of trying for the router pre-fetching data and only showing the page after the data is loaded. Something like using atom listener as a callback after the page data is loaded :))

wilkerlucio15:11:15

humm, gotcha, but honesty I'm a little skeptical about the listening to the atom idea, feels like getting back to watchers/observables, which in my experience is a big source of complexity, I would leave that as the last option if I were to try, but I might be wrong, once you try you tell me 🙂

tony.kay13:11:55

Right…distill…is my statement above correct?

wilkerlucio13:11:48

not sure if I follow it up, the second line about the doing from ui

tony.kay13:11:59

You can call transact! from the UI

wilkerlucio13:11:08

the idea would be to pre-process the load?

tony.kay13:11:09

so you could do your customized query from there

tony.kay13:11:26

no no…I’m making a problem statement…here, let me say it another way:

wilkerlucio13:11:40

I work better with code examples if you can gimme one 🙂

tony.kay13:11:59

If you had enqueue-load that could take an arbitrary query or even load-data-action from older versions of Untangled, then you would not need transact! here

tony.kay13:11:26

All load-action does is conj a load marker onto a queue

wilkerlucio13:11:28

we are talking about transaction chain, or the use of fulcro/load directly?

tony.kay13:11:50

load has load-action, which you should be using instead

tony.kay13:11:23

but in either case, you’re triggering the transact! mechanism to cause loads

wilkerlucio13:11:18

yeah, would be nice to be able to send the queries directly to those helpers

wilkerlucio13:11:45

currently it's usage is limited to the exact query (at most filtering with :without) from the component

tony.kay13:11:15

The problem with arbitrary queries is that they don’t make sense with the overall model’s parameters (e.g. targeting, markers, etc.)

tony.kay13:11:46

and in your case, you’re just loading an entity by ident…you just don’t know which one at first, right?

wilkerlucio13:11:52

what if we sent both? the component, so you can track it, and the custom query

wilkerlucio14:11:32

yeah, the ident has to be built from the route on this case

tony.kay14:11:45

sure…and you can actually do this with what already exists

tony.kay14:11:56

(react-type comp) will get you the class

tony.kay14:11:08

(load-action env ident (react-type comp))

tony.kay14:11:22

I think that’s right

wilkerlucio14:11:48

comp is already the class on this case

tony.kay14:11:56

Fulcro 2.0 will make it better, because of the more advanced targeting and load markers

tony.kay14:11:14

Oh, well, then it is even easier 🙂

wilkerlucio14:11:46

ok, going lunch, we can talk more later 🙂

tony.kay14:11:16

@rastandy Did that clarify things more for you?

tony.kay14:11:30

On dynamic queries: The shortcomings of them in Om Next is that they are tied to a combination of component instance, data path, and class path. Things like component lifecycle transitions can cause surprising behavior. If we’re talking about routing, then the solution is state anyway. The routing-tree mechanism can handle putting the “correct” ident in app state, but that ident could point off into the void (to a table entry that is not yet loaded). All sorts of solutions. To me, this logic belongs in your HTML routing event handling. The example in Fulcro Template just applies the route…but you could also pre-process the route before updating the UI. Add a multi-method hook that can see where you’re going (e.g. dispatches on the bidi match). Carrying around the reconciler at that layer is fine, as is calling transact! and load.

tony.kay14:11:49

Wilker’s solution is also good…just needs to use the primitive load-action instead of transact! and load

rastandy14:11:17

@tony.kay I’m trying to understand still

rastandy14:11:32

@tony.kay any example to point me to?

tony.kay14:11:46

load-action is covered in various places…but the essential thing is that it is just like load, but usable within a mutation.

tony.kay14:11:09

in terms of routing, the best example is the Fulcro Template, and section M15 of the dev guide

rastandy14:11:57

@tony.kay oh yeah that’s understood for me, I was re-reading your last long answer

rastandy14:11:27

this one. Which I think now I’ve got fully

rastandy14:11:07

I think I have a plan on how to proceed at least 🙂

rastandy14:11:14

thank you very much, as always

rastandy14:11:31

I really should learn to use Slack first…

vinnyataide17:11:00

quick question (ident [this props] [:new-user :singleton]) what does singleton mean in this context?

tony.kay17:11:18

it’s just an id

tony.kay17:11:36

there is only one of them

tony.kay17:11:45

perhaps :the-one would be better>

tony.kay17:11:16

If there were many (to go on screen), each would need its own ID derived from props

vinnyataide17:11:12

who provides the singleton?

vinnyataide17:11:47

I got it, just wanna know this detail

tony.kay17:11:40

So, when the initial state is pulled from InitialAppState, IF there is data in the tree for that component, THEN fulcro will normalize it into the app state normalized database. That function will be called to get the ident for the data.

tony.kay17:11:50

So, in effect, you do

tony.kay17:11:18

If you don’t include data for it in the startup state tree that matches the component’s location in the UI, it won’t be on the UI or in the db

tony.kay17:11:32

initial tree -> normalization (using ident functions) -> normalized db

vinnyataide17:11:48

got it, forgot about the env providing in the om tutorial

tony.kay17:11:57

Fulcro will allow you to pre-populate your own db with an atom, but InitialAppState is much much much better.

tony.kay17:11:48

of course, at runtime you can add/remove what you want from the db, and link in things…in any case, if THAT component is on-screen (any number of times) it would be linked by that ident to the single source of data for it

tony.kay17:11:01

so singleton is actually a bad term.

vinnyataide17:11:08

yeah I had a pure om next app before, am trying to provide fulcro for a good routing and parser solution

tony.kay17:11:11

it’s a single bit of data

vinnyataide17:11:19

thank you very much, now I have succesfully migrated from yada+compassus to only fulcro on server and frontend 😃

tony.kay19:11:55

ooof. I’m working on documentation and just realized I don’t cover Ident well in the dev guide…no wonder people are confused.

tony.kay21:11:50

So, anyone care to check my thinking on this? I know there was resistance to adding anything to mutations that indicate what data they change, but as I’m updating the documentation on mutations it is (again) striking me as odd that you cannot list the follow-on reads there, and since Fulcro 2.0 controls all of the code now, it occurs to me that I can do something about it 🙂

tony.kay21:11:12

That way, UI refresh would be automatic without having to specify the follow-on reads at transact, and the reasoning about what data changed would be…well..where it is changing

tony.kay21:11:18

so this:

(defmutation boo [{:keys [id] :as params}]
  (action [env] ...)
  (keys [env] [:person/name :address/street [:person/by-id id]]))
seems doable and useful.

tony.kay21:11:56

In Om Next the name for the concept is :keys I think (though it does nothing with them). Now that Om Next isn’t going to be present, we could totally use them to affect refresh automatically.

tony.kay21:11:11

The idea according to David was that your protocol (e.g. remote side) would specify these as documentation to the reader, but since there is no knowing what the UI-in-question normally queries for, there is no point in pushing them all down to the client. I’m thinking that since most updates are optimistic and local (before being remote), they serve a very fine purpose on any given client (which does know what is being affected locally).

rastandy23:11:08

It seems a nice feature indeed

rastandy23:11:16

it would also be nice to have that information as data and being able to form a dependency graph for mutations

tony.kay23:11:50

what do you mean? mutations are stand-alone

rastandy23:11:23

You are right, “dependency” is not the right word

tony.kay23:11:27

more of a dependency list, I think you mean…not much of a graph 🙂

tony.kay23:11:00

well, I guess you could pull the parts of the overall graph query that are affected, but don’t see an immediate use for that

rastandy23:11:56

It could be useful for debugging

rastandy23:11:22

just thinking

rastandy23:11:02

what about a subscription mechanism for mutations?

tony.kay23:11:37

subscriptions are easy to do with just normal code

tony.kay23:11:02

I’ve prototyped that and made a websocket/meteor js style app…it was like 200 lines of code (levereaged Datomic’s tx logging)

rastandy23:11:04

can you make an example?

tony.kay23:11:27

I can…but I’m doing other stuff for the next few weeks 😉

rastandy23:11:39

yeah right 🙂