This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2022-04-03
Channels
- # announcements (5)
- # aws (3)
- # babashka (52)
- # babashka-sci-dev (23)
- # beginners (51)
- # calva (191)
- # clj-commons (18)
- # clj-kondo (11)
- # cljdoc (39)
- # cljsrn (3)
- # clojure (24)
- # clojure-czech (3)
- # clojure-dev (2)
- # clojure-europe (15)
- # clojuredesign-podcast (2)
- # clojurescript (8)
- # conjure (2)
- # core-typed (151)
- # cursive (15)
- # data-science (3)
- # datalevin (4)
- # datomic (8)
- # figwheel-main (21)
- # fulcro (37)
- # gratitude (3)
- # honeysql (1)
- # hyperfiddle (2)
- # introduce-yourself (1)
- # malli (3)
- # membrane (54)
- # off-topic (21)
- # other-languages (4)
- # portal (18)
- # re-frame (12)
- # reagent (7)
- # releases (2)
- # sci (64)
- # spacemacs (14)
- # sql (2)
- # vim (4)
- # xtdb (6)
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!
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.
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})])))
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"}]])
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)))
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.
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!
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)])
?