- 前端
- UI组件
【免费下载链接】formily
📱🚀 🧩 Cross Device & High Performance Normal Form/Dynamic(JSON Schema) Form/Form Builder -- Support React/React Native/Vue 2/Vue 3
NumberPicker 是 Formily 基于 Fusion Design(@alifd/next)数字输入框封装的高阶表单组件,用于数字类型的录入、步进调节与范围约束。本文以 @formily/next 包中的 NumberPicker 组件为核心,完整演示 Markup Schema、JSON Schema、Pure JSX 三种接入方式的实战代码,并深入 connect 源码揭示其字段状态映射(size/status)、只读预览(readPretty)与 FormLayout 尺寸联动的底层机制,帮助你在 Formily 表单体系中正确、高效地使用数字输入组件。
一、组件定位与适用场景
NumberPicker 在 Formily 中承担"数字输入框"的角色,位于@formily/next包内。它并非从零实现的组件,而是对阿里 Fusion Design 的NumberPicker(@alifd/next)进行的协议化封装:通过@formily/react的connect高阶能力,把表单字段的校验状态、尺寸、只读态等自动映射到 Fusion 数字输入框上。
它适合以下典型场景:
- 数量、金额、年龄、价格等纯数字字段录入;
- 需要步进器(上/下箭头)微调数值;
- 需要限定最小值、最大值、整数位/小数位精度;
- 在详情展示场景下以只读预览形式回显数字。
从源码看,NumberPicker 的完整实现在 number-picker/index.tsx,核心只有几行:
import { connect, mapProps, mapReadPretty } from '@formily/react' import { NumberPicker as InputNumber } from '@alifd/next' import { PreviewText } from '../preview-text' import { mapSize, mapStatus } from '../__builtins__' export const NumberPicker = connect( InputNumber, mapProps(mapSize, mapStatus), mapReadPretty(PreviewText.NumberPicker) ) export default NumberPicker可以看到:底层输入框来自@alifd/next,接入字段后由mapProps(mapSize, mapStatus)自动注入尺寸与状态,由mapReadPretty(PreviewText.NumberPicker)接管只读预览渲染。组件样式通过 style.ts 引入@alifd/next/lib/number-picker/style,并通过@formily/next的 src/index.ts 统一对外导出。
二、三种接入方式完整示例
Formily 支持三种形态书写同一个表单,下面三个示例完全等价,均实现一个宽度 240 的必填数字输入框,并配套提交按钮。
2.1 Markup Schema 方式(JSX 语法糖)
Markup Schema 用 JSX 标签直接描述 Schema,可读性最好、最接近普通 JSX 开发习惯:
import React from 'react' import { NumberPicker, FormItem, FormButtonGroup, Submit } from '@formily/next' import { createForm } from '@formily/core' import { FormProvider, createSchemaField } from '@formily/react' const SchemaField = createSchemaField({ components: { NumberPicker, FormItem, }, }) const form = createForm() export default () => ( <FormProvider form={form}> <SchemaField> <SchemaField.String name="input" title="input box" x-decorator="FormItem" x-component="NumberPicker" required x-component-props={{ style: { width: 240, }, }} /> </SchemaField> <FormButtonGroup> <Submit onSubmit={console.log}>Submit</Submit> </FormButtonGroup> </FormProvider> )要点说明:
createSchemaField注册的组件表中同时包含NumberPicker与FormItem,x-decorator负责布局/校验信息展示,x-component负责实际输入控件;required声明必填,配合FormItem会在未填写时展示校验错误;x-component-props中的 props 会透传给 NumberPicker 底层组件(此处传入style.width)。
2.2 JSON Schema 方式(纯数据驱动)
JSON Schema 将描述与 UI 彻底解耦,适合由后端下发或动态配置的场景,写法与 Markup Schema 一一对应:
import React from 'react' import { NumberPicker, FormItem, FormButtonGroup, Submit } from '@formily/next' import { createForm } from '@formily/core' import { FormProvider, createSchemaField } from '@formily/react' const SchemaField = createSchemaField({ components: { NumberPicker, FormItem, }, }) const form = createForm() const schema = { type: 'object', properties: { input: { type: 'string', title: 'input box', 'x-decorator': 'FormItem', 'x-component': 'NumberPicker', 'x-component-props': { style: { width: 240, }, }, }, }, } export default () => ( <FormProvider form={form}> <SchemaField schema={schema} /> <FormButtonGroup> <Submit onSubmit={console.log}>Submit</Submit> </FormButtonGroup> </FormProvider> )与 Markup Schema 的对应关系:<SchemaField.String name="input">等价于properties.input;x-decorator、x-component、x-component-props三个字段名在 JSON 中改为字符串键。注意此处 Schema 的type: 'string'仅描述字段类型,NumberPicker 实际输入/输出的仍是数字值。
2.3 Pure JSX 方式(不使用 Schema)
如果不使用 Schema 体系,可以直接用@formily/react的Field组件命令式声明:
import React from 'react' import { NumberPicker, FormItem, FormButtonGroup, Submit } from '@formily/next' import { createForm } from '@formily/core' import { FormProvider, Field } from '@formily/react' const form = createForm() export default () => ( <FormProvider form={form}> <Field name="input" title="input box" required decorator={[FormItem]} component={[ NumberPicker, { style: { width: 240, }, }, ]} /> <FormButtonGroup> <Submit onSubmit={console.log}>Submit</Submit> </FormButtonGroup> </FormProvider> )三种方式的取舍建议:
| 方式 | 特点 | 适合场景 |
|---|---|---|
| Markup Schema | JSX 即 Schema,类型提示友好 | 前端直接书写表单的常规开发 |
| JSON Schema | 纯数据描述,可序列化/动态下发 | 低代码、配置化、服务端驱动表单 |
| Pure JSX | 跳过 Schema 解析,直连 Field | 追求最小依赖、复杂联动逻辑 |
无论哪种方式,<Submit onSubmit={console.log}>都会在提交时把表单值(含input字段)打印到控制台,可直接观察 NumberPicker 的取值结果。
三、源码机制:connect 如何让 Fusion 输入框接入 Formily 字段
理解了用法后,有必要看一下connect的底层机制,这能解释"为什么写一个 NumberPicker 标签就能自动拿到校验状态、尺寸和只读能力"。
connect定义在 packages/react/src/shared/connect.ts,其核心实现为:把传入的mapProps、mapReadPretty等映射器(mapper)依次作用于目标组件,最终包装成一个React.forwardRef组件。其中两个关键映射器:
1.mapProps(mapSize, mapStatus)—— 状态与尺寸自动注入
mapSize(packages/next/src/builtins/mapSize.ts):读取FormLayout上下文中的size配置。若布局未指定 size,则使用FormLayout的默认值(源码中'default'会被归一化为 Fusion 的'medium')。这意味着只要在表单外层配置了FormLayout size,所有 NumberPicker 都会自动跟随该尺寸,无需逐个设置。mapStatus(packages/next/src/builtins/mapStatus.ts):根据字段状态推导 Fusion 输入框的state属性——字段loading/validating时映射为loading,存在selfErrors时映射为error,存在selfWarnings时映射为warning,否则回退到decoratorProps.feedbackStatus。这就是"校验失败时输入框自动变红"的原理。
2.mapReadPretty(PreviewText.NumberPicker)—— 只读预览
mapReadPretty逻辑同样在 connect.ts 中:当字段pattern === 'readPretty'(只读模式)时,不再渲染 Fusion 输入框,而是渲染PreviewText.NumberPicker。该组件定义在 packages/next/src/preview-text/index.tsx,实现为:
const NumberPicker: React.FC<React.PropsWithChildren<NumberPickerProps>> = ( props ) => { return <NextNumberPicker {...props} isPreview /> }它复用了 Fusion NumberPicker 的isPreview能力,将数字值以纯文本形式展示。这意味着当表单进入详情/查看模式(如form.setPattern('readPretty'))时,NumberPicker 会自动从可输入态切换为纯文本回显,无需任何额外代码。
四、常用配置参数(API)
NumberPicker 的完整 API 对齐 Fusion Design 的数字输入框,可通过x-component-props(Schema 方式)或组件 props(JSX 方式)传入。以下为最常用的配置项:
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
value | number | - | 当前值(由 Formily 字段自动注入,一般无需手动传) |
defaultValue | number | - | 非受控模式下的初始值 |
onChange | (value: number) => void | - | 值变化回调(Formily 会自动接管该事件写入字段) |
min | number | - | 允许的最小值 |
max | number | - | 允许的最大值 |
step | number | 1 | 步长,点击步进器时每次增减的值 |
precision | number | - | 小数精度(保留的小数位数) |
size | 'large' \| 'medium' \| 'small' | 跟随 FormLayout | 输入框尺寸 |
disabled | boolean | false | 禁用输入 |
style | CSSProperties | - | 内联样式(如宽度) |
典型配置示例(限制 0~100、步长 5、保留整数):
<SchemaField.String name="score" title="评分" x-component="NumberPicker" required x-component-props={{ min: 0, max: 100, step: 5, precision: 0, style: { width: 200 }, }} />注意:size参数通常无需手动指定——如源码所示,NumberPicker 会从FormLayout上下文自动继承,手动传入的size优先级更高(props.size || takeSize())。同样的,state(loading/error/warning)也由mapStatus根据字段校验结果自动推导。
五、与 FormItem 的协作
在 Formily 体系中 NumberPicker 必须搭配FormItem(作为x-decorator)才能获得完整的表单能力:
- 校验反馈:
required、validator等校验结果通过mapStatus映射为 Fusion 输入框的error/warning状态,同时FormItem负责渲染错误文案; - 布局控制:
FormItem负责 label、帮助文案、错误信息的排版,style.width等样式配置作用于输入框本身; - 只读切换:
FormItem与mapReadPretty协同,在readPretty模式下将整个字段切换为纯文本展示。
因此,三处示例中的components: { NumberPicker, FormItem }注册缺一不可——FormItem由x-decorator="FormItem"引用,NumberPicker由x-component="NumberPicker"引用。
六、环境与依赖前提
使用@formily/next的 NumberPicker 需要满足以下前提(依据 packages/next/package.json):
- peerDependencies:
@alifd/next(^1.19.0)、react/react-dom/react-is(>=16.8.0,Hook 版本要求); - 依赖包:
@formily/core、@formily/react、@formily/reactive、@formily/json-schema等均为同版本 2.3.7; - 样式:组件样式通过 style.ts 引入
@alifd/next/lib/number-picker/style,构建时由create-style/build-style脚本处理。
安装后即可按上文三种方式使用;若只需展示数字而不希望用户编辑,可配合FormLayout的只读模式或直接使用PreviewText组件。
七、小结
NumberPicker 是 Formily Next 表单体系中"麻雀虽小、五脏俱全"的典型组件:使用层面,它支持 Markup Schema、JSON Schema、Pure JSX 三种等价写法;机制层面,connect+mapProps(mapSize, mapStatus)+mapReadPretty(PreviewText.NumberPicker)的组合让它在零配置下自动获得尺寸跟随、校验状态反馈与只读预览能力。掌握这一个组件,即可举一反三地理解@formily/next中其余所有 Schema 组件的封装模式。
- 前端
- UI组件
【免费下载链接】formily
📱🚀 🧩 Cross Device & High Performance Normal Form/Dynamic(JSON Schema) Form/Form Builder -- Support React/React Native/Vue 2/Vue 3
相关推荐
Formily 组件实战:@formily/antd NumberPicker 数字输入框的三种 Schema 用法与源码原理
Formily 组件实战:@formily/antd NumberPicker 数字输入框的三种 Schema 用法与源码原理 本文以 Formily 官方文档
前端UI组件Formily 数字输入框(@formily/antd NumberPicker)完全指南:三种 Schema 用法与源码原理解析
Formily 数字输入框(@formily/antd NumberPicker)完全指南:三种 Schema 用法与源码原理解析 数字输入框(NumberPi
前端UI组件Formily Next 密码输入框(Password)组件实战:checkStrength 密码强度校验与三种 Schema 用法
Formily Next 密码输入框(Password)组件实战:checkStrength 密码强度校验与三种 Schema 用法 本文基于 @formily
前端UI组件
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考