@beisen-platform/platform-form

Form ======

Usage no npm install needed!

<script type="module">
  import beisenPlatformPlatformForm from 'https://cdn.skypack.dev/@beisen-platform/platform-form';
</script>

README

Form

1. 介绍

注意!所有的回调方法都是用对象解构方式传参数的

就是这样 ⬇️

handleChange({fieldName,fieldBizData,...}){
  // Your code is here.
}

该组件从平台Form组件中抽出,内置了如下平台逻辑:

  • 表单联动
  • 字段依赖(根据字段的值刷新某一字段的数据源) - 表单验证
    • 同步验证
    • 异步验证

2. 可扩展的逻辑

2.1 表单元数据加载

2.1.1 直接提供表单元数据

当直接给overrideFormMetaData传递表单元数据时,表单将跳过网络请求,直接使用提供的元数据。

不提供这个属性时,表单将按照popOption中提供的数据加载数据。

2.1.2 自定义表单元数据请求地址

实现getFormViewRequestURL方法,表单将会使用该URL请求表单元数据。

getFormViewRequestURL(){
  return url
}

<Form {...otherProps} getFormViewRequestURL={getFormViewRequestURL}/>

2.1.3 使用自定义参数加载

请求表单时至少要有这四个基础的参数app, metaObjName, viewName, formState,这四个参数。当formState需要传入id

表单请求时会带上additionParamsWhenLoad里面的参数,保存时会带上additionParamsWhenSave里面的参数。

const formViewParams = {
  // id: '',
  app: 'BeisenCloudStepAAA',
  metaObjName: 'BeisenCloudStepAAA.liandongzhuanyong',
  viewName: 'BeisenCloudStepAAA.shareddemoform2',
  formState: 'create',
  additionParamsWhenLoad: {
    param1:'param1content',
    param2:'param2content'
  },
  additionParamsWhenSave: {
    param1:'param1content',
    param2:'param2content'
  },
  additionHeadersWhenSave:{
    header1:'header1content',
    header2:'header2content'
  },
  httpMethodWhenSave: 'POST'
  // customSaveURL: YOUR_API
}

<Form {...otherProps} formViewParams={formViewParams}/>

2.1.4 过滤(修改)表单元数据

当表单元数据从网络加载后,如果存在属性formMetaDataFilter,会执行该方法,并将返回的元数据作为展示表单的元数据。

formMetaDataFilter({formMetaData}){
  // do some change
  return formMetaData
}

<Form {...otherProps} formMetaDataFilter={formMetaDataFilter} />

2.1.5 修改表单参数重新加载表单

formViewParams中除additionParamsWhenSave以外任何一部分发生变化时,表单会重新加载。

可以通过实现shouldReloadFormData方法重写判断是否重新加载表单逻辑。

2.2 过滤数据源

当表单数据源加载后,会调用dataSourceFilter方法,该方法会传入每个DataSource项(fieldNamekeydataSourceResults),用returndataSourceResults作为新的数据源。

2.3 替换组件

替换组件的优先级如下:

当渲染该字段时,会首先根据字段元数据的cmp_data.field_name(复合表单为cmp_data.field_name_form_multi)从extendedFieldMapByFieldName中查询。

如果查询不到该组件,会首先根据字段元数据的cmp_typeextendedFieldMap中查询。

如果查询不到该组件,则会从平台标准组件的FieldMap中查询。

如果仍然查询不到,则会默认使用单行文本代替。

2.3.1 根据字段名替换组件

props为字段的渲染前的propsOriginalComponent

const overrideFields = {
  a1: (props, OriginComponent) => {
    return (
      <div>
        <OriginComponent {...props} />
        <span>自定义自定义</span>
      </div>
    );
  },
  extdanxuansetting_100013_62398125: ()=> {
    return <Hello />
  }
}

<Form {...otherProps} extendedFieldMapByFieldName={overrideFields}/>

2.3.2 根据字段类型替换组件

支持传入自定义字段组件。传入字段组件需要遵照以下格式:

const overrideFields = {
  FIELD_TYPE1: (props, OriginComponent) => {
    return (
      <div>
        <OriginComponent {...props} />
        <span>自定义自定义</span>
      </div>
    );
  },
  FIELD_TYPE2: ()=> {
    return <Hello />
  }
}

<Form {...otherProps} extendedFieldMap={overrideFields}/>

2.4 表单联动

此处将值改变时处罚联动逻辑的字段成为控制字段

2.4.1 设定监听字段

需要将所有的控制字段都通过extendedFormLinkageFields这个props传入。

这样做的目的是,当触发联动时,可以使用专门的onFormLinkage方法处理联动逻辑,减小onChange方法的体积。

设想一下,如果联动逻辑依赖于onChange方法,那么在onChange内做联动逻辑必定会使得onChange方法非常大。尤其是涉及到链式联动逻辑时。

将联动逻辑和onChange分开,可以得到更好的代码结构。

2.4.2 实现回调方法

实现接口方法onFormLinkage,该方法会传入:

  • fieldName 发生值改变的控制字段的字段名
  • fieldBizData 控制字段的值
  • formBizData 当前表单(所有字段)的值
  • fieldBizDataBeforeChange 控制字段的值改变之前的值
  • formLinkageFunctions 表单联动操作方法集,包含的方法在2.4.3介绍
  • commitFormLinkage 提交表单联动操作

当进行表单联动操作后,必须调用commitFormLinkage提交联动操作。这样做的原因是考虑到有一些联动逻辑是异步进行的,因此联动方法也应该是异步的。

请务必确保联动提交。否则,未使用commitFormLinkage提交的联动不会生效,还会遗留在联动操作队列中,产生奇怪的问题。

这里要强调,每调用一次commitFormLinkage,都会立刻进行联动逻辑。需要考虑数据同步(例如进行设定字段值后进行了commitFormLinkage,但一个异步请求还没返回)和性能问题。建议有异步操作的在异步回调中使用commitFormLinkage

实际上,每个表单联动方法做的事情,在表单联动操作队列中添加一条联动记录(也可以认为是一条消息),当调用commitFormLinkage时,执行联动操作队列中所有的联动逻辑。

对于设定setFieldErrorremoveFieldError这两个显示和移除字段错误信息的联动方法,由于没有传入验证器,错误信息的显示和隐藏需要手动维护。表单进行验证(且失败)时,回调方法onValidateFailed的参数formErrors会包含联动设置的错误。

2.4.3 表单联动操作方法

method params description
hideField fieldName 使字段fieldName隐藏
showField fieldName 使字段fieldName显示
presenceField fieldName 使字段fieldName必填
nonPresenceField fieldName 使字段fieldName非必填
readOnlyField fieldName 使字段fieldName只读
clearBizData fieldName 清空字段fieldName的值
setBizData fieldName, bizData 设置字段fieldName的值
setBizDataGroup bizDataMap, fieldNamesToInvokeNextLinkageOn 同时设置多个数据,如果有下一级联动,触发指定字段的联动
updateDataSource fieldName, dataSourceResults 更新字段fieldName的数据源
hideFormPart formPartIndex 隐藏第formPartIndex个表单区块(从0开始)
showFormPart formPartIndex 显示第formPartIndex个表单区块(从0开始)
setFieldError fieldName, errorMessage 使字段fieldName显示错误errorMessage
removeFieldError fieldName 移除字段fieldName上的错误信息(必须是由setFieldError设定的错误信息)

2.4.4 代码风格

**请尽量使用对象解构的方式接收参数,尽量使用switch-case来区分fieldName和fieldBizData.value的值,减少if-else的使用。**否则当逻辑较多时,过多的if-else会让代码变得非常乱。

2.5 表单验证

2.5.1 传入自定义验证器

自定义验证器function格式为validate.js的自定义验证器格式

const extendedValidators = {
  validatorName1: validateFunction1,
  validatorName2: validateFunction2,
  validatorName3: validateFunction3,
}

<Form {...otherProps} extendedValidators={extendedValidators} />

2.5.2 自定义字段的验证器

传入extendedFieldValidators属性可以单独定义每个字段的验证器。替换组件的组件只能使用这种方式定义验证器

extendedFieldValidators中,每个function的参数是表单元数据带的验证器,需要返回最终决定的该字段的验证器。

const extendedFieldValidators = {
  fieldName1: (originalValidators) => {
    return Object.assign({},validators,{presence: {message: "^必填"}})
  },
  fieldName2: () => {
    return Object.assign({presence: {message: "^必填"}})
  }
}

<Form {...otherProps} extendedFieldValidators={extendedFieldValidators}/>

2.5.3 重写验证逻辑

实现overrideValidate方法。

该方法的参数是表单数据formBizData和表单元数据formMetaData

该方法需要返回一个Promise,值为validate.js的错误类型,当验证通过时,应返回可判断为false的值(建议返回null)。

2.5.4 在onChange中手动设定错误信息

当onChange调用时,functions参数中有两个方法:setError(errorMessage)removeError(),可以通过调用这两个方法给字段设置错误信息。 但需要注意,和联动中的设置字段错误一样,需要手动清除字段错误信息才能通过表单验证(实际上内部实现是一样的,只是绑定了fieldName)。

2.6 表单提交

2.6.1 重写表单提交逻辑

实现overrideSubmit方法。参数是表单的数据bizData

2.6.2 自定义提交附带参数

2.1.3

2.6.3 表单提交前

表单验证通过后,开始提交前会回调preSubmit方法,该方法需要返回一个Promise。当返回resolve时,表单会继续进行提交动作,返回reject时,会中断提交动作。

这是一个例子,通过做一个前置请求,决定是否提交数据,当这个前置请求返回错误时,弹出错误提示

export default class PreSubmitExample extends Component {

  handlePreSubmit = ({ formBizData, functions }) => {
    const _417 = 'https://api.simgenius.cn/simrest/ObjectData/BSDevHelper/OperationResult/4615096d-6752-495f-a32c-ceac05e5beea';
    const _200 = 'https://api.simgenius.cn/simrest/ObjectData/BSDevHelper/OperationResult/6ea7ac57-14da-41bb-b59e-b60903c6db33';
    return new Promise((resolve,reject)=>{
      fetch(_417)
        .then(r => r.json())
        .then((operationResult) => {
          if(operationResult.code !== 200){
            this.formMethods.showTip({title:operationResult.message, infoType:'error', content:[]})
            reject(operationResult)
          }else {
            return functions.showConfirm(operationResult.message)
          }
        }).then(()=>{console.log('confirm'); resolve();},()=>{console.log('cancel');reject()})
    })
  };

  render() {
    const popOptions = {
      formSaveLabel: '想要一个长长的名字,很长很长的,长长长长长长长长的',
      cmpContext: {
        app: 'BeisenCloudDemo',
        'currentViewName': 'BeisenCloudDemo_Sshitu',
        'metaObjName': 'BeisenCloudDemo.123'
      },
      formState: 'create',
    };
    return (
      <FormWithFooter
        formMethods={f=>this.formMethods = f}
        overrideFormMetaData={require('../BugFix__NOT_EXAMPLE/metadata6')}
        BSGlobal={BSGlobal}
        popOptions={popOptions}
        preSubmit={this.handlePreSubmit}
      />
    );
  }
}

2.7 捕获内部事件

内部事件产生时机见下图的生命周期。

FormEvent

实现onImplicitEvent方法,参数为eventNameparams。目前params没有参数传进来,将来传递参数仍然会通过对象解构方式传递到params

3. API

3.1 值类型props

name type default description
overrideFormMetaData MetaData none 当使用这个属性时,表单会直接使用提供的元数据,不再通过网络请求获取表单的元数据。
formLinkageFields Array<string> none 扩展联动逻辑中,控制字段的字段名
extendedFieldMap Dictionary none 传入的自定义组件列表
extendedFieldMapByFieldName Dictionary none 传入的自定义组件列表
defaultBizData BizData none 表单的初始值
formViewParams Object none 表单的参数,其中: app, metaObjName, viewName, formState必填,编辑时需要填表单数据的id,additionParamsWhenLoad为加载时的额外参数,additionParamsWhenSave为提交时的额外参数
extendedValidators Dictionary none 自定义验证器
extendedFieldValidators Dictionary none 自定义字段的验证器
loadingIsFixedPosition boolean false 表单加载的loading是否是fixed定位

3.1 function类型props

name params returns description
onChange fieldName, fieldBizData, formBizData, fieldBizDataBeforeChange, functions none 表单数据改变时的回调
onFormLinkage fieldName, fieldBizData, formBizData, fieldBizDataBeforeChange, formLinkageFunctions, commitFormLinkage undefined 表单联动回调
dataSourceFilter fieldName, key, dataSourceResults dataSourceResults 数据源过滤器
formMetaDataFilter formMetaData formMetaData 过滤(修改)表单元数据
overrideValidate formBizData, formMetaData Promise<form_errors> 自定义表单验证过程
onValidateFailed formErrors none 表单验证失败时的回调
overrideSubmit bizData none 自定义表单提交过程
preSubmit formBizData, dataToSubmit, functions Promise 表单验证通过后,开始提交前的回调
onSubmitted response none 表单提交成功后的回调
onSubmitFailed Promise.reject<response> none 表单提交失败时的回调
onImplicitEvent eventName, params none 隐式事件回调
shouldReloadFormData prevProps, currentProps, formBizData, formErrors boolean 当formViewParams发生变化时,是否重新加载表单

3.3 methods

name params returns description
showTip tipOption none 弹出Tip
submit none none 表单提交
validate none Promise 表单验证
getBizData none BizData 获取表单数据