Fork me on GitHub
#re-frame
<
2022-04-03
>
zach08:04:35

Hi, I was wondering if folks had advice on implementing mobile-ready forms using re-frame. I am moving through the book ‘Web Development with Clojure’ and using its pattern for building forms, but it doesn’t seem to work for phones. I am not sure if I am missing something, or if there is a better pattern? I’ll add more context/code as a thread to this message. Thank you for any pointers you could give!

zach08:04:08

In the book, they recommend creating db entries for the form fields, and then using a reagent/track atom for when someone is typing into a field, then dispatching an event when they move away from the form to add the input to the field in the db.

zach08:04:47

So you have a component like so:

(defn text-input
  [{val :value
    attrs :attrs
    :keys [on-save]}]
  (let [draft (r/atom nil)
        value (r/track #(or @draft @val ""))]
    (fn []
      [:input.cg__form_text (merge attrs
                                   {:type :text
                                    :placeholder "…"
                                    :on-focus #(reset! draft (or @val ""))
                                    :on-blur (fn []
                                               (on-save (or @draft ""))
                                               (reset! draft nil))
                                    :on-change #(reset! draft (.. % -target -value))
                                    :value @value})])))

zach08:04:58

put into a form like so:

(defn new-game-form
  []
  [:form.cg__form
   [:div.cg__form_group
    [:label {:for :p1} "Player One "]
    [text-input {:attrs {:name :p1}
                 :value (rf/subscribe [:new-game/field :p1])
                 :on-save #(rf/dispatch [:new-game/set-field :p1 %])}]]
   [:div.cg__form_group
    [:label {:for :p2} "Player Two "]
    [text-input {:attrs {:name :p2}
                 :value (rf/subscribe [:new-game/field :p2])
                 :on-save #(rf/dispatch [:new-game/set-field :p2 %])}]]
   [:input.form_submit {:type :submit
                                    :on-click #(do (.preventDefault %)
                                                   (rf/dispatch [:new-game/start
                                                                 @(rf/subscribe [:new-game/form])]))
                                    :value "Start game"}]])

zach08:04:12

and then I have the re-frame events and subs for this form, like so:

;; events

(rf/reg-event-db
 :form/set-field
 [(rf/path :form/fields)]
 (fn [fields [_ id value]]
   (assoc fields id value)))
;; subs
(rf/reg-sub
 :new-game/form
 (fn [db]
   (:new-game-form db)))

(rf/reg-sub
 :new-game/field
 :<- [:new-game/form]
 (fn [fields [_ id]]
   (get fields id)))

zach08:04:49

The issue i am having is that the dispatch event to update a field is only called on-blur, and the change itself is being tracked in a component-only atom. But if i try this form on a phone, the second field often doesn’t get saved. This is because I am touching the field, filling out the name, then tapping the submit button. I believe on the phone the blur doesn’t register as happening because of the difference of touch versus click.

zach08:04:14

it only works if i tap somewhere else on screen and then tap submit, which is unintuitive. I’ve tried to not use the tracking atom, and instead update the field in the db on-change but that causes intense lag for typing into the field. I am feeling discouraged because this feels like an incredible amount of work to recreate basic html events. I feel I am missing something. Is there a better pattern for making a form that updates the db upon submit, is as responsive as native html, and works on phones? Thank you!

p-himik09:04:53

Try dispatch-sync for input fields.

zach09:04:52

Ah, that’s a good idea! with dispatch-sync, would i not need the local atoms or the on-blur effect, but instead just use the on-change event, and have it call #(rf/dispatch-sync [:set-field (.. % -target -value)])?

p-himik09:04:53

You don't need local atoms here at all. And that track as well. I don't know why the book overcomplicates things.

zach19:04:08

This is fantastic. Thank you!

👍 2