I've watched that video a few times. Is one of the best for understanding things - very good video. I see that load-data
does have config
as last argument, which can include params
. So I guess can achieve [({server-property (om/get-query SubqueryClass)} params)]
by other means - in fact I can see that [state-atom server-property-or-ident SubqueryClass config]
will do the job.
So -action
means it is callable from within a client mutation?? That's the all important thing for me right now.
@cjmurphy yes. The -action
is intended to mean that. It’s really simple how it works: Calls to load
basically run a mutation transaction on the untangled mutation load
(don’t remember the ns). The load mutation gets processed, but what it does is stores your desired load info into an app state load queue. The mutation specifies that it wants to be remote. When the layer just above networking sees the load mutation, it instead pulls the stuff from the load queue in app state and sends those queries instead.
The -action
versions are the part that put the load on the queue. So, you still need to specify a remote-side, and data-fetch
has a function (`remote-load`) for generating the right-looking thing for the remote side that will cause load queue processing to happen.
any number of load-actions
can be run (each adding an entry to the load queue), and one remote-load
(as the return value of the :remote
side.
@cjmurphy You need a remote on it
that’s what I was saying in prior comment
(remote [env] (df/remote-load env))
inside your defmutation
load-action
affects state, but Om won’t even try to go remote without a remote indicator, which is generated by the remote-load
function.
The doc string for load-action
is pretty specific about that. Do you not have docstring help?
otherwise it looks good
(defmutation load-existing-rules [{:keys [sub-query-comp source-bank target-ledger]}]
(remote [env] (df/remote-load env))
(action [{:keys [state]}]
(df/load-action state :my-existing-rules sub-query-comp
{:target help/rules-list-items-whereabouts
:refresh [[:banking-form/by-id p/BANKING_FORM]]
:params {:source-bank source-bank :target-ledger target-ledger}})))
Mutations in Om can have multiple parts. The action
is always local. It cannot trigger anything to do with networking. The other sections are names of remotes (the default name of a remote is remote
). Each one indicates what should be done on that specific remote for this action. The special value true
means “the same abstract thing that happened on the client`...but for load actions, you don’t want to run load-existing-rules
on the server, you want to send the query. The load-action
has stored the query in a queue. The remote-load
returns an AST that will trigger the queue processing on that remote.
technically, for loads, triggering remote-load from any remote causes queue processing, and the queue entries themselves use a :remote
option to indicate which remote they query
@tony.kay The docstring even has example, so you are correct it should be pretty obvious. At the moment I just read docstrings by looking at the source (cntl+B on the function), where there is no colour coding or anything. The example is in the old pre-defmutation style and is at the top of some lines of comments. And a colon sometimes looks like a semi-colon to me. So to my eyes :remote (df/remote-load env)
was just part of the comments and got missed. If it had been new style, starting with a paren, or the remote had been after the action, so with lots of whitespace around it - in either of those case it would have been hard to miss. Syntax colouring is probably my answer - I'll see about using the proper IntelliJ way of looking at docstrings.
Yeah, updating the docstrings would be good 🙂
It is cnrl+Q on my Linux machine for looking at docstrings, but it doesn't have a different colour for code. Really I should have understood/researched your comment: "`data-fetch` has a function (`remote-load`) for generating the right-looking thing...". All my eyes needed was to see (remote [env] (df/remote-load env))
, which was nowhere to be found. I did try (remote [_] true)
, but of course that didn't help.
Yeah, this was one of the important realizations in Untangled: how to turn mutations into loads so that we don’t need a parser to be involved in remote queries. It turns out that you need a mutation to modify state so that your parser can trigger a load anyway in Om Next (otherwise you’d be loading at every parse…the state has to change so your parser can be intelligent about loading). So, the trick was to realize that any mutation with a remote AST will cause Om Next to try to send that over the wire, so we hack into that to transform the mutation into a query. The only downside is that composing loads into mutations then looks a little wierd.
I guess there could be some macro similar to defmutation that always indicates you’re wanting to process the load queue and does it for you.
I'm going to be calling om/tranact!
from within a defmutation... that's what about to try
no, that is not advised
mutations are the bit twiddling. Don’t embed a higher-level abstraction in them
modify state, indicate what is remote. That’s all.
So I'll compose df/load-action into the threading macro on the state.
yep
that is the intention
So the other one that wasn't remote before will now become remote too.
?
You’re writing one mutation, right?
One that calls another.
I don’t see one that calls another
When a drop down is loaded with all its values - thats the first mutation. It works out the value which will be the chosen value of the drop down. Now data needs to be loaded from remote for this chosen value.
ah, so the completion of one load needs to trigger another?
No - the first is not a load
so, you’re trying to write a mutation that 1. sets the dropdown display, and 2. triggers a load
(as it happens in my case)
Yes
ok. (swap! state (fn [s] (-> s (do-dropdown-update) (load-action :kw ...))))
within action
and remote-load
in remote
Yes good. So the first mutation now becomes a remote when it wasn't before.
what first mutation? You mean do-dropdown-update?
Yes - put the list of values in the actual drop down control.
that isn’t a mutation, it is a function
that is part of the implementation of the action (local only) of a mutation
It is a mutation because it is changing state.
the remote-load
sends an AST that just processes the network queue
it knows nothing of what you did to state
if you say “remote true”, then you’re asking the top-level transaction to go over the wire…still doesn’t know the guts of action
by using remote-load
, you’re replacing the top-level transaction with a special AST that processes the network queue
I can just try it both ways, with and without making the first mutation remote. So it sounds right from what you are saying to make the first one be remote true.
(I’m using the term mutation here to mean the top-level transaction)
I don’t see a first one
I've got two defmutations.
so you’re just confusing me. I see defns
Both local.
not in the code you posted
but I assume you just elided that. So, remote true
means you want to send the top-level tx, as DATA, across the wire to the server, as is from the UI
if that is what you want, then yes 🙂
Yeah the code I posted is the second defmutation, that I'm calling from another one.
If you’re doing a load-action
, you do not want to send the top level UI tx…you want to process the queries of load-actions.
but you don’t call one defmutation from another
you either:
1. Write a transact that does the two in succession: (om/transact! this '[(a) (b)])
2. Write a transact that calls one (om/transact! this '[(combo)])
and then in that one mutation you do the local updates along with load-action
I think I have to. No way round it - if not actually call, do the equivalent of calling.
Yeah I'll be doing the second, but semantically it is a call of one of the other.
Why not write a query that asks for both facts you need, as a union query. Then the server can tell you everything you need with one load
without loading more than you need
chaining is a bad practice in general
[{:dropdown-options (om/get-query DD)} ({:subdata (om/get-query Union)} {:current-selection :a})]
which you could queue up with two calls (on one event) to load
There are other ways. Good to think...
and use :params
to send the current selection
either the server knows the complete answer, or you know a fact you can tell the server. There is no need for chaining in these cases 🙂
(and two calls to load
in the same event handler will be sent together as one request)
(load this :dropdown-options DD) (load this :subdata Union {:params {:current :a}})
Yeah the first mutation is choosing the one to be selected as current in the drop down, but that is the wrong place for that choice, I think...
It’s tough to un-learn the habits of js 🙂
Well especially when you never were a js programmer haha!
😄
You’re doing a thing like “choose your make of car”, then “choose a model”, where model gets loaded after they choose a make
right?
Yes - one dd to another dd. My first is types of account - Expense, Income etc
And second is then all the expenses say.
sure
you prob don’t even need a union then
Are you saying I should have all the models for all the cars already loaded?
1. the initial setup (e.g. a load of the first dd content)
2. The user interacts with the dd. This triggers (transact! this '[(choose-first {:val v})])
3. The choose-first
defmutation:
3a. Threads state through a function that updates the dropdown they interacted with
3b. continues the thread through a load-action
that can send the proper query and param based on v
3c. Has a remote
of (remote-load env)
When the load completes, the second dd is now populated
(assuming you targeted the load-action result to the dd state, or otherwise got it there with a post-mutation)
Hmm - problem is I need to load the second lot before any interaction.
but you don’t know which to load
until they select the first dd content
Yes - the default one that the first mutation is choosing.
how is the default chosen?
you must have known it at step (1), so you can do it during that load
so yes, you’d preload the “models” for the known “default” make
By default "Expenses" say. Kind of arbitarily - perhaps the most popular choice in my mind, so user has to do less actions.
yeah, but you’re loading data they may not want to see at all, which is extra overhead for your user and your server
I've got no "No choice" in the drop down.
bad for multiuser performance
but whatever…you can do it either way, as I just described…just move the default choice load to step (1)
But they are always going to go to the next step.
I don’t understand “but”. What’s missing?
the next step is as I described above
event triggers dd update and next load
Was talking about perf, I didn't get the issue there.
steps 2+
oh
If I make the first choice explicit at the client level (not defmutation level) then I think my design becomes better.
say you show me expenses. You just loaded data from the server. That just consumed CPU and disk IO. Say you have 10000 users. They all go to that screen. You just did 10000 bits of CPU and IO. Now let’s say 20% of them didn’t want that. You just blew 2000 users worth of interaction time…slowing down the other 8000 as well.
ie "Expenses" - no reason that should be down at defmutation level - then my problem goes away.
They will all want it in my use case. They are creating a rule that they must create. So the 20% you mention is really 100%.
oh, then you already know everything you need at step 1
Yes. But I needed your help to get a better design. So thanks for taking the time. Working solo here...
NP. Glad it helped
My experience with Untangled so far has been: If it’s hard, you’re doing it wrong or missing a concept.
So, when I run into something that seems difficult, I try to remind myself of that.
so far, I have yet to find anything that didn’t work out 🙏
:knock-on-wood:
The “chaining” temptation is tricky: I loaded A, now A told me something that makes me want B. The thing is, unless you’re loading from more than one server, the server knew about what was in A when it sent it to you, meaning you could have also asked for B in a generic sort of way and had it handled at the same time.
There’s nothing technically impossible about using transact!
within a mutation, other than it leads to a design where you could conceivably end up with recursive-looking transaction loops. So, poor design. If you had to do a two-server chained load, I think you’d probably be stuck with it, though. I just strongly encourage not doing it in most cases. Hm. actually, I take that back. There’s no way to get the timing right. You’d probably have to install a merge handler and trigger the second one from there. Or use a post mutation to trigger a transact!
against app
. Post mutations are not allowed to have remote sides (they’re ignored).
If possible, I’d probably design server A to talk to server B for me, and avoid the whole client scenario 😜
hello people, I would like to announce a new little open-source project I built with Untangled, it's a Chrome extension to represent an Youtube Queue based your emails, I made a post about it, I hope you can enjoy it 🙂 https://medium.com/@wilkerlucio/using-gmail-messages-to-track-youtube-channels-ef35308a0ceb