ClojureScript表单简单实用指南
单页面表单,简单逻辑
关于表单提交的最复杂的逻辑,无非包括有以下几种情况: a. 查询表单可初始化数据的接口, 结果分为两种情况,没有可初始化的数据,表单有可初始化的数据(之前提交过的再修改) b. 修改表单 有初始值时需要填入,没有初始值时需要清空 c. 重置表单 d. 表单验证 e. 表单提交
需要注意的点:
查询表单可初始化数据的接口 db中此初始值设置为nil, 并且每次调用接口前, 也需要将其重置为nil, 以免出现没有可用结果返回表单不初始化的问题。 接口调用成功后,将db设置为从接口返回中取值,初始值 {:key1 nil} 定义操作更新数据的 event handler/effect handler
(rf/reg-event-db
:update-key1
(fn [db result]
(assoc-in db [:key1] result)))
也可以这样定义:
(rf/reg-event-fx
:update-key1
(fn [cofx [result]]
(let [db (:db cofx)]
{:db (assoc-in db [:key1] result)})))
清除时直接调用(rf/dispatch [:update-key1 nil])
有时一个按钮操作需要同时执行多个请求,并且有某个接口的参数对其他接口的返回结果有依赖 定义调用接口的 event handler/effect handler
(rf/reg-event-fx
:search-key1
(fn [cofx [_ param]]
(let [db (:db cofx)]
{:db (assoc-in db [:key1] nil)
:http-xhrio {:uri "url"
:method :get
:params param
...
:on-success [:update-key2]
}})))
在实际开发过程中,我经常会遇到reagent中组件的刷新问题, react中的render, github地址 https://github.com/reagent-project/reagent 在此截取某一段 State is handled using Reagent's version of atom, like this:
(defonce click-count (r/atom 0))
(defn state-ful-with-atom []
[:div {:on-click #(swap! click-count inc)}
"I have been clicked " @click-count " times."])
Any component that dereferences a reagent.core/atom will be automatically re-rendered.
If you want do some setting up when the component is first created, the component function can return a new function that will be called to do the actual rendering:
(defn timer-component []
(let [seconds-elapsed (r/atom 0)]
(fn []
(js/setTimeout #(swap! seconds-elapsed inc) 1000)
[:div
"Seconds Elapsed: " @seconds-elapsed])))
This way you can avoid using React's lifecycle callbacks like getInitialState and componentWillMount most of the time
代码示例
此代码可参考之处无非是: form表单中的项根据指定item动态生成 表单的验证、提交和重置,都使用antd的方法 表单Input无法输入中文的问题 其实这个问题还是控件render的问题,有些写法,导致每次input输入时会使得整个表单重绘,就会遇到只能输入英文字符,或者输入一个字符就失去焦点的问题(错误的示例正是此问题) 当然,如果你在写表单相关或者还未入手,可以参考此段代码,看看代码,同时找找问题,一起进步~
(ns size-measure.test
(:require
[reagent.core :as r :refer [atom]]
[re-frame.core :as rf]
[ajax.core :as http]
[day8.re-frame.http-fx]
[size-measure.utils :as utils]
["antd" :as ant]))
(defn- concat-key [keys]
(concat [:test] keys))
(defn- data->db [db keys value]
(assoc-in db (concat-key keys) value))
(defn- get-value-from-db [db keys]
(get-in db (concat-key keys)))
(rf/reg-event-db
:test-init
(fn [_ _]
{:test {:api-result nil
:api-result-2 nil}}))
(rf/reg-event-db
:search-api-result
(fn [db _]
(let [random-n (int (rand 100))
init-result {:key1 (str "val1+" random-n)}
result (if (> 50 random-n) nil init-result)]
(data->db db [:api-result] result))))
(rf/reg-event-db
:set-api-result-2
(fn [db [_ result2]]
(data->db db [:api-result-2] {:key-test-2 (:key-test result2)
:key2-test-2 (:key2-test result2)})))
;;返回map, 请求多个接口,并且第二个接口对第一个接口返回结果有依赖
(rf/reg-event-fx
:search-api-result-multi
(fn [cofx [_ _]]
(let [random-n (fn [] (rand-int 100))
init-result (fn [value]
{:key-test (str "1-val-" value)
:key2-test (str "2-val-" value)}
)
result (fn [] (let [value (random-n)] (if (> 50 value) nil (init-result value))))
]
{:db (data->db (:db cofx) [:api-result] (result))
:dispatch [:set-api-result-2 (result)]})))
(rf/reg-event-fx
:submit-form
(fn [cofx [_ form-datas]]
(prn "form-datas:>>>" form-datas)
(js/alert (str "你填了" (clojure.string/join ", " (vals form-datas))))
(let []
{})))
(rf/dispatch-sync [:test-init])
(rf/reg-sub
:api-result
(fn [db _] (-> db :test :api-result)))
(rf/reg-sub
:api-result-2
(fn [db _] (-> db :test :api-result-2)))
(def api-result-data (rf/subscribe [:api-result]))
(def api-result-data-2 (rf/subscribe [:api-result-2]))
(def form-items [{:name "1表单项1" :form-key :key-test :form-name "keyTest"}
{:name "1表单项2" :form-key :key2-test :form-name "key2Test"}])
(def form-items-2 [{:name "2表单项1" :form-key :key-test-2 :form-name "keyTest2"}
{:name "2表单项2" :form-key :key2-test-2 :form-name "key2Test2"}])
(def FormItem (.-Item ant/Form))
(defn test-form-1 []
(let []
(prn ">>>>load 1...")
(fn [props]
(prn ">>>>load 2...")
(let [the-form (utils/get-form)
{:keys [resetFields
validateFields]} the-form
;form-data (r/atom @api-result-data)
form-rows (for [item form-items]
[:> FormItem {:label (:name item)}
(utils/decorate-field
the-form (:form-name item)
{:initialValue (if (nil? @api-result-data) "" ((-> item :form-key) @api-result-data))
:rules [{:required true}]
}
[:> ant/Input {:type "text"
:name (:form-name item)
:placeholder (str "请填写" (:name item))
:on-change (fn [e] (.preventDefault e))}]
)])
form-rows-div (fn [] (into [:div ] form-rows))
]
(prn ">>>>load 3....")
[:div
[:> ant/Form {:layout "horizontal"
:on-submit (fn [e]
(.preventDefault e)
#_((-> the-form :validateFields)
(fn [err values]
(if (not err)
(do (prn ">>>form values" values)
(rf/dispatch
[:submit-form (js->clj values :keywordize-keys true)])
))))
(validateFields
(fn [err values]
(if (not err)
(rf/dispatch [:submit-form (js->clj values :keywordize-keys true)])))))}
[:div {:style {:margin-top 10 :color "red"}}
[:div "错误示例"]]
[form-rows-div]
[:div {:style {:margin-top 10 :color "red"}}
[:div "正确示例"]]
(into [:div ] (for [item form-items-2]
[:> FormItem {:label (:name item)}
(utils/decorate-field
the-form (:form-name item)
{:initialValue (if (nil? @api-result-data-2) "" ((-> item :form-key) @api-result-data-2))
:rules [{:required true}]
}
[:> ant/Input {:type "text"
:name (:form-name item)
:placeholder (str "请填写" (:name item))
:on-change (fn [e] (.preventDefault e))}]
)]))
[:> FormItem
[:> ant/Button {:type "default"
;:on-click #(:resetFields the-form)
:on-click #(resetFields)
}
"重置"]
[:> ant/Button {:type "primary"
:html-type "submit" }
"提交"]]]
]))))
(defn test-div []
[:div
[:div
[:> ant/Button
{:type "primary"
:on-click #(rf/dispatch [:search-api-result-multi])}
"点我,从接口初始化表单"]]
(utils/create-form test-form-1)])
(defn ^:dev/after-load mount-root []
(r/render [:div [test-div]]
(.getElementById js/document "app")))
(defn init! []
(mount-root))