@formily/core
English | 简体中文
The form state core management package does not rely on any third-party UI frameworks. This package will provide the following features:
- Manage Form status
- Manage Field status
- Manage the Validator status
- Manage dependencies between Form, Field, and Validator
Install
npm install --save @formily/core
Table Of Contents
Background
There are two main scenarios in the middle and back-end field, one is data entry, one is data query + data presentation, whether it is data entry or data query, it is realized by means of form, from the perspective of implementation complexity, the complexity of them is similar, because the data rendering level will inevitably have extremely complex renderings (such as Tree Table, etc.), but the data rendering is easier to reuse and abstract, only the Form requirements will involve a lot of interactive logic. So, as long as we solve the Form problem fundamentally, for the mid- and back-stage scenes, most of the mid- and back-stage scene problems are solved.
Formily is born for this purpose.
Design
Anything comes from Observable Graph.
Core highlights
-
Time travel, with the help of the Observable Graph, can record the full state at any time, can also roll back the state to any time, such abilities will maximize the performance in heavy transaction applications and local debugging scenarios.
-
Efficient update, accurate rendering, no full tree rendering required
-
Built-in immer.js, intelligent degradation, no need to care about browser compatibility
-
More complete life cycle hook
-
More complete verification engine
-
- ValidateFirst verification
- Warning Verification (no blocking submission verification)
- Verification message template engine (a complex verification message solution that does not affect international copy storage)
- The verification rule can be extended, and the regular verification library can be extended.
-
More flexible path parsing, matching, evaluation, value engine
-
- Batch matching data path capability
- Deconstruct evaluation, deconstruct value ability
-
Provides state management capabilities beyond the basic form state model.
Architecture diagram
Terminology explanation
FormPath/FormPathPattern Is an abstract data path form, FormPath is a path class, and FormPathPattern is a path form that can be parsed by FormPath. Cool-path Path parsing matching, ability to evaluate values
The virtual field Is a special Field data structure. The difference between the Field and the Field is that it does not manage values. That is to say, it has no relevance to the value of the Form. Usually we use it, more importantly, it acts as a proxy for the status of a UI container. For example, the layout component FormBlock in Formily exists as an independent node in the whole Form Graph, however, this node type is a VirtualField, but when the final data is submitted, the FormBlock does not pollute the data structure of the submitted data.
Observable Graph Form is a unique Observer Tree. With the help of the observer tree, many forms-related internal linkage logic can be implemented.
Data Path Is the name attribute of Field/VirtualField, which exists as the data path.
Node Path Is the path attribute of Field/VirtualField, which exists as the node path.
For the data path and node path, we can look at the following figure:
If there exists such a tree, then:
- The name attribute of field c is a.c, and the path attribute is a.b.c.
- The name attribute of field B is a.b, and the path attribute is a.b.
- The name attribute of field d is a.d, and the path attribute is a.d.
- The name attribute of field e is a.d.e, and the path attribute is a.d.e.
After this explanation, we roughly understand that as long as VirtualField exists in a node path, its data path will skip VirtualField. However, for VirtualField itself, its name attribute contains its own node identification, which is why the name attribute of field B is a.b.
API
createForm
Create a Form instance
Signature
createForm(options?: IFormCreatorOptions): IForm
Usage
import { createForm } from '@formily/core'
const form = createForm({
values:{},
initialValues:{},
onChange:(values)=>{
console.log(values)
}
})
const aa = form.registerField({
path:"aa"
})
aa.setState(state=>{
state.value = 123
})
console.log(form.getFormState(state=>state.values)) //{aa:123}
registerValidationFormats
Register a regular verification rule set
Signature
registerValidationFormats(formats:{
[formatName in string]: RegExp;
}) : void
Usage
import { createForm,registerValidationFormats } from '@formily/core'
registerValidationFormats({
number: /^[+-]?\d+(\.\d+)?$/
})
const form = createForm({
values:{},
initialValues:{},
onChange:(values)=>{
console.log(values)
}
})
const aa = form.registerField({
path:"aa",
rules:[{
format:"number",
message:'This field is not a number.'
}]
})
aa.setState(state=>{
state.value = 'hello world'
})
form.validate()
console.log(form.getFormState(state=>state.errors))
/**
[{
path: 'aa',
messages: [ 'This field is not a number.' ]
}]
**/
registerValidationRules
The difference between registering a verification rule set and registering formats is that it can register complex verification rules, but the formats are just regular expressions.
Signature
registerValidationRules(
rules:{
[ruleName:string]:(value:any,rule:ValidateDescription)=>boolean
}
) : void
Usage
import { createForm,registerValidationRules } from '@formily/core'
registerValidationRules({
custom: value => {
return value === '123' ? 'This field can not be 123' : ''
}
})
const form = createForm({
values: {},
initialValues: {},
onChange: values => {
console.log(values)
}
})
const aa = form.registerField({
path: 'aa',
rules: [
{
custom: true
}
]
})
aa.setState(state => {
state.value = '123'
})
form.validate()
console.log(form.getFormState(state =>state.errors))
/**
[{
path: 'aa',
messages: ['This field can not be 123']
}]
**/
registerValidationMTEngine
Register a verification message template engine
Signature
registerValidationMTEngine(callback:(message,context)=>any) : void
Usage
import { createForm,registerValidationMTEngine } from '@formily/core'
registerValidationMTEngine((message,context)=>{
return message.replace(/\{\{\s*(\w+)\s*\}\}/g, (_, $0) => {
return FormPath.getIn(context, $0)
})
})
const form = createForm({
values: {},
initialValues: {},
onChange: values => {
console.log(values)
}
})
const aa = form.registerField({
path: 'aa',
rules: [
{
validator(value){
return value === 123 : 'This field can not be 123 {{scope.outerVariable}}'
},
scope:{
outerVariable:'addonAfter'
}
}
]
})
aa.setState(state => {
state.value = '123'
})
form.validate()
console.log(form.getFormState(state =>state.errors))
/**
[{
path: 'aa',
messages: ['This field can not be 123 addonAfter']
}]
**/
setValidationLanguage
Set the international language type
Signature
setValidationLanguage(lang: string): void
Usage
import { setValidationLanguage } from '@formily/core'
setValidationLanguage('en-US')
setValidationLocale
Set a language pack
Signature
interface ILocaleMessages {
[key: string]: string | ILocaleMessages;
}
interface ILocales {
[lang: string]: ILocaleMessages;
}
setValidationLocale(locale: ILocales) => void
Usage
import { setValidationLocale } from '@formily/core'
setValidationLocale({
'en-US':{
required:"This field is required."
}
})
Classes
new FormPath()
The form path engine is responsible for path analysis, matching, evaluation, value, deconstruction evaluation, and deconstruction value.
For more information, see: https://github.com/janrywang/cool-path
new FormLifeCycle()
Create a life cycle listener
Signature
type FormLifeCycleHandler<T> = (payload: T, context: any) => void
new FormLifeCycle(handler: FormLifeCycleHandler<Payload>)
new FormLifeCycle(...type: LifeCycleTypes, handler: FormLifeCycleHandler<Payload>...)
new FormLifeCycle(handlerMap: { [key: LifeCycleTypes]: FormLifeCycleHandler<Payload> })
Usage
import { createForm,FormLifeCycle,LifeCycleTypes } from '@formily/core'
const form = createForm({
lifecycles:[
new FormLifeCycle(({type:LifeCycleTypes,payload:IForm | IField | IVirtualField })=>{
}),
new FormLifeCycle(
LifeCycleTypes.ON_FORM_MOUNT,
(payload:IForm | IField | IVirtualField)=>{
}),
new FormLifeCycle({
[LifeCycleTypes.ON_FORM_MOUNT]:(payload:IForm | IField | IVirtualField)=>{
}
}),
]
})
Enums
LifeCycleTypes
enum LifeCycleTypes {
ON_FORM_WILL_INIT = 'onFormWillInit',
ON_FORM_INIT = 'onFormInit',
ON_FORM_CHANGE = 'onFormChange',
ON_FORM_MOUNT = 'onFormMount',
ON_FORM_UNMOUNT = 'onFormUnmount',
ON_FORM_SUBMIT = 'onFormSubmit',
ON_FORM_RESET = 'onFormReset',
ON_FORM_SUBMIT_START = 'onFormSubmitStart',
ON_FORM_SUBMIT_END = 'onFormSubmitEnd',
ON_FORM_SUBMIT_VALIDATE_START = 'onFormSubmitValidateStart',
ON_FORM_SUBMIT_VALIDATE_SUCCESS= 'onFormSubmitValidateSuccess',
ON_FORM_SUBMIT_VALIDATE_FAILED = 'onFormSubmitValidateFailed',
ON_FORM_VALUES_CHANGE = 'onFormValuesChange',
ON_FORM_INITIAL_VALUES_CHANGE = 'onFormInitialValuesChange',
ON_FORM_VALIDATE_START = 'onFormValidateStart',
ON_FORM_VALIDATE_END = 'onFormValidateEnd',
ON_FORM_INPUT_CHANGE = 'onFormInputChange',
ON_FORM_GRAPH_CHANGE = 'onFormGraphChange',
ON_FIELD_WILL_INIT = 'onFieldWillInit',
ON_FIELD_INIT = 'onFieldInit',
ON_FIELD_CHANGE = 'onFieldChange',
ON_FIELD_INPUT_CHANGE = 'onFieldInputChange',
ON_FIELD_VALUE_CHANGE = 'onFieldValueChange',
ON_FIELD_INITIAL_VALUE_CHANGE = 'onFieldInitialValueChange',
ON_FIELD_MOUNT = 'onFieldMount',
ON_FIELD_UNMOUNT = 'onFieldUnmount',
}
Interfaces
IFormCreatorOptions
CreateForm parameter object protocol
interface IFormCreatorOptions {
initialValues?: {}
values?: {}
lifecycles?: FormLifeCycle[]
editable?: boolean | ((name: string) => boolean)
useDirty?: boolean
validateFirst?: boolean
onChange?: (values: IFormState['values']) => void
onSubmit?: (values: IFormState['values']) => any | Promise<any>
onReset?: () => void
onValidateFailed?: (validated: IFormValidateResult) => void
}
IForm
Form instance object API created by using createForm
interface IForm {
submit(
onSubmit?: (values: IFormState['values']) => any | Promise<any>
): Promise<{
Validated: IFormValidateResult
Payload: any
}>
clearErrors: (pattern?: FormPathPattern) => void
hasChanged(
target: IFormState | IFieldState | IVirtualFieldState,
path: FormPathPattern
): boolean
reset(options?: {
forceClear?: boolean
validate?: boolean
selector?: FormPathPattern
clearInitialValue?: boolean
}): Promise<void | IFormValidateResult>
validate(
path?: FormPathPattern,
options?: {
first?: boolean
}
): Promise<IFormValidateResult>
setFormState(
callback?: (state: IFormState) => any,
silent?: boolean
): void
getFormState(
callback?: (state: IFormState) => any
): any
setFieldState(
path: FormPathPattern,
callback?: (state: IFieldState) => void,
silent?: boolean
): void
getFieldState(
path: FormPathPattern,
callback?: (state: IFieldState) => any
): any
registerField(props: {
path?: FormPathPattern
name?: string
value?: any
values?: any[]
initialValue?: any
visible?: boolean
display?: boolean
props?: any
rules?: ValidatePatternRules[]
required?: boolean
editable?: boolean
useDirty?: boolean
computeState?: (draft: IFieldState, prevState: IFieldState) => void
}): IField
registerVirtualField(props: {
path?: FormPathPattern
name?: string
visible?: boolean
display?: boolean
props?: any
useDirty?: boolean
computeState?: (draft: IFieldState, prevState: IFieldState) => void
}): IVirtualField
createMutators(field: IField): IMutators
getFormGraph(): IFormGraph
setFormGraph(graph: IFormGraph): void
subscribe(
callback?: ({ type, payload }: { type: string; payload: any }) => void
): number
unsubscribe(id: number): void
notify: <T>(type: string, payload?: T) => void
setFieldValue(path?: FormPathPattern, value?: any): void
getFieldValue(path?: FormPathPattern): any
setFieldInitialValue(path?: FormPathPattern, value?: any): void
getFieldInitialValue(path?: FormPathPattern): any
}
IMutators
The instance API created by crewikiutators is mainly used to operate field data.
interface IMutators {
change(...values: any[]): any
focus(): void
blur(): void
validate(): Promise<IFormValidateResult>
exist(index?: number | string): Boolean
push(value?: any): any[]
pop(): any[]
insert(index: number, value: any): any[]
remove(index: number | string): any
unshift(value: any): any[]
shift(): any[]
move($from: number, $to: number): any[]
moveDown(index: number): any[]
moveUp(index: number): any[]
}
Validation
Here we mainly list the intermediate type signatures related to verification.
type CustomValidator = (
value: any,
description?: ValidateDescription
) => ValidateResponse
type SyncValidateResponse =
| null
| string
| boolean
| {
type?: 'error' | 'warning'
message: string
}
type AsyncValidateResponse = Promise<SyncValidateResponse>
type ValidateResponse = SyncValidateResponse | AsyncValidateResponse
interface IFormValidateResult {
errors: Array<{
path: string
messages: string[]
}>
warnings: Array<{
path: string
messages: string[]
}>
}
type InternalFormats =
| 'url'
| 'email'
| 'ipv6'
| 'ipv4'
| 'idcard'
| 'taodomain'
| 'qq'
| 'phone'
| 'money'
| 'zh'
| 'date'
| 'zip'
| string
interface ValidateDescription {
format?: InternalFormats
validator?: CustomValidator
required?: boolean
pattern?: RegExp | string
max?: number;
maximum?: number
exclusiveMaximum?: number
exclusiveMinimum?: number
minimum?: number
min?: number
len?: number
whitespace?: boolean
enum?: any[]
message?: string
[key: string]: any
}
IFormState
Form the core state
interface IFormState<FormProps = any> {
pristine: boolean
valid: boolean
invalid: boolean
validating: boolean
submitting: boolean
errors: string[]
warnings: string[]
loading: boolean
initialized: boolean
editable: boolean | ((name: string) => boolean)
values: {}
initialValues: {}
mounted: boolean
unmounted: boolean
props: FormProps
}
IFieldState
CORE Field status
interface IFieldState<FieldProps = any> {
displayName?: string
name: string
path: string
initialized: boolean
pristine: boolean
valid: boolean
invalid: boolean
validating: boolean
modified: boolean
touched: boolean
active: boolean
visited: boolean
visible: boolean
display: boolean
editable: boolean
loading: boolean
values: any[]
errors: string[]
warnings: string[]
value: any
initialValue: any
rules: ValidatePatternRules[]
required: boolean
mounted: boolean
unmounted: boolean
props: FieldProps
}
IVirtualFieldState
Virtual Field core status
interface IVirtualFieldState<FieldProps = any> {
displayName: string
name: string
path: string
initialized: boolean
visible: boolean
display: boolean
mounted: boolean
unmounted: boolean
props: FieldProps
}
IField/IVirtualField
The instance API created by using registerField/registerVirtualField
interface IField/IVirtualField {
batch: (callback?: () => void) => void
getState: (callback?: (state: IFieldState) => any) => any
setState: (
callback?: (state: IFieldState | Draft<IFieldState>) => void,
silent?: boolean
) => void
getSourceState: (callback?: (state: IFieldState) => any) => any
setSourceState: (callback?: (state: IFieldState) => void) => void
hasChanged: (key?: string) => boolean
isDirty: (key?: string) => boolean
getDirtyInfo: () => StateDirtyMap<IFieldState>
}