formilyjs的实践总结

1,653 阅读3分钟

什么是formilyjs?

Formily 是一个用于构建高性能、可扩展的表单解决方案的 JavaScript 库。虽然 Formily 提供了许多有用的功能,但在使用过程中可能会遇到一些常见的痛点。以下是一些可能的问题:

  1. 学习曲线:Formily 具有自己的一套概念和 API,因此使用者需要花时间学习和理解其工作原理。这可能对于初次接触 Formily 的开发者来说是一个挑战。
  2. 配置复杂性:Formily 提供了丰富的配置选项和功能,以满足各种表单需求。然而,这也可能导致配置变得复杂,特别是对于较为复杂的表单。开发者需要仔细研究文档和示例,以正确配置和使用 Formily。
  3. 缺乏文档和示例:尽管 Formily 的文档已经在不断完善,但仍然可能存在一些缺失或不完整的部分。有时候可能会遇到一些缺乏示例或详细说明的情况。文档也比较分散,不是很好查找。

如何使用$self.value

例1:生成选项的uuid,保证ArrayCards中每一个数组的唯一值

{  
    "uuid": {  
        "x-component""Input",  
        "x-decorator""FormItem",  
        "x-hidden"true,  
        "x-reactions": {  
            "fulfill": {  
                "state": {  
                    "value""{{ $self.value ? $self.value : uuid()}}"  
                }  
            },  
            "dependencies": [  
                {  
                    "property""value",  
                    "type""any"  
                }  
            ]  
        },  
        "title""选项UUID",  
        "type""string"  
    }  
}

在schema中,self.value很有用,当uuid已经有值的时候,不会重新生成新的uuid。

例2:根据Input中的数量自动生成多少个选项

Object.assign(correctAnswerExample, {
          'x-reactions': [
            {
              dependencies: [inputDataSource.toString()],
              fulfill: {
                schema: {
                  default:
                    "{{(function(){  const answerNumber = $deps[0] || 0;  const values = new Array(answerNumber).fill(''); const oldValues = $self.value;const valueList = values.map((item, index) => {  if(oldValues?.[index]) {   return oldValues[index]; } return { id: String(index) };  });  return valueList; })()}}"
                }
              }
            }
          ]
        });
  • inputDataSource为某个元素的name
  • default 里面支持一个自执行函数的字符串,如answerNumber为3,生成3个选项,当answerNumber改为4,生成4个选项。根据$self.value,当存在的值返回存在的数据,当不存在的值,返回仅有id属性的对象。
  • default的自执行函数
{{(function(){  
    const answerNumber = $deps[0] || 0;  
    const values = new Array(answerNumber).fill('');
    const oldValues = $self.value;
    const valueList = values.map((item, index) => {  
        if(oldValues?.[index]) {   return oldValues[index]; } 
        return { id: String(index) };  
    });  
    return valueList;
})()
}}

例3: 根据数量生成选项的下拉框,例如:选项1,选项2,选项3。当数量修改为2,可以过滤选中的下拉框值。

{
  "type": "string",
  "x-decorator": "FormItem",
  "title": "对应问题空格",
  "x-component": "Select",
  "x-component-props": {
    "mode": "multiple"
  },
  "x-reactions": {
    "dependencies": [
      {
        "property": "value",
        "type": "any",
        "source": "questionInfo.answerArea.imageAnswerAreaContent",
        "name": "answers"
      },
      {
        "property": "value",
        "type": "any",
        "source": "determineInfo.type",
        "name": "type"
      }
    ],
    "fulfill": {
      "state": {
        "display": "{{(function(){  return $form?.values?.determineInfo.type === 1 ? 'visible' : 'hidden'; })()}}",
        "componentProps": {
          "options": "{{ (function(){ const options = []; const imageAnswerAreaContent = $form?.values?.questionInfo?.answerArea?.imageAnswerAreaContent; for (let i = 0; i < imageAnswerAreaContent?.length; i++) { const { id } = imageAnswerAreaContent[i]; options.push({ label: '选项' + (i + 1), value: id }); } options.push({ label: '干扰项', value: -1 }); return options; })() }}"
        },
        "value": "{{ (function(){ const ids = $deps.answers?.map(it => it.id) || []; const values = $self.value?.filter(id => [...ids, -1]?.includes(id)); return values; })() }}"
      }
    }
  }
}
  • options的生成
{{ (function(){ 
    const options = []; const imageAnswerAreaContent = $form?.values?.questionInfo?.answerArea?.imageAnswerAreaContent;
    for (let i = 0; i < imageAnswerAreaContent?.length; i++) { 
        const { id } = imageAnswerAreaContent[i]; 
        options.push({ label: '选项' + (i + 1), value: id }); } 
        options.push({ label: '干扰项', value: -1 }); 
    return options; })()
}}
  • value数据的过滤
{{ (function(){ 
    const ids = $deps.answers?.map(it => it.id) || []; 
    const values = $self.value?.filter(id => [...ids, -1]?.includes(id)); 
    return values; })() 
}}

setValues设置不生效?

我们项目由于是动态获取的表单Schema,如果我们在获取到表单Schema之前调用createForm初始化表单实例,会导致setValues设置表单数据不生效。解决方案:

获取到表单schema之后,设置:

formRef.current = createForm({
   validateFirst: true
});

表单

{formRef.current && (
  <Form
    labelCol={3}
    wrapperCol={16}
    form={formRef.current}
    previewTextPlaceholder='-'
    size='large'
    layout='horizontal'>
    <SchemaField schema={normalSchema} />
    <Affix offsetBottom={30}>
      <div style={{ display: 'flex', justifyContent: 'right', marginTop: 20 }}>
        <Button
          style={{ marginRight: 20 }}
          取消
        </Button>
        <Button type='primary' onClick={onOk} loading={loading.save}>
          保存
        </Button>
      </div>
    </Affix>
  </Form>
)}

设置数据方法,就是必须先存着formRef.current,再渲染表单,不然设置表单值会失败。感觉是formily的一个bug。

formRef.current.setValues(res.data);

表单无数据情况下,默认显示:N/A,用previewTextPlaceholder可以重新自定义默认值。

根据radio隐藏显示字段,如富文本显示富文本相关字段,图片显示图片相关字段。

{
    "properties": {
        "answerAreaType": {
            "type": "number",
            "title": "类型",
            "x-decorator": "FormItem",
            "x-component": "Radio.Group",
            "default": 0,
            "enum": [
                {
                    "label": "富文本",
                    "value": 0
                },
                {
                    "label": "图片",
                    "value": 1
                }
            ]
        },
        "richType": {
            "type": "void",
            "title": "富文本类型",
            "x-reactions": {
                "dependencies": [
                    {
                        "property": "value",
                        "type": "any",
                        "source": ".answerAreaType",
                        "name": "type"
                    }
                ],
                "fulfill": {
                    "state": {
                        "display": "{{(function(){ return $deps.type === 0 ? 'visible' : 'hidden'; })()}}"
                    }
                }
            },
            "properties": {}
        },
        "imageType": {
            "type": "void",
            "title": "图片类型",
            "rule": {},
            "x-reactions": {
                "dependencies": [
                    {
                        "property": "value",
                        "type": "any",
                        "source": ".answerAreaType",
                        "name": "type"
                    }
                ],
                "fulfill": {
                    "state": {
                        "display": "{{(function(){ return $deps.type === 1 ? 'visible' : 'hidden'; })()}}"
                    }
                }
            },
            "properties": {}
        }
    }
}

依赖的字段路径

dependencies的路径,可以写全路径,也就是表单提交数据的结构,如:questionInfo.answerArea.imageAnswerAreaContent,找到imageAnswerAreaContent字段

如果字段唯一,也可以*.answerAreaType用*模糊匹配

相对路径

相对路径语法主要是在数据型路径头部用点语法表示,对于计算数组的相邻元素非常好用,它主要有几个特点:

  • 一个点代表当前路径
  • 每个层级用两个点,..向上寻找。

x-index,可以用作属性的排序。但x-index不能重复,否则重复的属性,只会显示一个

如何给某个地方添加提示信息,给Typography组件设置属性children,style等就可以完成添加提示信息。

image.png

{
    "info": {
        "type": "void",
        "x-component": "Space",
        "x-index": 3,
        "properties": {
            "spaceTitle": {
                "x-component": "Typography.Text",
                "x-component-props": {
                    "children": "说明:",
                    "style": {
                        "color": "#8a8f8d"
                    }
                }
            },
            "spaceText1": {
                "x-component": "Typography.Text",
                "x-component-props": {
                    "children": "1、通用数学逻辑 支持数学符号 xxxxxxx",
                    "style": {
                        "color": "#8a8f8d"
                    }
                }
            }
        },
        "title": "Space",
        "x-component-props": {
            "title": "Space",
            "direction": "vertical",
            "style": {
                "marginBottom": "10px"
            }
        }
    }
}

参考文献