ant design form表单的使用
ant design form表单的使用
在表单页面中,如果表单元素较多,那么各项元素值的绑定、初始化、修改更新,尤其是提交前的校验和提示,就会变得尤为繁杂,而使用ant design中的form表单则可以使我们处理起来简洁高效、清晰明了。
React的原生用法
在介绍clojure用法之前我先附上form组件使用React的原生的写法,便于后边我们的对比与理解。
import { Form, Input } from 'antd';
@Form.create()
class UserForm extends React.PureComponent {
handleChangeGender = (value) => {
const { form: { setFieldsValue } } = this.props;
setFieldsValue({ userName: value });
};
onClickSubmit = () => {
const { form } = this.props;
form.validateFields((err, values) => {
if (!err) {
// 把values提交给接口
}
});
};
renderFormView() {
const {
form: { getFieldDecorator },
userForm: { data },
} = this.props;
return (
<Form>
<Form.Item {...formItemLayout} label="用户名">
{getFieldDecorator('userName', {
initialValue: data.userName || '',
rules: [{ required: true, message: '请输入用户名' }],
})(<Input placeholder="请输入用户名" />)}
</Form.Item>
</Form>
);
}
}
从上面的示例中可以看出,主要是form的create、getFieldDecorator、validateFields方法的使用。
而关于前两个方法之前大神已经在utils中写了三个工具方法便于我们的使用:
create-form、decorate-field、get-form(该方法用于获取当前创建的form变量在后续的decorate-field方法中使用)。
clojure用法示例
由于示例代码之间的联系比较紧密,不便拆分单独演示,所以就直接把代码一块贴出来了,下边再分步讲解。
(ns custom-manage.user.user-form-views
(:require
["antd" :as ant]
[custom-manage.common.utils :as utils]))
(def FormItem (.-Item ant/Form))
(def SelectOption (.-Option ant/Select))
(def user-detail (rf/subscribe [:user-sub/user-form]))
(defn user-form-view []
(utils/create-form
(fn [props]
(let [form (utils/get-form)
{:keys [resetFields
setFieldsValue
validateFieldsAndScroll]} form]
[:> ant/Form
[:> FormItem {:label "性别:"
:labelCol {:span 6}
:wrapperCol {:span 10}}
(utils/decorate-field
form "user_gender"
{:initialValue (:user_gender @user-detail)
:rules [{:required true :message "请选择性别"}]}
[:> ant/Select {:placeholder "请选择性别"
:onChange (fn [value]
(if (= "man" value)
(setFieldsValue (clj->js {:user_name "先生"}))
(setFieldsValue (clj->js {:user_name "女士"}))))}
[:> SelectOption {:key "man"} "男"]
[:> SelectOption {:key "woman"} "女"]])]
[:> FormItem {:label "用户名:"
:labelCol {:span 6}
:wrapperCol {:span 10}}
(utils/decorate-field
form "user_name"
{:initialValue (:user_name @user-detail)
:rules [{:required true :message "请输入用户名"}]}
[:> ant/Input {:placeholder "请输入用户名"}])]
[:> FormItem {:wrapperCol {:offset 10}}
[:> ant/Button {:type "danger"
:on-click (fn []
(resetFields))} "重置"]]
[:> FormItem {:wrapperCol {:offset 10}}
[:> ant/Button {:type "primary"
:on-click (fn []
(validateFieldsAndScroll
(fn [err values]
(prn "values---" values)
(when (not err)
(rf/dispatch [:user/submit-userInfo
(js->clj values :keywordize-keys true)])))))} "提交"]]]))))
示例比较简单,场景就是一个性别选择器,一个用户名输入框,并且选择完性别后会自动级联在用户名输入框中填充“先生”或“女士”。最后是一个重置和提交的按钮。
场景很简单,但是基本展示了form的常用方法:
- 布局
- 初始化
- 校验规则
- 联动赋值
- 重置
- 提交
布局问题
表单布局主要分为三种:横向、竖向、行内 比较简单,主要是属性的设置,详情请参考表单布局
值的初始化和校验规则设置
这两项主要是依靠form的
getFieldDecorator方法也就是工具类方法decorate-field的第三个参数options进行配置initialValue和rules。 关于校验,除了刚才最基础的用法外,还可以自定义校验。当需要封装、复用或者通过数据生成
FormItem时,请使用custom-manage.common.utils下的decorate-field进行双向绑定。 并且保证其第一个参数的form是在create-form参数中通过get-form获取到的。
联动赋值
- 在某些场景下,设置某个表单后,同时需要联动修改另一个相关表单的值,这就需要
setFieldsValue方法来实现。
注意:该函数是form提供的js函数,并且只要是antd函数,通props暴露出来的,都是js函数,它的参数需要用 clj->js转一下。
(setFieldsValue (clj->js {:user_name "先生"}))
- 联动的复杂情况是,当改了表单值之后,需要根据变化的值进行业务处理(比如:复选框里包含全选选项时,切换全选选项要处理整个CheckboxGroup选项的选中状态)。
这种情况建议使用
decorate-field的第三个参数options的normalize属性。例如:
(utils/decorate-field
form "craft_ids"
{:initialValue (:craft_ids @default-craft)
:rules [{:required true :message "请选择工艺"}]
:normalize (fn [value _]
;; 2019/12/3 alisa 同一工艺的叶子节点互斥校验
(let [ids (js->clj value)
is-parent? (fn [s id] (some #(-> % :craftwork_id (compare id) zero?) (:children s)))
parent-map (reduce
(fn [m id]
(let [parent-nodes (specter/select (specter/walker #(is-parent? % id)) @category-crafts)
parent-id (-> parent-nodes first :craftwork_id)]
(if (contains? m parent-id) m (conj {parent-id id} m))))
{}
(reverse ids))
select-ids-js (clj->js (vals parent-map))]
; (rf/dispatch [:set-default-craft/select-craft-ids (vals parent-map)])
select-ids-js))}
重置表单
使用resetFields方法
提交
通过调用validateFieldsAndScroll方法,会自动触发表单的校验规则,如果校验通过则匿名函数的第一个参数err会为空并且函数的第二个值values就是所有表单的值。
注意:获取的values数据是js类型的,所以需要js->clj转一下(js->clj values :keywordize-keys true)
数据转换问题
- 最后顺便记录一下因为数据转换问题,在table页面踩过的一次坑:
(def columns [{:title "操作"
:dataIndex "action"
:render
(fn [val row]
(prn "id---" row.craftwork_id) ;; 此时本地开发打印是有值的
(r/as-element
[:div
[:a {:href (str "/main/craftwork/form?id=" (:craftwork_id (js->clj row :keywordize-keys true)))} "编辑"]]))}])
在回调函数中拿到的row参数是个js类型的数据对象,我开发时使用以前惯用的js手法尝试了一下row.craftwork_id进行取值,发现没有问题,所以感觉不需要js->clj那么麻烦的转换取值,于是很多地方都这么直接js式的取值了,本地运行也完全没有问题。
虽然本地调试OK,但是,在项目发布到线上运行后却发现id拿不到了!其他地方的值也是一样。。。
- js的多层嵌套对象数据一定不要直接使用“点”连续取值,一方面是刚才的问题,另一方面如果连续点多次,如果某次结果是undefined了,再继续点取值时就会崩溃了,所以写js时一定要小心规范。
- clojure的get-in方法就比较友好了,无论对map数据取值多少层没值,都会安全的返回一个nil。