CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-vee-validate

Painless forms for Vue.js with comprehensive validation, composition API, and component-based approaches.

Pending
Overview
Eval results
Files

field-management.mddocs/

Field Management

Field-level composables for individual field validation, state management, value binding, and dynamic field arrays. These composables provide fine-grained control over individual form fields and collections of fields.

Capabilities

useField Composable

Creates a managed field with validation, state tracking, and reactive value binding.

/**
 * Creates a managed field with validation and state tracking
 * @param path - Field path/name, can be reactive
 * @param rules - Optional validation rules for the field
 * @param options - Optional field configuration
 * @returns Field context with value, meta, errors, and methods
 */
function useField<TValue = unknown>(
  path: MaybeRefOrGetter<string>,
  rules?: MaybeRef<RuleExpression<TValue>>,
  options?: Partial<FieldOptions<TValue>>
): FieldContext<TValue>;

interface FieldOptions<TValue> {
  initialValue?: MaybeRefOrGetter<TValue>;
  validateOnMount?: boolean;
  validateOnValueUpdate?: boolean;
  validateOnBlur?: boolean;
  bails?: boolean;
  type?: string;
  label?: MaybeRefOrGetter<string | undefined>;
  standalone?: boolean;
  keepValueOnUnmount?: MaybeRefOrGetter<boolean | undefined>;
  syncVModel?: boolean;
  checkedValue?: MaybeRefOrGetter<TValue>;
  uncheckedValue?: MaybeRefOrGetter<TValue>;
}

interface FieldContext<TValue = unknown> {
  // Field state
  value: Ref<TValue>;                          // Reactive field value
  meta: FieldMeta<TValue>;                     // Field metadata
  errors: Ref<string[]>;                       // Field errors array
  errorMessage: Ref<string | undefined>;      // First error message
  
  // Field methods
  resetField(state?: Partial<FieldState<TValue>>): void;
  handleReset(): void;
  validate(opts?: Partial<ValidationOptions>): Promise<ValidationResult<TValue>>;
  handleChange(e: Event | unknown, shouldValidate?: boolean): void;
  handleBlur(e?: Event, shouldValidate?: boolean): void;
  setState(state: Partial<FieldState<TValue>>): void;
  setTouched(isTouched: boolean): void;
  setErrors(message: string | string[]): void;
  setValue(value: TValue, shouldValidate?: boolean): void;
}

type RuleExpression<TValue> = 
  | string 
  | Record<string, unknown> 
  | GenericValidateFunction<TValue> 
  | GenericValidateFunction<TValue>[]
  | TypedSchema<TValue>;

Usage Examples:

import { useField } from "vee-validate";

// Basic field with validation
const { value: email, errorMessage, meta } = useField('email', (value) => {
  if (!value) return 'Email is required';
  if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value)) return 'Email is invalid';
  return true;
});

// Field with multiple validation rules
const { value: password, errors, validate } = useField('password', [
  (value) => value ? true : 'Password is required',
  (value) => value.length >= 8 || 'Password must be at least 8 characters',
  (value) => /[A-Z]/.test(value) || 'Password must contain uppercase letter',
  (value) => /[0-9]/.test(value) || 'Password must contain a number'
]);

// Field with custom configuration
const { value: username, meta, setValue, resetField } = useField('username', 
  (value) => value.length >= 3 || 'Username too short',
  {
    initialValue: '',
    validateOnMount: true,
    label: 'Username',
    bails: false // Continue validation even after first error
  }
);

// Checkbox field
const { value: isSubscribed, handleChange } = useField('newsletter', undefined, {
  type: 'checkbox',
  checkedValue: true,
  uncheckedValue: false,
  initialValue: false
});

// Reactive field path
const fieldPath = ref('user.email');
const { value, errorMessage } = useField(fieldPath, 'required|email');

// Programmatic field manipulation
const updateField = () => {
  setValue('new-value');
  setTouched(true);
};

const validateField = async () => {
  const result = await validate();
  if (!result.valid) {
    console.log('Field errors:', result.errors);
  }
};

// Reset field to initial state
const resetToDefault = () => {
  resetField({
    value: '',
    errors: [],
    touched: false
  });
};

useFieldArray Composable

Manages dynamic arrays of form fields with manipulation methods for adding, removing, and reordering items.

/**
 * Manages dynamic arrays of form fields
 * @param arrayPath - Path to the array field, can be reactive
 * @returns Field array context with fields and manipulation methods
 */
function useFieldArray<TValue = unknown>(
  arrayPath: MaybeRefOrGetter<string>
): FieldArrayContext<TValue>;

interface FieldArrayContext<TValue = unknown> {
  fields: Ref<FieldEntry<TValue>[]>;                    // Reactive array of field entries
  remove(idx: number): void;                            // Remove item at index
  replace(newArray: TValue[]): void;                    // Replace entire array
  update(idx: number, value: TValue): void;             // Update item at index
  push(value: TValue): void;                            // Add item to end
  swap(indexA: number, indexB: number): void;           // Swap two items
  insert(idx: number, value: TValue): void;             // Insert item at index
  prepend(value: TValue): void;                         // Add item to beginning
  move(oldIdx: number, newIdx: number): void;           // Move item to new position
}

interface FieldEntry<TValue = unknown> {
  value: TValue;                                        // Entry value
  key: string | number;                                 // Unique key for tracking
  isFirst: boolean;                                     // True if first entry
  isLast: boolean;                                      // True if last entry
}

Usage Examples:

import { useFieldArray, useField } from "vee-validate";

// Basic field array for a list of strings
const { fields, push, remove } = useFieldArray<string>('tags');

// Add new tag
const addTag = () => {
  push('');
};

// Remove tag by index
const removeTag = (index: number) => {
  remove(index);
};

// Complex field array for objects
interface User {
  name: string;
  email: string;
  age: number;
}

const { 
  fields: userFields, 
  push: addUser, 
  remove: removeUser,
  swap: swapUsers,
  move: moveUser
} = useFieldArray<User>('users');

// Add new user
const addNewUser = () => {
  addUser({
    name: '',
    email: '',
    age: 0
  });
};

// Remove user by index
const removeUserAt = (index: number) => {
  removeUser(index);
};

// Swap two users
const swapUserPositions = (indexA: number, indexB: number) => {
  swapUsers(indexA, indexB);
};

// Move user to new position
const moveUserToPosition = (fromIndex: number, toIndex: number) => {
  moveUser(fromIndex, toIndex);
};

// Render field array in template
// Template usage with individual field validation
const renderUserFields = () => {
  return userFields.value.map((entry, index) => {
    // Create individual fields for each user property
    const { value: name, errorMessage: nameError } = useField(
      `users[${index}].name`, 
      'required'
    );
    const { value: email, errorMessage: emailError } = useField(
      `users[${index}].email`, 
      'required|email'
    );
    const { value: age, errorMessage: ageError } = useField(
      `users[${index}].age`, 
      (value) => value >= 18 || 'Must be at least 18'
    );

    return {
      key: entry.key,
      index,
      fields: {
        name: { value: name, error: nameError },
        email: { value: email, error: emailError },
        age: { value: age, error: ageError }
      },
      isFirst: entry.isFirst,
      isLast: entry.isLast
    };
  });
};

// Bulk operations
const replaceAllUsers = (newUsers: User[]) => {
  replace(newUsers);
};

const updateUser = (index: number, updatedUser: User) => {
  update(index, updatedUser);
};

// Insert user at specific position
const insertUserAt = (index: number, user: User) => {
  insert(index, user);
};

// Add user to beginning
const prependUser = (user: User) => {
  prepend(user);
};

Field State Management

FieldMeta Interface

Provides metadata about an individual field's validation and interaction state.

interface FieldMeta<TValue> {
  touched: boolean;        // True if field has been interacted with
  dirty: boolean;          // True if value differs from initial value
  valid: boolean;          // True if field passes validation
  validated: boolean;      // True if field has been validated at least once
  required: boolean;       // True if field is required by validation rules
  pending: boolean;        // True if validation is in progress
  initialValue?: TValue;   // Original field value
}

FieldState Interface

Complete field state structure for reset and setState operations.

interface FieldState<TValue = unknown> {
  value: TValue;           // Current field value
  touched: boolean;        // Field interaction state
  errors: string[];        // Field error messages
}

State Management Examples:

import { useField } from "vee-validate";

const { value, meta, setState, setTouched, setErrors, setValue } = useField('username');

// Check field state
const isFieldReady = computed(() => {
  return meta.validated && meta.valid && !meta.pending;
});

const fieldStatus = computed(() => {
  if (meta.pending) return 'validating';
  if (meta.errors.length > 0) return 'error';
  if (meta.valid && meta.touched) return 'success';
  return 'default';
});

// Programmatic state updates
const markFieldAsTouched = () => {
  setTouched(true);
};

const setCustomError = () => {
  setErrors(['This username is already taken']);
};

const updateFieldValue = (newValue: string) => {
  setValue(newValue, true); // true = trigger validation
};

const resetFieldState = () => {
  setState({
    value: '',
    touched: false,
    errors: []
  });
};

Advanced Field Patterns

Conditional Field Validation

Dynamic validation rules based on form state or other conditions.

import { useField, useFormContext } from "vee-validate";

const form = useFormContext();

// Conditional validation based on other field values
const { value: confirmPassword, errorMessage } = useField(
  'confirmPassword',
  computed(() => {
    return (value) => {
      if (!value) return 'Please confirm password';
      const password = form.values.password;
      if (value !== password) return 'Passwords do not match';
      return true;
    };
  })
);

// Dynamic validation rules based on field type
const fieldType = ref('email');
const { value: inputValue, errorMessage: inputError } = useField(
  'userInput',
  computed(() => {
    if (fieldType.value === 'email') {
      return [(value) => !!value || 'Email required', (value) => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value) || 'Invalid email'];
    } else if (fieldType.value === 'phone') {
      return [(value) => !!value || 'Phone required', (value) => /^\d{10}$/.test(value) || 'Invalid phone'];
    }
    return (value) => !!value || 'This field is required';
  })
);

Cross-Field Validation

Validation that depends on multiple field values.

import { useField, useFormContext } from "vee-validate";

const form = useFormContext();

// Price range validation
const { value: minPrice } = useField('minPrice', (value) => {
  if (!value) return 'Minimum price is required';
  const maxPrice = form.values.maxPrice;
  if (maxPrice && value >= maxPrice) return 'Minimum must be less than maximum';
  return true;
});

const { value: maxPrice } = useField('maxPrice', (value) => {
  if (!value) return 'Maximum price is required';
  const minPrice = form.values.minPrice;
  if (minPrice && value <= minPrice) return 'Maximum must be greater than minimum';
  return true;
});

// Date range validation
const { value: startDate } = useField('startDate', (value) => {
  if (!value) return 'Start date is required';
  const endDate = form.values.endDate;
  if (endDate && new Date(value) >= new Date(endDate)) {
    return 'Start date must be before end date';
  }
  return true;
});

const { value: endDate } = useField('endDate', (value) => {
  if (!value) return 'End date is required';
  const startDate = form.values.startDate;
  if (startDate && new Date(value) <= new Date(startDate)) {
    return 'End date must be after start date';
  }
  return true;
});

Install with Tessl CLI

npx tessl i tessl/npm-vee-validate

docs

configuration-rules.md

core-validation.md

field-management.md

form-actions.md

form-management.md

index.md

state-access.md

vue-components.md

tile.json