Here is the announcement: https://www.cognitect.com/blog/2020/12/15/sponsoring-open-source-developers . Apart from the sponsorship, it is also a very meaningful recognition (almost like an award) to Tony and other outstanding OSS developers who have contributed so much to the Clojure ecosystem. Congratulations! P.S. Agree entirely with Rich Hickey's view on this. This is a very good start.
Why does js/setTimeout
inside dr/route-deferred
break the order of transactions?
This works, I get :route!
to the target in question first, then dr/target-ready
on it:
:ident (fn [] [:component/id ::Accounts])
:will-enter (fn [app _]
(dr/route-deferred [:component/id ::Accounts]
#(comp/transact! app [(dr/target-ready {:target [:component/id ::Accounts]})])))
but if I instead execute the transact! inside a timeout (the motivation was to simulate a load that takes a while) it breaks and the target-ready
appears in the transaction list before the corresponding :route!
:
:will-enter (fn [app _]
(dr/route-deferred [:component/id ::Accounts]
(fn simulate-load []
#?(:cljs (js/setTimeout
#(comp/transact! app [(dr/target-ready {:target [:component/id ::Accounts]})])
10)))))
How is this possible? When I look at the code, the :route!
tx is submitted here https://github.com/fulcrologic/fulcro/blob/1d5f260a0c861d7156d00638a6ae3ee315d9de7a/src/main/com/fulcrologic/fulcro/routing/dynamic_routing.cljc#688 right before the (completing-action)
is called some 4 lines down. Even if the target-ready transaction happens async due to the setTimeout, it is only triggered after the route tx has been submitted. What is it I am missing?@holyjak try 3.4.3 or so...I recently added a fix for route flickering that might be the problem
Look at commit log
3.4.3 behaves in the same way. I looked at the commits a way back but couldnt find the original fix for flickering. Trying 3.4.0 now. But the code in dr looks correct to me. Yet somehow this pseudocode, a simplifyication of the actual dr code
(comp/transact! :route! ["accounts"])
(js/setTimeout #(comp/transact! :target-ready ..) 10)
causes target-ready
to occur before the :route!
. Is it because transact!
is asynchronous (in regard to attaching transactions to the tx list) so the first transact is not guaranteed to have modified the global tx list before the second transact runs 10ms later from a different "thread"? (yes, js has no threads...)I have to digg more into it. 3.4.0 behaves the same. But if I execute the core of the dr function manually, the transactions appear in the correct order. I guess my simplification of the code lost something important:
(do
(require '[com.fulcrologic.fulcro.ui-state-machines :as uism])
(uism/trigger! app
:com.example.ui/MainRouter
:route!
{:error-timeout 5000,
:deferred-timeout 20,
:path-segment ["accounts"],
:router
[:com.fulcrologic.fulcro.routing.dynamic-routing/id
:com.example.ui/MainRouter],
:target [:component/id :com.example.ui/Accounts]})
(binding [comp/*after-render* true]
(js/setTimeout
#(comp/transact! app [(ui-ready {:id :second :loc "within setTimeout"})])
10)))
hello again, still on that thing about :account/id
from yesterday, it was working yesterday, but today I spin again and it stopped, after some debug I figured the issue is in the fo/default-values
part, because we use the account-id there, which is some data in the app state. it was working yesterday because I was in a "refresh flow", so it could pick up the data, but on fresh restart, it tries to compute default-values at app init time, but at this time the DB is not ready with the account-id, making it nil, and I noticed it only tries to compute that once. in normal Fulcro I could add this to the mutation logic so I have a delayed state to work with, what is the approach to fix this with RAD?
ok, I figured it out, the values on default-values
can be fns, this fixes it:
fo/default-values {:computed-value (fn [] (compute-here))}
another thing, I like to have a virtual field that's a complex type, I see to create an attribute of any type if needs to use defattr
, but I can't find how to define a complex type (a map in my case), how do I create an attribute with an open type? or map type? (consider its virtual, not needed for schema or saving anywhere)
I used :map
and it just worked, I guess i can use any name here? but wonder if this is the appropriated way to do it
In https://book.fulcrologic.com/#_initializing_a_tree_of_data what does
> If you also are supporting form interactions then you can augment the form with form configuration data
>
mean? I believed that any time I want to use a component as a form - which I presumably want here since there are :form-fields
declared on the components - I must add the form config to the data. Or?
Thanks a lot for the edit, I think the book is clearer now.
I think that's just a substitute in ex code for a ...*
mutation helper. In reality that would not be called as a separate component or as a ui renderer. hence the explicit call to merge/merge-component!
Edit, I'm dumb, you say that (more or less) in your post already
next RAD question: we are trying to validate a consistency on the subform, in this case, its a check to see if the sum of percentages in the subforms is exact 100%, otherwise the form is not valid. We were able to prevent the form to submit by adding a form level validation, but now trying to figure how to display some error message for it
one thing I noticed is that, for the subforms field, the validator never tries to validate this field in specific, and given Fulcro RAD form errors must be attached to a field to give an error message (please correct me if this is a wrong assumption), how can we make a message for this to be visible?
see demo
the account forms have a sample global validation on email address
the invoice form has sample derived value calc
error does have to be attached to some field..there is no provision at the moment for the render plugin to show an error elsewhere….however, the form is just a defsc. If you provide the body you can render. You can also make your own derived plugin, or send PR on current one.
I think form/render-layout
is what the default body is…so if you wanted to wrap that, you can
The plugins are never meant to cover every case, and at the moment, there are some cases that should be covered but are not. There’s an escape hatch for every one that I’m aware of, which is part of the design.
My opinion on global errors is generally: if the form isn’t valid, you should be able to point to some field. Passwords don’t match is the classic case…which one is wrong? The first or second? Who knows…show the error on first or second or both. Your call.
what I just found was the option :validate-edges? false
by default on the validator
I tried to trigger that on, but still not getting the ref edge validator called, doing some debug around it to understand why
ok, I think we reached something here with the debug
the issue is there the subform field is never considered completed
by Fulcro
this means it never runs the subform field validation
by commenting the check for completed the thing worked as expected
but not sure what's the right way to go about this for a real fix
ok, I figure this works when we click to save, so, good enough, moving forward 👍
ok, Fulcro was right all along, it makes the field as complete when the subform fields are all filled, working great! 🎉
The text is fine as-is. A form is nothing more than inputs you control. So, you can make one without form-state all day long. Form state is a ns of helpers that help you do what I consider the common bookkeeping tasks. If you don’t want to use form-state.cljc, then don’t. If you want those functions to work on a form, then you need to add the initial bookkeeping data they use.
if you want to expand upon that in a PR I’d consider it, but really: you write the Form. You write the logic to put data in the entity. All form-state does is field state tracking, validations, and minimal diff calculation. If you want to write that stuff yourself, then go for it 🙂
users editing, and the existence of the form are 100% your code. Form state takes a picture of your entity when you tell it to, and can compute diffs, and can run a validator if you want. It is not some mutable-state-controls-your-form-with-magic system. It’s a set of functions for snapshotting state, comparing it over time, and checking if you consider that state “valid”. Remember: Fulcro is very literal about View = F(State)…form-state is about helping you go from state to state and compute interesting things about state.
TL;DR; I’m open to clarifications of book, but your suggested edit isn’t accurate 🙂
Perhaps “For the form-state helper functions to work…”
Would probably be useful to write it like above. TBH I got lost reading the ex in the book, and I've already used FS pretty extensively.
@holyjak have you had luck replicating your issue in RAD Demo?
Not yet but I now suspect it could be because of pre-merge I do in the prod app. Regardiing ☝️ do I understand correctly that when you switch Show inactive, the hard-reload will not show the account list as expected?
It doesn’t matter the status of the switch actually
Ok. So the GIF shows that hard reload is not working for you, correct or not?
Yup
Correct