fulcro

Book: http://book.fulcrologic.com, Community Resources: https://fulcro-community.github.io/, RAD book at http://book.fulcrologic.com/RAD.html
Alex Piers 2020-12-16T05:31:17.032Z

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.

Jakub Holý 2020-12-16T10:06:57.042400Z

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?

tony.kay 2020-12-16T14:29:11.044400Z

@holyjak try 3.4.3 or so...I recently added a fix for route flickering that might be the problem

👍 1
tony.kay 2020-12-16T14:29:29.044800Z

Look at commit log

Jakub Holý 2020-12-16T14:51:11.045600Z

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...)

Jakub Holý 2020-12-16T15:15:08.045800Z

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)))

wilkerlucio 2020-12-16T15:53:45.048600Z

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?

✅ 1
wilkerlucio 2020-12-16T16:14:23.048800Z

ok, I figured it out, the values on default-values can be fns, this fixes it:

wilkerlucio 2020-12-16T16:14:45.049Z

fo/default-values {:computed-value (fn [] (compute-here))}

🎉 1
wilkerlucio 2020-12-16T16:28:33.050200Z

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)

wilkerlucio 2020-12-16T16:32:22.050400Z

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

Jakub Holý 2020-12-16T17:29:03.050900Z

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?

Jakub Holý 2020-12-17T10:25:21.065800Z

Thanks a lot for the edit, I think the book is clearer now.

JAtkins 2020-12-16T17:49:49.051100Z

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

❤️ 1
wilkerlucio 2020-12-16T18:16:56.052900Z

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

✅ 1
wilkerlucio 2020-12-16T18:18:01.053Z

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?

tony.kay 2020-12-16T18:30:45.053300Z

see demo

tony.kay 2020-12-16T18:30:56.053500Z

the account forms have a sample global validation on email address

tony.kay 2020-12-16T18:31:09.053700Z

the invoice form has sample derived value calc

tony.kay 2020-12-16T18:32:20.053900Z

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.

❤️ 1
tony.kay 2020-12-16T18:32:43.054100Z

I think form/render-layout is what the default body is…so if you wanted to wrap that, you can

tony.kay 2020-12-16T18:33:48.054300Z

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.

tony.kay 2020-12-16T18:34:19.054500Z

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.

wilkerlucio 2020-12-16T18:38:13.054900Z

what I just found was the option :validate-edges? false by default on the validator

wilkerlucio 2020-12-16T18:38:31.055100Z

I tried to trigger that on, but still not getting the ref edge validator called, doing some debug around it to understand why

wilkerlucio 2020-12-16T19:04:38.055300Z

ok, I think we reached something here with the debug

wilkerlucio 2020-12-16T19:04:50.055500Z

the issue is there the subform field is never considered completed by Fulcro

wilkerlucio 2020-12-16T19:04:56.055700Z

this means it never runs the subform field validation

wilkerlucio 2020-12-16T19:05:10.055900Z

by commenting the check for completed the thing worked as expected

wilkerlucio 2020-12-16T19:05:28.056100Z

but not sure what's the right way to go about this for a real fix

wilkerlucio 2020-12-16T19:16:19.056300Z

ok, I figure this works when we click to save, so, good enough, moving forward 👍

wilkerlucio 2020-12-16T19:23:03.056600Z

ok, Fulcro was right all along, it makes the field as complete when the subform fields are all filled, working great! 🎉

1
tony.kay 2020-12-16T22:07:31.058900Z

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.

👍 1
tony.kay 2020-12-16T22:08:46.060300Z

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 🙂

😱 1
tony.kay 2020-12-16T22:11:51.060400Z

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.

tony.kay 2020-12-16T22:13:43.060600Z

TL;DR; I’m open to clarifications of book, but your suggested edit isn’t accurate 🙂

tony.kay 2020-12-16T22:14:49.060900Z

Perhaps “For the form-state helper functions to work…”

❤️ 1
JAtkins 2020-12-16T22:16:04.061100Z

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.

JAtkins 2020-12-16T23:25:21.062Z

@holyjak have you had luck replicating your issue in RAD Demo?

Jakub Holý 2020-12-17T10:20:34.065400Z

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?

JAtkins 2020-12-17T18:52:43.066400Z

It doesn’t matter the status of the switch actually

Jakub Holý 2020-12-17T21:22:32.079800Z

Ok. So the GIF shows that hard reload is not working for you, correct or not?

JAtkins 2020-12-17T21:27:24.080400Z

Yup

JAtkins 2020-12-17T21:27:28.080700Z

Correct