Claude
Skills
Sign in
Back

Advanced Formily Patterns

Included with Lifetime
$97 forever

This skill should be used when the user asks to "implement dynamic forms", "formily complex forms", "formily conditional fields", "formily nested forms", "formily async validation", "formily performance optimization", or "advanced formily patterns".

General

What this skill does


# Advanced Formily Patterns

This skill provides advanced guidance for implementing complex form scenarios with Formily. Focus on dynamic forms, conditional fields, nested forms, async validation, performance optimization, and sophisticated form patterns in TypeScript.

## Dynamic Forms

### Conditional Field Visibility

```typescript
import { createForm, onFieldChange } from '@formily/core'

const form = createForm({
  effects() {
    // Show/hide fields based on other field values
    onFieldChange('userType', ['value'], (field) => {
      const userType = field.value

      form.setFieldState('companyName', state => {
        state.visible = userType === 'business'
        if (userType !== 'business') {
          state.value = ''
        }
      })

      form.setFieldState('studentId', state => {
        state.visible = userType === 'student'
        if (userType !== 'student') {
          state.value = ''
        }
      })
    })
  }
})
```

### Dynamic Field Options

```typescript
const form = createForm({
  effects() {
    onFieldChange('country', ['value'], (field) => {
      const country = field.value

      // Update city options based on selected country
      const cityOptions = country === 'us'
        ? [
            { label: 'New York', value: 'nyc' },
            { label: 'Los Angeles', value: 'la' },
            { label: 'Chicago', value: 'chi' }
          ]
        : country === 'uk'
        ? [
            { label: 'London', value: 'lon' },
            { label: 'Manchester', value: 'man' },
            { label: 'Birmingham', value: 'bir' }
          ]
        : []

      form.setFieldState('city', state => {
        state.dataSource = cityOptions
        state.value = cityOptions.length > 0 ? cityOptions[0].value : ''
      })
    })
  }
})
```

### Dynamic Form Schema

```typescript
import { createForm, Form } from '@formily/core'

interface DynamicFormConfig {
  fields: Array<{
    name: string
    type: 'string' | 'number' | 'boolean' | 'array' | 'object'
    label: string
    required?: boolean
    options?: Array<{ label: string; value: any }>
    validation?: any[]
  }>
}

const createDynamicForm = (config: DynamicFormConfig) => {
  const schema = {
    type: 'object',
    properties: config.fields.reduce((acc, field) => {
      acc[field.name] = {
        type: field.type,
        title: field.label,
        required: field.required,
        'x-validator': field.validation
      }

      if (field.type === 'boolean') {
        acc[field.name]['x-component'] = 'Checkbox'
      } else if (field.options) {
        acc[field.name]['x-component'] = 'Select'
        acc[field.name]['x-component-props'] = {
          options: field.options
        }
      } else {
        acc[field.name]['x-component'] = 'Input'
      }

      return acc
    }, {} as any)
  }

  return createForm({ schema })
}
```

## Array Field Patterns

### Dynamic Array Items

```typescript
import { createForm, isArrayField } from '@formily/core'
import { ArrayField } from '@formily/react'

const form = createForm({
  initialValues: {
    contacts: [
      { name: '', phone: '', relationship: '' }
    ]
  }
})

// Add new contact item
const addContact = () => {
  form.pushValues('contacts', {
    name: '',
    phone: '',
    relationship: ''
  })
}

// Remove contact item
const removeContact = (index: number) => {
  form.removeValues(`contacts.${index}`)
}

// Reorder contacts
const moveContact = (fromIndex: number, toIndex: number) => {
  const field = form.query('contacts')
  if (isArrayField(field)) {
    field.move(fromIndex, toIndex)
  }
}
```

### Nested Array Objects

```typescript
interface Contact {
  name: string
  emails: Array<{
    address: string
    type: 'work' | 'personal' | 'other'
  }>
}

const NestedArrayForm = () => {
  const form = createForm<Contact>({
    initialValues: {
      name: '',
      emails: []
    }
  })

  const addEmail = () => {
    form.pushValues('emails', {
      address: '',
      type: 'work'
    })
  }

  return (
    <FormProvider form={form}>
      <ArrayField name="emails">
        {(field) => (
          <div>
            {field.value?.map((email, index) => (
              <div key={index}>
                <Field name={`emails.${index}.address`}>
                  {/** Email input component */}
                </Field>
                <Field name={`emails.${index}.type`}>
                  {/** Email type select component */}
                </Field>
              </div>
            ))}
            <button onClick={addEmail}>Add Email</button>
          </div>
        )}
      </ArrayField>
    </FormProvider>
  )
}
```

## Async Validation Patterns

### Server-Side Validation

```typescript
import { createForm } from '@formily/core'

const form = createForm({
  effects() {
    // Async email uniqueness check
    onFieldChange('email', ['value'], async (field) => {
      if (!field.value) return

      field.validating = true

      try {
        const isUnique = await checkEmailUniqueness(field.value)

        if (!isUnique) {
          field.errors = ['Email already exists']
        } else {
          field.errors = []
        }
      } catch (error) {
        field.errors = ['Validation failed. Please try again.']
      } finally {
        field.validating = false
      }
    })

    // Debounced validation for performance
    onFieldChange('username', ['value'],
      debounce(async (field) => {
        if (!field.value || field.value.length < 3) return

        field.validating = true

        try {
          const exists = await checkUsernameExists(field.value)
          field.errors = exists ? ['Username already taken'] : []
        } catch (error) {
          field.errors = ['Unable to validate username']
        } finally {
          field.validating = false
        }
      }, 500)
    )
  }
})

// Helper function for debouncing
const debounce = <T extends (...args: any[]) => any>(
  func: T,
  wait: number
) => {
  let timeout: NodeJS.Timeout
  return (...args: Parameters<T>) => {
    clearTimeout(timeout)
    timeout = setTimeout(() => func(...args), wait)
  }
}
```

### Complex Validation Rules

```typescript
import { createForm, onFieldReact } from '@formily/core'

const form = createForm({
  effects() {
    // Password strength validation
    onFieldReact('password', (field) => {
      const password = field.value || ''
      const errors = []

      if (password.length < 8) {
        errors.push('Password must be at least 8 characters')
      }

      if (!/[A-Z]/.test(password)) {
        errors.push('Password must contain uppercase letter')
      }

      if (!/[a-z]/.test(password)) {
        errors.push('Password must contain lowercase letter')
      }

      if (!/\d/.test(password)) {
        errors.push('Password must contain number')
      }

      if (!/[!@#$%^&*]/.test(password)) {
        errors.push('Password must contain special character')
      }

      field.errors = errors
    })

    // Confirm password validation
    onFieldReact('confirmPassword', (field) => {
      const password = form.values.password
      const confirmPassword = field.value

      if (confirmPassword && password !== confirmPassword) {
        field.errors = ['Passwords do not match']
      } else {
        field.errors = []
      }
    })

    // Date range validation
    onFieldReact('endDate', (field) => {
      const startDate = form.values.startDate
      const endDate = field.value

      if (startDate && endDate && new Date(endDate) <= new Date(startDate)) {
        field.errors = ['End date must be after start date']
      } else {
        field.errors = []
      }
    })
  }
})
```

## Nested Form Patterns

### Multi-Step Forms

```typescript
import { createForm } from '@formily/core'
import { useState } from 'react'

interface MultiStepFormData {
  personal: {
    firstName: string
    lastName: string
    email: string
  }
  professional: {
    company: string
    position: string
    experienc

Related in General