From介紹
請大家先導讀Form元件使用和Field元件使用
- 首先先明白一點,這是為了解決什麼問題?
- 簡化表單的操作,優美的布局,提供方法的api
- 如何進行資料存儲的
- 通過Filed元件,統一對資料進行管理,可以自己傳,也可以不用傳,内部已經定義好了.
- 在進行資料管理的時候,通過onChange方法與Filed資料管理之間進行資料傳遞
Form元件的api
inline - 内聯表單
const formClassName = classNames({
[`${prefix}form`]: true,
[`${prefix}inline`]: inline
});
return (
<Tag
className={formClassName}>
123
</Tag>
size - 元件的大小
通過context或props傳遞給子元件
//元件的樣式設定
// 擷取父元件傳遞過來的size大小
getSize() {
return this.props.size || this.context._formSize;
}
/**
* Item的包裹
*/
getItemWrapper() {
const {
prefix,
children
} = this.props;
const childrenProps = {size: this.getSize() };
let childrenNode = children;
const ele = React.Children.map(childrenNode, child=> {
return React.cloneElement(child, {...childrenProps}); // 屬性進行傳遞,例如Input元件會把props映射到自己的屬性上,實作大小的變化
})
return (
<div className={`${prefix}form-item-control`}>
{ ele }
</div>
);
}
label群組件樣式設定;
const itemClassName = classNames({
[`${prefix}form-item`]: true,
[`${prefix}${size}`]: !!size,
[`${prefix}${labelAlign}`]: labelAlign,
});
// console.log( `${prefix}${labelAlign}` );
// 垂直模式并且左對齊才用到
const Tag = 'div';
const label = this.getItemLabel();
return (
<Tag
{...obj.pickOthers(Item.propTypes, this.props)}
className={itemClassName}
>
{ label }
{this.getItemWrapper()}
</Tag>
這面代碼的css樣式解讀
&.#{$css-prefix}large { // next-form.next-large
margin-bottom: $form-item-l-margin-b; //20px;
#{$form-prefix}-item-label { //設定行高
line-height:$form-element-large-height; //$s-10
}
#{$form-prefix}-item-label { // 設定字型的大小
font-size: $form-element-large-font-size;
}
}
// 對于size="small"的字型的設定
&.#{$css-prefix}small {
margin-bottom: $form-item-s-margin-b; //
#{$form-prefix}-item-label { //設定行高
line-height:$form-element-small-height; //
}
#{$form-prefix}-item-label { // 設定字型的大小
font-size: $form-element-small-font-size;
}
}
labelAlign - 标簽的位置
手動傳遞了 wrapCol labelCol 會使用 Grid 輔助布局; labelAlign=‘top’ 會強制禁用 Grid,而使用div布局
當我top時
const Tag = 'div';
// 當為inset的時候,需要顯示在元件内
const label = labelAlign === 'inset'? null: this.getItemLabel();
// 選擇div情況下,預設就是上下布局
當為left時
使用Grid布局,為左右的形式,是以不用做處理
當為inset時
// 這個主要是使用,元件自己特性,進行内嵌
getItemLabel() {
const {
id,
label,
prefix,
labelAlign,
wrapperCol,
labelCol
} = this.props;
// labelAlign為inset和left的包裹
if((wrapperCol || labelCol)&& labelAlign !== 'top') {
return (<Col {...labelCol} className={cls}>
{ele}
</Col>);
}
}
/**
* 狀态的判斷
*/
getState() {
}
/**
* Item的包裹
*/
getItemWrapper() {
const {
prefix,
children,
labelAlign,
labelCol,
wrapperCol,
} = this.props;
const childrenProps = {size: this.getSize() };
let childrenNode = children;
if( labelAlign === 'inset' ) {
childrenProps.label = this.getItemLabel();
}
const ele = React.Children.map(childrenNode, child=> { //這裡進行注入到元件的内部
return React.cloneElement(child, {...childrenProps});
})
//當labelAlign為inset和left的樣式
if((wrapperCol || labelCol) && labelCol !== 'top') {
return (
<Col
{...wrapperCol}
className={`${prefix}form-item-control`}
key="item"
>
{ele}
</Col>
);
}
}
labelTextAlign 标簽的左右對齊方式
當值為left時
// 在Item元件那種設定
const cls = classNames({
[`${prefix}form-item-label`]: true,
[`${prefix}left`]: labelTextAlign === 'left' //這裡
})
// labelAlign為inset和left的包裹
if((wrapperCol || labelCol)&& labelAlign !== 'top') {
return (<Col {...labelCol} className={cls}>
{ele}
</Col>);
}
對應的
css樣式
// 包裹label的css樣式的設定
&-item-label {
display: inline-block; // 可設寬高,不自動換行
vertical-align: top;
color: $form-label-color; // #666666
text-align: right;
padding-right: $form-label-padding-r; // 12px;
// 設定屬性 labelTextAlign =="left"的樣式
&.#{$css-prefix}left {
text-align: left;
}
}
當值為right時
預設就是右對齊,因為在
next-form-item-label
設定
text-align: right;
field
- 經 new Field(this) 初始化後,直接傳給 Form 即可 用到表單校驗則不可忽略此項
// 當使用者傳遞props屬性時
if(props.field) {
this._formField = props.field;
const onChange = this._formField.onChange;
//Field内部的onchange方法 和 onChange 進行綁定在一起
options.onChange = func.makeChain(onChange, this.onChange);
//設定參數
this._formField.setOptions && this._formField.setOptions(options);
} else {
if('value' in props) {
// 傳遞表單的數值
options.values = props.value;
}
this._formField = new Field(this, options);
}
saveField
- 儲存 Form 自動生成的 field 對象
props.saveField(this._formField);
// 使用者使用saveFile方法的時候,會傳回給使用者封裝後的field
labelCol
- 控制第一級 Item 的 labelCol
- 意思就是列占用的寬度,對應Grid的
wrapperCol
- 控制第一級 Item 的 wrapperCol
- 控制目前元件占用的寬度
onSubmit
- 若使用者不傳此方法,則預設會阻止預設事件
//阻止預設事件
function preventDefault(e) {
e.preventDefault();
static defaultProps = {
prefix: 'next-',
component: 'form',
onSubmit: preventDefault,
};
class Form{
render() {
const {
...
onSubmit,
} = this.props;
...
return (
<Tag
className={formClassName}
onSubmit={onSubmit}
>
....
</Tag>
)
}
}
children
- 就是代表子元素
value
- 我了解的是通過Field對表單元素初始化
onChange
- 這個方法厲害了,這個方法由Field統一管理,就是所有元件的change都會到達這裡[setValue不會觸發該函數],666
constructor() {
const options = {
...props.fieldOptions,
// 通過onchange去管理資料
onChange: this.onChange,
};
this._formField = new Field(this, options);
}
onChange = (name, value) => {
// console.log(123);
this.props.onChange(this._formField.getValues(), {
name,
value,
field: this._formField,
});
};
在Item.jsx中
/**
* Item的包裹
*/
// React.Children.map 去把方法傳遞給子元件 666
// 當數值改變時,會調用元件中的onChange方法,然後通過this.props.onChange去回調到Field的方法,然後是使用者自定義的onChange方法
getItemWrapper() {
const {
prefix,
children,
labelAlign,
labelCol,
wrapperCol,
} = this.props;
const childrenProps = {size: this.getSize() };
let childrenNode = children;
// console.log('---');
// console.log(typeof children);
// 擷取變量的資料
if (typeof children === 'function' && this.context._formField) {
childrenNode = this.context._formField.getValues();
}
const ele = React.Children.map(childrenNode, child=> {
if(child && typeof child.type === 'function' &&
child.type._formField !== 'form_item') {
let extraProps = childrenProps;
if( this.context._formField && 'name' in child.props ) {
// extraProps = this.context._formField.init(this.props.name, {
// });
console.log( child.props );
extraProps = this.context._formField.init(child.props.name,{
props: child.props //child.props 代表子元件對象
}, childrenProps);// childrenProps 需要修飾的對象
}else {
extraProps = Object.assign({}, child.props, extraProps);
}
return React.cloneElement(child, extraProps); // 統一當做props傳遞給子元件
}
return child;
})
component
- 設定标簽的類型預設為form