This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2023-05-31
Channels
- # announcements (6)
- # babashka (40)
- # beginners (6)
- # calva (1)
- # cider (1)
- # clerk (43)
- # clj-kondo (3)
- # clojure (93)
- # clojure-denver (8)
- # clojure-europe (52)
- # clojure-norway (20)
- # clojure-sweden (7)
- # community-development (5)
- # datascript (15)
- # datomic (30)
- # emacs (24)
- # events (15)
- # fulcro (23)
- # graalvm (12)
- # gratitude (1)
- # helix (4)
- # honeysql (4)
- # hoplon (39)
- # hyperfiddle (7)
- # introduce-yourself (1)
- # jobs (1)
- # jobs-discuss (26)
- # lambdaisland (3)
- # lsp (6)
- # matcher-combinators (2)
- # matrix (5)
- # meander (39)
- # nrepl (4)
- # nyc (1)
- # off-topic (5)
- # portal (73)
- # practicalli (1)
- # re-frame (2)
- # reitit (22)
- # releases (1)
- # remote-jobs (4)
- # shadow-cljs (5)
- # sql (17)
- # testing (1)
- # tools-deps (15)
Do I understand correctly that form validation in RAD isn't recursive, ie only the top level form's attributes are checked (including edges to subforms, excluding attributes of these subforms)? What if I want all my forms to be valid before save is attempted? Ie. when creating an Order, I want to be sure not only that it has >0 order lines but also that each line has pos? :count
There is only one state machine, and it is on the top-level form. It is responsible for all layers of validation, so that top-level form’s validator must cover all subforms as well.
> that top-level form’s validator must cover all subforms as well. I guess that is the problem, no? The default RAD form validator, if I read it correctly, only checks the attributes of the top-level form: https://github.com/fulcrologic/fulcro-rad/blob/462d749cd36553925d9c6f0673590b0c5b0d9f81/src/main/com/fulcrologic/rad/form.cljc#L460 Is that correct & intended? If I mirror what I believe RAD is doing, I will get this validator
(def valfn (attr/make-attribute-validator [r.order/type
r.order/customer
r.order/order-lines] true))
which claims my form is valid, even though the order-line is missing both its required attributes. If I add those attributes manually to the validator like this:
(def valfn (attr/make-attribute-validator [r.order/type
r.order/customer
r.order/order-lines
r.order-line/product
r.order-line/package-count] true))
then it correctly recongizes the form as invalid.
So my question is: Given a form, what is the simplest way to construct a validator for the form including all subforms? Preferably without having to manually collect attributes from each one. And why isn’t this - ie validating also subform’s attributes - the default? Thank you!!!Here’s the thing: I don’t know if you want to validate edges, validate sub-elements, etc. I guess the assumption should be that you want to validate it all. So, at present the code needs expansion for the “default” validator to do all of that.
I just make a single global validator using all-attributes
of the RAD model and explicitly set that on my forms. A subform could be to-one and be a picker, but I guess in that case there would be no fo/subforms entry.
If you want to write a patch that tries to walk the form set using the component options and looks for the subforms recursively and collects all of the attributes I think that would be ok.
https://github.com/fulcrologic/fulcro-rad/commit/12f7b612c19326851ba3a53193a6ab1e3360e58c
Thanks for noticing…I got used to just using my global one and never re-addressed it
:star-struck:
This looks awesome, thanks a lot. I am trying it now but somehow it does not seem to make a difference. I suppose I messed up my environment and will keep trying 🙂
It works as expected from the REPL, when I run ((attr/make-attribute-validator (form/form-and-subform-attributes my.order-forms/OrderLineForm) true) my-captured-form-props)
- it returns :invalid where before it returned valid. But in the app it still tries to save the form. In the console log I see
Form my.order-forms/OrderForm valid? false
Form my.order-forms/OrderForm dirty? true
:order/order-lines is not marked complete
:order/order-lines is not marked complete
:order-line/package-count is not marked complete
:order-line/product is not marked complete
Form is not valid because required attribute is missing: :order-line/product
Form my.order-forms/OrderLineForm valid? false
...
Trigger [:order/id …#fulcro/tempid["#uuid "5cca09cf-0afe-4a03-8af6-fd82cb67066c""]TempId] :event/save
Activating state :state/saving
i.e. I would expect the save to have failed, replacing the last line with
Form is not valid because <some reason>
Activating state :state/editing
I guess it does not fail b/c it skips checking the two :order-line/
attributes because the are not marked complete. That’s why com.fulcrologic.rad.form/valid?
explicitly checks all-required-present?
, but this check does not include subform’s attributes. Correct?
You're saying that fields that are only marked required, but that have no validator function themselves are a problem? Yeah, I didn't look at those, I just tried it where attributes had a valid?
function
One of them (package-count) has valid?
, the other (product) does not, and both have required? In this case, valid? is not even triggered I think b/c I have added a new order line to the order but have not touched any of its fields, so they are both nil/missing. That’s why the are both “no marked complete” as logged above.
Save really should not trigger unless everything is checked... So sounds like a bug somewhere
I tried it on the demo yesterday and it was working for me with the same structure you are describing on invoice
Ok, I'll dig in...
I am not sure what I do wrong. My OrderForm is marked as valid. When I look at its proposed-form-props
from the :event/save
there are some weird things:
::fs/config {
fs/complete? #{:order/type :order/customer :order/order-lines}
fs/fields #{:order/type} ; I guess ok since both customer and order-lines are subforms, not simple fields?
fs/subforms {:order/customer {}, :order/order-lines {}} ; as expected
}
:order/order-lines {
:order-line/id #fulcro/tempid[..] {
fs/config {
fs/complete? #{:order-line/package-count :order-line/product}
fs/fields #{:order-line/package-count}
fs/subforms {:order-line/product {}}
}
:order-line/id ... ; NO OTHER order-line property, neither count (expected, since unset?) nor the product ref
}}
So both package-count
and product
are marked as complete, and both have ao/required? true, yet neither has any value and does not fail the validation?!
I have
defsc-form OrderLineForm
fo/attributes [r.order-line/product r.order-line/package-count]
fo/field-styles {:order-line/product :pick-one}
...
defsc-form OrderForm
fo/attributes [r.order/type r.order/customer r.order/order-lines]
fo/field-styles {:order/customer :pick-one}
fo/subforms {:order/order-lines {fo/ui OrderLineForm, ...}}
...
; with the key attributes
(defattr product :order-line/product :ref {ao/required? true, ...})
(defattr package-count :order-line/package-count :int {ao/required? true, ao/valid? (fn [value _ _] (some-> value pos?)), ...})
(defattr order-lines :order/order-lines :ref {ao/required? true, ao/cardinality :many, ...})
Any idea what could be the problem? 🙏🙏🙏
package-count has a valid? fn so it should have been invoked (b/c the field is marked as complete) and have failed, no? I am not sure how validation of product should have worked. I assume there is a reason that form/valid?
has an explicit check for all-required-present?
but this one only checks, I believe the top form’s attributes, not those of any subform.Interestingly, if I copy these very same captured props I explore above and try to validate them, I do get the expected invalid:
(def form-props {::fs/config {..} ...})
(def valfn (attr/make-attribute-validator (form/form-and-subform-attributes my/OrderForm) true))
(valfn form-props)
; => :invalid
I have no idea why the almost same [removed some asm-id, cache options maps] data would produce different result in the app and in the clojure REPL 🤯If you change your RAD attributes and use hot code reload, then the validator might still have the OLD version of the attributes. Try stopping and starting the compiler, and reloading the page with the cache disabled to make sure you’re not getting stale things.
I.e. Your validator on the top-level form will be called on all fields even in subforms.
Ok, so I need to dig into why it doesn't work then
> that top-level form’s validator must cover all subforms as well. I guess that is the problem, no? The default RAD form validator, if I read it correctly, only checks the attributes of the top-level form: https://github.com/fulcrologic/fulcro-rad/blob/462d749cd36553925d9c6f0673590b0c5b0d9f81/src/main/com/fulcrologic/rad/form.cljc#L460 Is that correct & intended? If I mirror what I believe RAD is doing, I will get this validator
(def valfn (attr/make-attribute-validator [r.order/type
r.order/customer
r.order/order-lines] true))
which claims my form is valid, even though the order-line is missing both its required attributes. If I add those attributes manually to the validator like this:
(def valfn (attr/make-attribute-validator [r.order/type
r.order/customer
r.order/order-lines
r.order-line/product
r.order-line/package-count] true))
then it correctly recongizes the form as invalid.
So my question is: Given a form, what is the simplest way to construct a validator for the form including all subforms? Preferably without having to manually collect attributes from each one. And why isn’t this - ie validating also subform’s attributes - the default? Thank you!!!