天天看點

Fusion Design之Form元件源碼分析1From介紹Form元件的api

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

繼續閱讀