Fork me on GitHub
#fulcro
<
2020-04-18
>
Chris O’Donnell13:04:22

Hey all, I'm working on a blog post that includes creating a fulcro form. I'd love to hear thoughts on whether this implementation is idiomatic from anyone who has time to take a look: https://gist.github.com/codonnell/1ea9b2edea542e1766f9631e94763140.

👍 4
currentoor17:04:10

yeah seems correct, some things i would a little differently but they are cosmetic

currentoor17:04:11

like i usually call mark-complete on blur

currentoor17:04:57

also a subform example might be useful

currentoor17:04:04

to show the power of normalization

currentoor17:04:20

otherwise it might be unclear how this is all that useful

currentoor17:04:52

that example might be too involved for what you want to do, but it does highlight several useful aspects

currentoor17:04:10

in your blogs you could also link to the relevant parts of the book as a reference if someone wants more detail

Chris O’Donnell17:04:37

I have done that a bit, but could definitely do it more. Good suggestion, thanks.

currentoor17:04:55

no problem, thanks for writing these by the way!

Chris O’Donnell17:04:56

I'm not sure how to fit a subform into this application at the moment; I'll take some time to think about that.

Chris O’Donnell17:04:26

You're right that it would make for a better demonstration of the usefulness of form-state

currentoor17:04:04

yeah if form-state didn’t sub-form -> flat diff delta mapping, it would be waaay less useful

currentoor17:04:34

the flat diff delta is amazing, it’s so easy to write a generic function that can turn it into a DB transaction for any particular DB

currentoor17:04:49

RAD’s DB adapters do exactly that

currentoor17:04:56

and it’s only possible b/c fulcro stores app state in a global normalized way

roklenarcic18:04:33

I am trying to mark form as complete in :pre-merge , where I have (and return) a data tree for component. Unfortunately mark-complete function only works on normalized state map… is there some other way?

tony.kay18:04:12

@roklenarcic You can just reach into the form config and set them…there should be a utility

tony.kay18:04:27

it’s just a map

tony.kay18:04:46

the complete marker is just a set of keywords

tony.kay18:04:46

I guess there should be mark-complete* for inside mutatoins, mark-complete! as a top-level call, and mark-complete for data trees

roklenarcic19:04:13

yeah, I can try to copy the logic… currently I am hitting a wall guard-rails failing add-form-config's return type which is weird since it’s return spec is `

(s/keys :req [::config])
which it generates by itself

roklenarcic19:04:21

timbre_support.cljs:80 ERROR  com/fulcrologic/fulcro/algorithms/form_state.cljc:122 add-form-config's return type
 -- Spec failed --------------------

  {:app.model.sepa/full-name "",
                             ^^
   :app.model.sepa/address ...,
   :app.model.sepa/iban ...,
   :com.fulcrologic.fulcro.algorithms.form-state/config ...}

should satisfy

  (partial app.model.sepa/max-str 140)

or value

  {:app.model.sepa/full-name ...,
   :app.model.sepa/address [],
                           ^^
   :app.model.sepa/iban ...,
   :com.fulcrologic.fulcro.algorithms.form-state/config ...}

should satisfy

  (<= 1 (count %) 7)

-- Relevant specs -------

:app.model.iban/iban:
  (cljs.spec.alpha/and
   cljs.core/string?
   app.model.iban/correct-length?
   app.model.iban/valid-checksum?)
:app.model.sepa/address:
  (cljs.spec.alpha/coll-of
   :app.model.sepa/address-part
   :min-count
   1
   :kind
   cljs.core/vector?
   :max-count
   7)
:app.model.sepa/full-name:
  (cljs.core/partial app.model.sepa/max-str 140)

-- Spec failed --------------------

  {:app.model.sepa/full-name ...,
   :app.model.sepa/address ...,
   :app.model.sepa/iban "",
                        ^^
   :com.fulcrologic.fulcro.algorithms.form-state/config ...}

should satisfy

  correct-length?

-- Relevant specs -------

:app.model.iban/iban:
  (cljs.spec.alpha/and
   cljs.core/string?
   app.model.iban/correct-length?
   app.model.iban/valid-checksum?)
:app.model.sepa/address:
  (cljs.spec.alpha/coll-of
   :app.model.sepa/address-part
   :min-count
   1
   :kind
   cljs.core/vector?
   :max-count
   7)
:app.model.sepa/full-name:
  (cljs.core/partial app.model.sepa/max-str 140)

-------------------------
Detected 2 errors

roklenarcic19:04:51

why does failing general specs on input data fail guardrails on return spec of add-form-config , makes no sense…

Jakub Holý (HolyJak)20:04:19

I do not know anything about this but the :com.fulcrologic.fulcro.algorithms.merge/not-found is a hint. Is something wrong with the state of the entity, i.e. the input data that feeds into add-form-config ? What and why? You require full-name but somehow it is missing / Fulcro is unable to generate a prisitne state for it. Can you see anything there?

roklenarcic20:04:03

yeah but the error says there’s something wrong with add-form-config’s return type

Jakub Holý (HolyJak)20:04:43

Yes, because it contains the .../not-found keyword where there should be a string. If you started with a valid entity - with valid data without "not found", it would have worked I assume.

Jakub Holý (HolyJak)20:04:40

When you fetch data from Pathom and ask for an attribute that it cannot resolve, it sets the value to this not-found key.

roklenarcic20:04:08

OK I now return "" and that does not match the spec for that form element (at least 1 character is required)

roklenarcic20:04:08

and I still get an error… is the implication here that I cannot add form config to data that doesn’t validate on form field spec?

roklenarcic20:04:18

would be weird if that was true

Jakub Holý (HolyJak)20:04:32

Sorry, I have no idea about that. I was just guessing to try help you troubleshoot the problem.

fjolne21:04:10

@roklenarcic i’ve hit the same issue with guardrails validating my specs on add-form-config for whatever reason jfyi you can disable guardrails to throw exceptions on errors (so that it would just print them)

roklenarcic22:04:20

I’ll open an issue, it seems to be validating everything it can find…

tony.kay22:04:09

@roklenarcic this is not specific to guardrails

tony.kay22:04:30

It is a “feature” of spec that I’m still annoyed with and struggling to cope with when writing specs.

tony.kay22:04:51

If you say map?, then spec seems to check every key in the map…then again, I copied a lot of guardrails from ghostwheel, so perhaps that library does that? I don’t think that’s the case…I think it is spec, and there is nothing that can be done on our end other than, as @UDQ2UEPMY suggested, turn off throwing (which I turn off for dev, and on for tests).

tony.kay22:04:41

unfortunately, this means you’ll get warnings from guardrails (or even exceptions) when you put a thing in a map and doesn’t fit the spec, even for a transient time period. On the one hand I see why, but on the other it prevents you from doing specs for intermediate helper functions that might use a key when in some intermediate state, or in the case of FUlcro the form-state config had to have its spec changes to be “this map of stuff, or an ident” so that it would die when seeing that key normalized.

roklenarcic22:04:38

I’ll close the issue

roklenarcic22:04:47

I always forget about this totally stupid undocumented crap in spec

fjolne07:04:04

@tony.kay thanks for clarification, i thought that was the exclusive feature of s/keys to check all the keys, didn’t know about map?

Jakub Holý (HolyJak)19:04:08

According to this, it does not do it for map?, at least not in this simple case

(s/def ::int integer?)
=> :com.example.client/int
(s/valid? map? {::int "I fail"})
=> true
(s/valid? (s/keys) {::int "I fail"})
=> false

tony.kay19:04:37

This is Guardrails checking code that runs when enabled:

(defn run-check [args? {:keys [log-level vararg? throw? fn-name]} spec value]
  (let [vargs?          (and args? vararg?)
        varg            (if vargs? (last (seq value)) nil)
        specable-args   (if vargs?
                          (if (map? varg) (into (vec (butlast value)) (flatten (seq varg))) (into (vec (butlast value)) (seq varg)))
                          value)
        valid-exception (atom nil)]
    (try
      (when-not (s/valid? spec specable-args)
        (let [config  (assoc log/*config* :output-fn output-fn)
              problem (exp/expound-str spec specable-args)]
          (log/log* config (or log-level :error) (str fn-name (if args? " argument list" " return type") "\n") problem)
          (when throw?
            (reset! valid-exception (ex-info problem {:fn-name (str fn-name)})))))
      (catch #?(:cljs :default :clj Throwable) e
        (log/error e "BUG: Internal error in expound or clojure spec.")))
    (when @valid-exception
      (throw @valid-exception)))
  nil)

tony.kay19:04:39

So I have no idea why valid? for map? in GR would be any diff from what you just did, yet I’ve seen it fail like @UDQ2UEPMY is saying, so not sure what is up. My assumption from my obsv. was that it was checking the keys…not sure what is going on now.

Jakub Holý (HolyJak)20:04:33

Works for me:

(quard/run-check false {:throw? true} map? {::int "I fail"})
=> nil
(quard/run-check false {:throw? true} (s/keys) {::int "I fail"})
Error: -- Spec failed --------------------
so something else is at play here...

Jakub Holý (HolyJak)20:04:52

I have tried to replicate the form problem but failed. Any tips? I expected this to fail with a Spec error but it did not:

(comp/defsc NameComp [_ {::keys [name]}]
    {:query [::name]}
    (dom/p name))
(s/def ::name string?)
(fs/add-form-config NameComp {::name :not-found})
Tony: No problem with conform either:
(s/conform map? {::int "I fail"})
=> {:com.example.client/int "I fail"}

tony.kay00:04:50

no ideas myself

tony.kay00:04:53

version of spec???

Jakub Holý (HolyJak)06:04:18

I use the one that comes with RAD, 0.2.176 @roklenarcic Could you try my code snippet ☝️ and see whether it fails for you?

Jakub Holý (HolyJak)20:04:42

@tony.kay I am online now for a while, if you want any assistance with https://github.com/fulcrologic/fulcro-rad/issues/32

tony.kay22:04:58

@holyjak sorry, I just have bigger fish to fry. If you find something and want to send a PR fine, but I don’t want to discuss or even see issues like this for the time being. If I run into them myself I’ll fix them. If you want to patch something you see a fix for, great. otherwise, I’m sorry, I just don’t have the bandwidth for the distractions

👍 4
Jakub Holý (HolyJak)08:04:16

Ok, got it. I will stick to the version of RAD that works then. I will try to dig into the code base but it will take me quite a while to be able to understand it somewhat :'( I'd love to send a PR but I am very far from having the knowledge to be able to make one.