目錄
- 1 form 表單中 FormItem 的布局
- 2 form 表單,F(xiàn)ormItem 的 rules 中新增 validator宛逗,實時請求校驗
- 3 利用 validator 和正則呼盆,驗證中文
- 4 form.validateFields 直接獲取表單的值
- 5 form 表單提交 htmlType年扩,改為 onClick
- 6 Input 組件,利用 maxLength 屬性访圃,限制最大輸入內(nèi)容長度
- 7 InputNumber 只能輸入數(shù)字
- 8 menu 實現(xiàn)回縮效果注意點
- 9 左側(cè)菜單調(diào)整寬度設(shè)置
- 10 表格 Columns 字段 id 頁面不展示情況
- 11 自定義 Modal
- 12 Select 組件清除選框內(nèi)容
- 13 antd厨幻、mobx @注入的順序
- 14 解決 table 組件, key 警告
14.1 方法一:使用 rowKey
14.2 方法二:dataSource 數(shù)據(jù)新增 key - 15 Form.create 方式
15.1 方式一:@ 注解
15.2 方式二:高階寫法 - 16 Form initialValue 值編輯后腿时,表單的值不改變問題
16.1 方法一
16.2 方法二 - 17 Modal 是否顯示 footer 底部按鈕
- 18 有 connect 和 Form 表單
- 19 Tree 樹組件增加右鍵菜單
- 20 直接使用 rc-form 庫 createForm况脆,與 antd Form 的 Form.create() 設(shè)置樣式不同
- 21 Form 表單實時校驗 _.debounce 應(yīng)用,和不同字段相互校驗影響
- 22 Form 組件方法 getFieldsValue 獲取自定義組件的值
- 23 Form 表單清空和重置的區(qū)別以及方法
- 24 DatePicker 組件批糟,部分日期/時間為可選
1 form表單中FormItem的布局
使用 getFieldDecorator 包裹的輸入框或者 Select格了,必須是在最外層,也就是只有一層徽鼎,否則盛末,檢驗會一直不通過弹惦,所以,需要重新布局應(yīng)該在 getFieldDecorator 的外層添加父節(jié)點悄但,而不應(yīng)該在里面肤频。
例:
<FormItem
{...formItemLayout}
label="所屬應(yīng)用"
>
<div>
{getFieldDecorator('apiwgAppName', {
rules: [{ required: false, message: '請選擇' }],
initialValue: apiwgAppName || ""
})(
<Input disabled={this.store.data.apiId ? true : false}
className="control-special" readOnly style={{ width: "70%" }}
onClick={this.showModal.bind(this, "apiwgApp")} />
)}
<Button className="btn-modal" type="primary" onClick={this.showModal.bind(this, "apiwgApp")}
disabled={this.store.data.apiId ? true : false}
>選擇所屬應(yīng)用
</Button>
<a
style={{ marginLeft: '8px' }}
onClick={this.openNewAppDlg.bind(this)}
className={`api-add ${
this.store.data.apiId ? 'disabled' : ''
}`}
>
+新增應(yīng)用
</a>
</div>
</FormItem>
2 form表單,F(xiàn)ormItem 的 rules 中新增 validator算墨,實時請求校驗
<FormItem
labelCol={{ span: 8 }}
wrapperCol={{ span: 15 }}
label="菜單名稱"
>
{form.getFieldDecorator('menuName', {
rules: [
{ required: true, message: '菜單名稱不能為空' },
{ type: 'string', max: 30, message: '菜單名稱過長' },
{ validator: this.handleCheckName },
{ whitespace: true, message: '請輸入非空白內(nèi)容' }
],
initialValue: this.props.menuSysData.menuName,
})(
<Input
// placeholder="請輸入菜單名稱"
disabled={disableFlag}
/>
)}
</FormItem>
// 實時校驗
handleCheckName = (rule, value, callback) => {
const { checkName, actionType } = this.state;
if (!this.trim(value) || (checkName && actionType === 'M' && this.trim(value) === checkName)) {
callback();
return;
}
let params = {
menuName: value,
state: "00A"
};
MenuSysService.checkMenuName(params).then(result => {
if (!result || !result.resultObject) {
return;
}
let code = result.resultObject.code;
if (code && code > 0) {
callback('系統(tǒng)名稱已存在宵荒!');
}
callback();
});
}
3 利用 validator 和正則,驗證中文
<FormItem
hasFeedback={!disableFlag}
labelCol={{ span: 6 }}
wrapperCol={{ span: 15 }}
label="賬號" >
{form.getFieldDecorator('userCode', {
initialValue: '',
rules: [
{ required: !disableFlag, validator: this.usercodeValidator },
{ type: 'string', max: 30, message: '賬號過長' },
{ whitespace: true, message: '內(nèi)容不能為空' }
],
})(
<Input placeholder="請輸入賬號" disabled={account} maxLength="30" autoComplete="false" />)}
</FormItem>
usercodeValidator = (rule, value, callback) => {
const { userData } = this.props;
if (!value) {
callback('內(nèi)容不能為空');
return;
}
// >秽帧1取!中文驗證
const reg = /[\u4E00-\u9FA5]{1,4}/; /*定義驗證表達式*/
if (reg.test(value)) { /*進行驗證*/
callback('賬號不能為中文');
return;
}
if (userData.userCode === value) {
callback();
}
else {
let params = {
userCode: value + "", // 查一下有沒有這個編碼
useState: '10301'
};
SysUserMgService.checkUserCode(params).then(result => {
if (!result || result.code !== '0') {
callback(result.message);
return;
}
if (result.resultObject && result.resultObject.num !== 0) {
callback('該賬號已存在');
return;
}
callback();
});
}
}
4 form.validateFields 直接獲取表單的值
this.props.form.validateFields((err, fieldsValue) => {
if (err) return;
this.handleSubmit(fieldsValue);
});
5 form 表單提交 htmlType挖藏,改為 onClick
說明:因為之前遇到過使用 htmlType 提交表單會有問題暑刃,但是改為 onClick 后,就沒問題了膜眠,所以岩臣,也記錄一下。
htmlType 是官網(wǎng)使用的方式宵膨,具體問題本人當(dāng)時忘記截個圖了架谎。
<Form layout="inline" onSubmit={this.handleSubmit}>
<FormItem
validateStatus={userNameError ? 'error' : ''}
help={userNameError || ''}
>
{getFieldDecorator('userName', {
rules: [{ required: true, message: 'Please input your username!' }],
})(
<Input prefix={<Icon type="user" style={{ color: 'rgba(0,0,0,.25)' }} />} placeholder="Username" />
)}
</FormItem>
<FormItem
validateStatus={passwordError ? 'error' : ''}
help={passwordError || ''}
>
{getFieldDecorator('password', {
rules: [{ required: true, message: 'Please input your Password!' }],
})(
<Input prefix={<Icon type="lock" style={{ color: 'rgba(0,0,0,.25)' }} />} type="password" placeholder="Password" />
)}
</FormItem>
<FormItem>
<Button
type="primary"
htmlType="submit"
disabled={hasErrors(getFieldsError())}
>
Log in
</Button>
</FormItem>
</Form>
// 改變后:
<Form layout="inline" >
<FormItem
validateStatus={userNameError ? 'error' : ''}
help={userNameError || ''}
>
{getFieldDecorator('userName', {
rules: [{ required: true, message: 'Please input your username!' }],
})(
<Input prefix={<Icon type="user" style={{ color: 'rgba(0,0,0,.25)' }} />} placeholder="Username" />
)}
</FormItem>
<FormItem
validateStatus={passwordError ? 'error' : ''}
help={passwordError || ''}
>
{getFieldDecorator('password', {
rules: [{ required: true, message: 'Please input your Password!' }],
})(
<Input prefix={<Icon type="lock" style={{ color: 'rgba(0,0,0,.25)' }} />} type="password" placeholder="Password" />
)}
</FormItem>
<FormItem>
<Button
type="primary"
disabled={hasErrors(getFieldsError())}
onClick={() => this.handleSubmit()}
>
Log in
</Button>
</FormItem>
</Form>
6 Input 組件,利用 maxLength 屬性辟躏,限制最大輸入內(nèi)容長度
<Input
placeholder="請輸入賬號"
disabled={account}
maxLength="30"
autoComplete="off"
/>
7 InputNumber 只能輸入數(shù)字:
<InputNumber
formatter={value => value}
parser={value => parseInt(value) || ''}
style={{ width: '100%' }}
step={1}
onChange={(val) => this.onChangeIpt(1, val)}
/>
8 menu 實現(xiàn)回縮效果注意點
說明:menu 必須放在 Sider 中谷扣,才能實現(xiàn)縮回去的,這個有特定的布局捎琐。
<Sider
style={{ background: '#1D2023', height: '100%' }}
trigger={null}
collapsible
collapsed={this.state.collapsed}
width={140}
collapsedWidth={40}
>
<BaseMenu
toggle={this.toggle}
collapsed={this.state.collapsed}
history={history}
location={location}
/>
</Sider>
9 左側(cè)菜單調(diào)整寬度設(shè)置
說明:通過在 Sider 組件会涎,設(shè)置 width,調(diào)整菜單的寬度瑞凑,通過設(shè)置 collapsedWidth末秃,調(diào)整調(diào)整縮進的寬度。
<Sider
style={{ background: '#1D2023', height: '100%' }}
trigger={null}
collapsible
collapsed={this.state.collapsed}
width={140}
collapsedWidth={40}
>
<BaseMenu
toggle={this.toggle}
collapsed={this.state.collapsed}
history={history}
location={location}
/>
</Sider>
10 表格 Columns 字段 id 頁面不展示情況
說明:一般而言籽御,表格 Columns 字段 id 是在界面不展示的练慕,但是,對于有些邏輯的處理篱蝇,又是需要的贺待,可以使用相應(yīng)樣式隱藏的處理方式徽曲。
常規(guī)展示的情況:
{
title: '序號',
dataIndex: 'algoId',
key: 'algoId'
},
不展示id字段:
{
title: '',
dataIndex: 'algoId',
key: 'algoId',
width: 0,
render: item => {
return (
<span style={{ display: 'none' }} title={item}>
{item}
</span>
);
}
},
11 自定義 Modal
查看元素可知零截,Modal 是在界面構(gòu)建完成之后,由 js 控制秃臣,動態(tài)的添加涧衙,所以想事先獲取 ant-modal-body 中 DOM 元素的節(jié)點是不可能的哪工,但是一般情況也不會去獲取它。
自定義 Modal弧哎,解決上述的問題雁比。
關(guān)鍵代碼:
說明:
1:因為我們使用的是 antd,所以撤嫩,下面的樣式是不需要引入的偎捎。這個跟 antd 的 Modal 樣式重復(fù)。
2:Modal 的隱藏和顯示序攘,是通過控制 class 為 ant-modal-mask 和 ant-modal-wrap 兩個 div 的顯示和隱藏茴她。
- 通過給 ant-modal-mask 的 div,添加另外一個 className:ant-modal-mask-hidden程奠,來控制其隱藏丈牢,也可以通過 display 來控制。
- 通過給 ant-modal-wrap 設(shè)置行內(nèi)樣式 display: none瞄沙,來控制其隱藏己沛。不過,也可以使用 className距境,隨便都可以申尼。
界面布局:
<div className="ant-modal-mask" ></div>
<div tabIndex="-1" className="ant-modal-wrap " role="dialog" aria-labelledby="rcDialogTitle0" style={{}}>
<div role="document" className="ant-modal" style={{ width: '920px' }}>
<div className="ant-modal-content">
<div className="ant-modal-header">
...
</div>
<div className="ant-modal-body" style={{ background: 'rgb(16, 16, 17)' }}>
...
</div>
</div>
</div>
<div tabIndex="0" style={{ width: '0px', height: '0px', overflow: 'hidden' }}>
sentinel
</div>
</div>
樣式:
.ant-modal-mask { // 遮罩層
position: fixed;
top: 0;
right: 0;
left: 0;
bottom: 0;
background-color: rgba(0, 0, 0, 0.65);
height: 100%;
z-index: 1000;
filter: alpha(opacity=50);
}
.ant-modal-wrap {
position: fixed;
overflow: auto;
top: 0;
right: 0;
bottom: 0;
left: 0;
z-index: 1000;
-webkit-overflow-scrolling: touch;
outline: 0;
}
.ant-modal {
font-family: "Chinese Quote", -apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "Helvetica Neue", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
font-size: 14px;
font-variant: tabular-nums;
line-height: 1.5;
color: rgba(0, 0, 0, 0.65);
-webkit-box-sizing: border-box;
box-sizing: border-box;
margin: 0;
padding: 0;
list-style: none;
position: relative;
width: 920px;
margin: 0 auto;
top: 100px;
padding-bottom: 24px;
}
12 Select組件清除選框內(nèi)容
通過給 Select 組件新增 allowClear 屬性。注意:allowClear 也會觸發(fā) onChange 方法垫桂,所以晶姊,也要單獨處理一下,因為 value 和 element 為undefined伪货。
<Select {...this.props} placeholder="請選擇" allowClear={true} >
...
</Select>
13 antd们衙、mobx @注入的順序
14 解決table組件, key警告
一般都是使用 rowkey 方法一解決(后臺數(shù)據(jù)要保證沒有重復(fù))碱呼;
14.1 方法一:使用 rowKey
14.2 方法二:dataSource 數(shù)據(jù)新增 key
15 Form.create 方式
15.1 方式一:@注解
@Form.create({})
15.2 方式二:高階寫法
export default (Form.create({})(APP));
16 Form initialValue 值編輯后蒙挑,表單的值不改變問題
16.1 方法一
其實,只要編輯成功后愚臀,回調(diào)調(diào)用 form.resetFields()忆蚀,就可以了,如果
是使用 modal 框彈出的表單姑裂,就可以直接使用 destroyOnClose = {true} 屬性馋袜。
import React from 'react';
import { Input, Modal, Form } from 'antd';
import styles from './UserModal.less';
const FormItem = Form.Item;
const UserModal = ({ currentItem, dispatch, form, visible }) => {
function handleOk() {
form.validateFields((err, fieldsValue) => {
if (err) return;
dispatch({
type: 'demo/update',
payload: {
currentItem: fieldsValue
}
});
});
}
function handleCancel() {
dispatch({
type: 'demo/hideModal'
})
}
const formItemLayout = {
labelCol: {
xs: { span: 24 },
sm: { span: 4 },
},
wrapperCol: {
xs: { span: 24 },
sm: { span: 20 },
},
};
const { getFieldDecorator } = form;
return (
<div className={styles.root}>
<Modal
title="編輯"
visible={visible}
onOk={() => handleOk()}
onCancel={() => handleCancel()}
destroyOnClose={true}
>
<Form>
<FormItem
{...formItemLayout}
label="用戶名"
>
{getFieldDecorator('name', {
initialValue: currentItem.name,
rules: [{
required: true, message: 'Please input your name!',
}],
})(
<Input placeholder="請輸入用戶名" />
)}
</FormItem>
<FormItem
{...formItemLayout}
label="年齡"
>
{getFieldDecorator('age', {
initialValue: currentItem.age,
rules: [{
required: true, message: 'Please input your age!',
}],
})(
<Input placeholder="請輸入年齡" />
)}
</FormItem>
<FormItem
{...formItemLayout}
label="地址"
>
{getFieldDecorator('address', {
initialValue: currentItem.address,
rules: [{
required: true, message: 'Please input your address!',
}],
})(
<Input placeholder="請輸入地址" />
)}
</FormItem>
</Form>
</Modal>
</div>
)
}
export default (Form.create({})(UserModal));
主要代碼:destroyOnClose={true}
<Modal
title="編輯"
visible={visible}
onOk={() => handleOk()}
onCancel={() => handleCancel()}
destroyOnClose={true}
>
...
</Modal>
16.2 方法二
如果是 class 類,可以使用鉤子舶斧。
componentDidUpdate = (prevProps, prevState) => {
if (!prevProps.visible) {
prevProps.form.resetFields();
}
};
代碼參考:
import React from 'react';
import { Input, Modal, Form } from 'antd';
import styles from './UserModal.less';
const FormItem = Form.Item;
@Form.create({})
class UserModal extends React.PureComponent {
componentDidUpdate = (prevProps, prevState) => {
if (!prevProps.visible) {
prevProps.form.resetFields();
}
};
handleOk = () => {
const { dispatch, form } = this.props;
form.validateFields((err, fieldsValue) => {
if (err) return;
dispatch({
type: 'demo/update',
payload: {
currentItem: fieldsValue
}
});
});
}
handleCancel = () => {
const { dispatch } = this.props;
dispatch({
type: 'demo/hideModal'
})
}
render() {
const { currentItem, form, visible } = this.props;
const formItemLayout = {
labelCol: {
xs: { span: 24 },
sm: { span: 4 },
},
wrapperCol: {
xs: { span: 24 },
sm: { span: 20 },
},
};
const { getFieldDecorator } = form;
return (
<div className={styles.root}>
<Modal
title="編輯"
visible={visible}
onOk={() => this.handleOk()}
onCancel={() => this.handleCancel()}
>
<Form>
<FormItem
{...formItemLayout}
label="用戶名"
>
{getFieldDecorator('name', {
initialValue: currentItem.name,
rules: [{
required: true, message: 'Please input your name!',
}],
})(
<Input placeholder="請輸入用戶名" />
)}
</FormItem>
<FormItem
{...formItemLayout}
label="年齡"
>
{getFieldDecorator('age', {
initialValue: currentItem.age,
rules: [{
required: true, message: 'Please input your age!',
}],
})(
<Input placeholder="請輸入年齡" />
)}
</FormItem>
<FormItem
{...formItemLayout}
label="地址"
>
{getFieldDecorator('address', {
initialValue: currentItem.address,
rules: [{
required: true, message: 'Please input your address!',
}],
})(
<Input placeholder="請輸入地址" />
)}
</FormItem>
</Form>
</Modal>
</div>
)
}
}
export default UserModal;
// export default (Form.create({})(UserModal));
17 Modal 是否顯示 footer 底部按鈕
一般應(yīng)用場景欣鳖,詳情不需要底部按鈕,新增和修改需要茴厉。
解決:
通過父組件傳遞一個空的字符串或者 {footer: null} 給 Modal 組件進行屬性解構(gòu)泽台。
18 有 connect 和 Form 表單
function mapStateToProps({ onlineCamera }) {
return {
favorites: onlineCamera.favorites,
};
}
export default connect(mapStateToProps)(Form.create()(TreeModal));
19 Tree 樹組件增加右鍵菜單
參考:
https://github.com/ant-design/ant-design/issues/5151
關(guān)鍵代碼:
<Tree onRightClick={this.treeNodeonRightClick} >
// 實現(xiàn)這個方法 treeNodeonRightClick
treeNodeonRightClick(e) {
this.setState({
rightClickNodeTreeItem: {
pageX: e.event.pageX,
pageY: e.event.pageY,
id: e.node.props['data-key'],
categoryName: e.node.props['data-title']
}
});
}
// id 和 categoryName 是生成時綁上去的
<TreeNode
key={item.id}
title={title}
data-key={item.id}
data-title={item.categoryName}
/>);
// 最后綁個菜單就可以實現(xiàn)了
getNodeTreeRightClickMenu() {
const {pageX, pageY} = {...this.state.rightClickNodeTreeItem};
const tmpStyle = {
position: 'absolute',
left: `${pageX - 220}px`,
top: `${pageY - 70}px`
};
const menu = (
<Menu
onClick={this.handleMenuClick}
style={tmpStyle}
className={style.categs_tree_rightmenu}
>
<Menu.Item key='1'><Icon type='plus-circle'/>{'加同級'}</Menu.Item>
<Menu.Item key='2'><Icon type='plus-circle-o'/>{'加下級'}</Menu.Item>
<Menu.Item key='4'><Icon type='edit'/>{'修改'}</Menu.Item>
<Menu.Item key='3'><Icon type='minus-circle-o'/>{'刪除目錄'}</Menu.Item>
</Menu>
);
return (this.state.rightClickNodeTreeItem == null) ? '' : menu;
}
getNodeTreeRightClickMenu 方法放在 render 中:
getNodeTreeRightClickMenu 方法是放在生成主界面的方法里( render )什荣,因為每一次 state 的變化后,render 方法都會執(zhí)行怀酷,所以變一下任意的 this.state 里面的狀態(tài)稻爬,就會執(zhí)行 render 方法 ,這樣 getNodeTreeRightClickMenu 方法放在 render 方法里來生成界面的一部分蜕依。就可以了
項目中實現(xiàn)關(guān)鍵代碼:
/*
* @Author: lin.zehong
* @Date: 2018-12-02 22:13:59
* @Last Modified by: lin.zehong
* @Last Modified time: 2018-12-19 16:36:27
* @Desc: 收藏夾--樹
*/
import React from 'react';
import { connect } from 'dva';
import { Tree, Menu } from 'antd';
import Zcon from 'zteui-icon';
import styles from './TreeCollect.less';
const { TreeNode } = Tree;
class TreeCollect extends React.Component {
state = {
expandedKeys: ['-1'],
}
// 樹節(jié)點右鍵事件
treeNodeonRightClick = ({ event, node }) => {
event.persist();
const { offsetLeft, _isCollapsed } = this.props;
const menuWidth = _isCollapsed ? 80 : 200;
const { favorites, favoritesDetail } = node.props;
this.changefavorites(favorites);
const hasChild = !!(favorites && favorites.scjId); // 收藏夾
this.setState({
rightClickNodeTreeItem: {
pageX: event.pageX - offsetLeft - 16 - menuWidth,
pageY: event.target.offsetTop + 28,
key: node.props.eventKey,
id: node.props.eventKey,
title: node.props.title,
favorites,
favoritesDetail,
hasChild,
},
});
}
// 右鍵節(jié)點頁面展示
getNodeTreeRightClickMenu = () => {
const { rightClickNodeTreeItem } = this.state;
const { pageX, pageY, hasChild, key } = { ...rightClickNodeTreeItem };
const tmpStyle = {
position: 'absolute',
left: `${pageX}px`,
top: `${pageY}px`,
boxShadow: '2px 2px 10px #333333',
};
const menuHasNode = (
<Menu
onClick={this.handleMenuClick}
style={tmpStyle}
className={styles.categs_tree_rightmenu}
>
<Menu.Item key='1'>自動巡查</Menu.Item>
<Menu.Item key='2'>重命名</Menu.Item>
<Menu.Item key='3'>添加同級目錄</Menu.Item>
<Menu.Item key='4'>添加子目錄</Menu.Item>
<Menu.Item key='5'>刪除</Menu.Item>
</Menu>
);
const menuRoot = (
<Menu
onClick={this.handleMenuClick}
style={tmpStyle}
className={styles.categs_tree_rightmenu}
>
<Menu.Item key='1'>自動巡查</Menu.Item>
<Menu.Item key='2'>重命名</Menu.Item>
<Menu.Item key='4'>添加子目錄</Menu.Item>
</Menu>
);
const menuNoNode = (
<Menu
onClick={this.handleMenuClick}
style={tmpStyle}
className={styles.categs_tree_rightmenu}
>
<Menu.Item key='6'>取消收藏</Menu.Item>
</Menu>
);
const menu = hasChild ? (key === "-1" ? menuRoot : menuHasNode) : menuNoNode;
return (rightClickNodeTreeItem == null) ? '' : menu;
}
// 隱藏右鍵菜單
hideTreeRight = () => {
this.setState({ rightClickNodeTreeItem: null });
}
render() {
const { expandedKeys, selectedKeys } = this.state;
const { isExpand, gData } = this.props;
const loop = data => data.map((item) => {
if (item.children && item.favorites) {
return <TreeNode key={item.key} icon={<Zcon type="thing" />} title={item.title} favorites={item.favorites}>{loop(item.children)}</TreeNode>;
}
return <TreeNode key={item.favoritesDetail.sxtxxId} title={item.title} favoritesDetail={item.favoritesDetail} />;
});
return (
<div className={`${styles.root} ${isExpand ? '' : styles.hideTree}`} onClick={() => this.hideTreeRight()}>
<Tree
showIcon
className="draggable-tree"
defaultExpandedKeys={expandedKeys}
selectedKeys={selectedKeys}
onRightClick={this.treeNodeonRightClick}
onSelect={this.onSelect}
>
{loop(gData)}
</Tree>
{this.getNodeTreeRightClickMenu()}
</div>
);
}
}
function mapStateToProps({ onlineCamera, publicModel }) {
return {
gData: onlineCamera.collectTree,
cameraNum: onlineCamera.cameraNum,
inspectionCamera: onlineCamera.inspectionCamera,
_isCollapsed: publicModel._isCollapsed,
};
}
export default connect(mapStateToProps)(TreeCollect);
20 直接使用 rc-form 庫 createForm桅锄,與 antd Form 的 Form.create() 設(shè)置樣式不同
- 使用 antd Form 的 Form.create()
import React from 'react'
import PropTypes from 'prop-types'
import { Form, Button } from 'antd'
class BalloonContent extends React.Component {
render() {
const { form } = this.props;
return (
<div>
<Form
size='medium'
className={Styles.wrapForm}
>
<Form.Item
label="算子輸出"
>
{form.getFieldDecorator('stdioOutput', {
rules: [
{
required: true,
message: '輸出不能為空',
},
],
})(<Input />)}
</Form.Item>
</Form>
</div>
)
}
}
export default Form.create()(BalloonContent) // !!!
- 直接使用 rc-form 庫 createForm
import React from 'react'
import PropTypes from 'prop-types'
import { Form, Button } from 'antd'
import { createForm } from 'rc-form'
class BalloonContent extends React.Component {
render() {
const { form } = this.props;
const { getFieldDecorator, getFieldError } = form ;
const stdioOutputError = getFieldError('stdioOutput'); // !!!
return (
<div>
<Form
size='medium'
className={Styles.wrapForm}
>
<Form.Item
label="算子輸出"
required // !!!
validateState={stdioOutputError ? 'error' : 'success'} // !!!
help={stdioOutputError} // !!!
>
{form.getFieldDecorator('stdioOutput', {
rules: [
{
required: true,
message: '輸出不能為空',
},
],
})(<Input />)}
</Form.Item>
</Form>
</div>
)
}
}
export default createForm ()(BalloonContent) // !!!
21 Form 表單實時校驗 _.debounce 應(yīng)用,和不同字段相互校驗影響
例如:表單字段样眠,密碼和確認密碼竞滓,改變 Password,如果與 Confirm Password 不一致吹缔,也會在 Confirm Password 做提示:
官網(wǎng)示例:注冊新用戶商佑,主要代碼
compareToFirstPassword = (rule, value, callback) => {
const { form } = this.props;
if (value && value !== form.getFieldValue('password')) {
callback('Two passwords that you enter is inconsistent!');
} else {
callback();
}
};
validateToNextPassword = (rule, value, callback) => {
const { form } = this.props;
if (value && this.state.confirmDirty) {
form.validateFields(['confirm'], { force: true });
}
callback();
};
<Form.Item label="Password" hasFeedback>
{getFieldDecorator('password', {
rules: [
{
required: true,
message: 'Please input your password!',
},
{
validator: this.validateToNextPassword,
},
],
})(<Input.Password />)}
</Form.Item>
<Form.Item label="Confirm Password" hasFeedback>
{getFieldDecorator('confirm', {
rules: [
{
required: true,
message: 'Please confirm your password!',
},
{
validator: this.compareToFirstPassword,
},
],
})(<Input.Password onBlur={this.handleConfirmBlur} />)}
</Form.Item>
實際項目例子,選擇所屬數(shù)據(jù)庫厢塘,校驗表名:
主要代碼:
import _ from "lodash";
// 寫入新表茶没,選擇數(shù)據(jù)庫,需要校驗已有的表名
validateToTableName = (rule, value, callback) => {
const { form: { getFieldValue, validateFields }} = this.props;
const targetTableCode = getFieldValue("targetTableCode");
if (targetTableCode) {
validateFields(['targetTableCode'], { force: true });
}
callback();
};
// 寫入新表晚碾,校驗表名
// eslint-disable-next-line
validateTableExist = _.debounce((rule, value, callback) => {
const { form: { getFieldValue }, dispatch } = this.props;
const targetDataSource = getFieldValue("targetDataSource");
const targetTableCode = getFieldValue("targetTableCode");
dispatch({
type: "applyDetail/tableExist",
payload: {
dataSourceCode: targetDataSource,
table: targetTableCode,
},
}).then(result => {
if (result) {
callback("該表名已存在");
} else {
callback();
}
})
}, 500);
<Form.Item label="所屬數(shù)據(jù)庫">
{getFieldDecorator("targetDataSource", {
rules: [
{
required: true,
message: "請選擇所屬數(shù)據(jù)庫",
},
{
validator: this.validateToTableName, // Wグ搿!格嘁!
},
],
initialValue:
exchangeFormat.targetDataSource ||
(dataSourceList.length > 0
? dataSourceList[0].code
: undefined),
})(dataBaseComponent({ className: styles.formInput }))}
</Form.Item>
<Form.Item label="表名">
{getFieldDecorator("targetTableCode", {
rules: [
{
required: true,
message: "請輸入新表表名",
},
{
pattern: checkBackEndTableName,
message: "只支持英文字母笛求、數(shù)字、英文格式糕簿、下劃線",
},
{
validator: this.validateTableExist, // L饺搿!懂诗!
},
],
initialValue:
(exchangeFormat.formatType === WRITE_IN_NEW_TABLE
? exchangeFormat.targetTableCode
: undefined) || undefined,
})(
<Input
className={styles.formInput}
disabled={disabled}
placeholder="請輸入"
/>
)}
</Form.Item>
22 Form 組件方法 getFieldsValue 獲取自定義組件的值
項目實例:對 antd RangePicker 抽取完獨立組件后蜂嗽,form 表單獲取不到值
自定義組件被 getFieldsValue 包裹,會獲得以下屬性:
onChange方法, 子組件調(diào)用此方法殃恒,可將值傳給父組件植旧,從而Form可拿到自定義組件的值 value屬性,獲得初始值
<Form.Item label="發(fā)送時間">
{getFieldDecorator('range-time-picker', {
rules: [{ required: false, message: '請輸入開始時間-結(jié)束時間' }],
})(
<RangePickerPage />
)}
</Form.Item>
下面是對 antd RangePicker 進行封裝离唐,通過組件 RangePicker 本身的 onChange 方法病附,調(diào)用 this.props.onChange(子組件不用傳 onChange 方法,自定義組件被 getFieldsValue 包裹亥鬓,會自動獲取 onChage 屬性)完沪,則通過 form.validateFields 可以獲取到值。
/*
* Author: lin.zehong
* Date: 2019-10-04 09:14:52
* Last Modified by: lin.zehong
* Last Modified time: 2019-10-04 09:14:52
* Desc: 對 antd RangePicker 進行封裝
*/
import React from "react";
import moment from "moment";
import { DatePicker } from "antd";
const { RangePicker } = DatePicker;
class RangePickerPage extends React.Component {
range = (start, end) => {
const result = [];
for (let i = start; i < end; i += 1) {
result.push(i);
}
return result;
}
disabledDate = (current) => {
// Can not select days before today and today
return current && current < moment().endOf('day');
}
disabledRangeTime = (_, type) => {
if (type === 'start') {
return {
disabledHours: () => this.range(0, 60).splice(4, 20),
disabledMinutes: () => this.range(30, 60),
disabledSeconds: () => [55, 56],
};
}
return {
disabledHours: () => this.range(0, 60).splice(20, 4),
disabledMinutes: () => this.range(0, 31),
disabledSeconds: () => [55, 56],
};
}
onChange = (dates, dateStrings) => {
const { onChange } = this.props; // V埂@龊浮较剃!
onChange(dateStrings);
}
render() {
return (
<RangePicker
allowClear
disabledDate={this.disabledDate}
disabledTime={this.disabledRangeTime}
showTime={{
hideDisabledOptions: true,
defaultValue: [moment('00:00:00', 'HH:mm:ss'), moment('11:59:59', 'HH:mm:ss')],
}}
format="YYYY-MM-DD HH:mm:ss"
onChange={this.onChange} // 9颈稹<冀 !
/>
);
}
}
export default RangePickerPage;
參考:https://juejin.im/post/5c9c6c08e51d4503e514eaac
23 Form 表單清空和重置的區(qū)別以及方法
這里首先需要明確惰拱,清空和重置是不同的概念雌贱,清空是把內(nèi)容都清空掉,而重置是恢復(fù) form 表單初始值偿短。
例如:新增功能欣孤,清空和重置就是一樣的效果,而對于編輯昔逗,清空就是把初始值都清空掉降传,重置就是恢復(fù)剛開始的初始值。
- 清空
form.setFieldsValue({"fieldName": ""});
- 重置
form.resetFields();
24 DatePicker 組件勾怒,部分日期/時間為可選
24.1 不能選擇今天之前的日期婆排,包括今天的日期也不可以選擇
const disabledDate = (current) => {
return current && current < moment().endOf('day');
}
24.2 不能選擇今天之前的日期,今天日期可以選擇
const disabledDate = (current) => {
return current && current < moment().subtract(1, 'day');
}
24.3 當(dāng)前時間之后的時間點笔链,精確到小時
const [upgradeTime, setUpgradeTime] = useState(moment('00:00:00', 'HH:mm:ss'))
const disabledDate = (current) => {
return current && current < moment().subtract(1, 'day'); // 今天可以選擇
}
const disabledDateTime = () => {
const hours = moment().hours(); // 0~23
// 當(dāng)日只能選擇當(dāng)前時間之后的時間點
if (upgradeTime.date() === moment().date()) {
return {
disabledHours: () => range(0, hours + 1),
};
}
}
<Form.Item label="發(fā)送時間">
{getFieldDecorator('pushTime', {
rules: [{ required: false, message: '請輸入發(fā)送時間' }],
initialValue: record.pushType === 0 ? null :
(record.pushTime ? moment(record.pushTime, 'YYYY-MM-DD HH:mm:ss') : null), // 定時發(fā)送才顯示時間
})(
<DatePicker
format="YYYY-MM-DD HH:mm:ss"
disabledDate={disabledDate}
disabledTime={disabledDateTime}
style={{ width: "100%" }}
onChange={(timer) => setUpgradeTime(timer)} // 6沃弧!鉴扫!
showTime={{ defaultValue: moment(upgradeTime) }} // T拚怼!坪创!
/>,
)}
</Form.Item>
24.4 當(dāng)前時間之后的時間點炕婶,精確到分
const [upgradeTime, setUpgradeTime] = useState(moment('00:00:00', 'HH:mm:ss'))
const disabledDate = (current) => {
return current && current < moment().subtract(1, 'day'); // 今天可以選擇
}
const disabledDateTime = () => {
const hours = moment().hours(); // 0~23
const minutes = moment().minutes(); // 0~59
// 當(dāng)日只能選擇當(dāng)前時間之后的時間點
if (upgradeTime.date() === moment().date()) {
return {
disabledHours: () => range(0, hours),
disabledMinutes: () => range(0, minutes), // 精確到分
};
}
}
<Form.Item label="發(fā)送時間">
{getFieldDecorator('pushTime', {
rules: [{ required: false, message: '請輸入發(fā)送時間' }],
initialValue: record.pushType === 0 ? null :
(record.pushTime ? moment(record.pushTime, 'YYYY-MM-DD HH:mm:ss') : null), // 定時發(fā)送才顯示時間
})(
<DatePicker
format="YYYY-MM-DD HH:mm:ss"
disabledDate={disabledDate}
disabledTime={disabledDateTime}
style={{ width: "100%" }}
onChange={(timer) => setUpgradeTime(timer)} // !@吃ぁ古话!
showTime={{ defaultValue: moment(upgradeTime) }} // !K陪踩!
/>,
)}
</Form.Item>