0
# Field Management
1
2
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.
3
4
## Capabilities
5
6
### useField Composable
7
8
Creates a managed field with validation, state tracking, and reactive value binding.
9
10
```typescript { .api }
11
/**
12
* Creates a managed field with validation and state tracking
13
* @param path - Field path/name, can be reactive
14
* @param rules - Optional validation rules for the field
15
* @param options - Optional field configuration
16
* @returns Field context with value, meta, errors, and methods
17
*/
18
function useField<TValue = unknown>(
19
path: MaybeRefOrGetter<string>,
20
rules?: MaybeRef<RuleExpression<TValue>>,
21
options?: Partial<FieldOptions<TValue>>
22
): FieldContext<TValue>;
23
24
interface FieldOptions<TValue> {
25
initialValue?: MaybeRefOrGetter<TValue>;
26
validateOnMount?: boolean;
27
validateOnValueUpdate?: boolean;
28
validateOnBlur?: boolean;
29
bails?: boolean;
30
type?: string;
31
label?: MaybeRefOrGetter<string | undefined>;
32
standalone?: boolean;
33
keepValueOnUnmount?: MaybeRefOrGetter<boolean | undefined>;
34
syncVModel?: boolean;
35
checkedValue?: MaybeRefOrGetter<TValue>;
36
uncheckedValue?: MaybeRefOrGetter<TValue>;
37
}
38
39
interface FieldContext<TValue = unknown> {
40
// Field state
41
value: Ref<TValue>; // Reactive field value
42
meta: FieldMeta<TValue>; // Field metadata
43
errors: Ref<string[]>; // Field errors array
44
errorMessage: Ref<string | undefined>; // First error message
45
46
// Field methods
47
resetField(state?: Partial<FieldState<TValue>>): void;
48
handleReset(): void;
49
validate(opts?: Partial<ValidationOptions>): Promise<ValidationResult<TValue>>;
50
handleChange(e: Event | unknown, shouldValidate?: boolean): void;
51
handleBlur(e?: Event, shouldValidate?: boolean): void;
52
setState(state: Partial<FieldState<TValue>>): void;
53
setTouched(isTouched: boolean): void;
54
setErrors(message: string | string[]): void;
55
setValue(value: TValue, shouldValidate?: boolean): void;
56
}
57
58
type RuleExpression<TValue> =
59
| string
60
| Record<string, unknown>
61
| GenericValidateFunction<TValue>
62
| GenericValidateFunction<TValue>[]
63
| TypedSchema<TValue>;
64
```
65
66
**Usage Examples:**
67
68
```typescript
69
import { useField } from "vee-validate";
70
71
// Basic field with validation
72
const { value: email, errorMessage, meta } = useField('email', (value) => {
73
if (!value) return 'Email is required';
74
if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value)) return 'Email is invalid';
75
return true;
76
});
77
78
// Field with multiple validation rules
79
const { value: password, errors, validate } = useField('password', [
80
(value) => value ? true : 'Password is required',
81
(value) => value.length >= 8 || 'Password must be at least 8 characters',
82
(value) => /[A-Z]/.test(value) || 'Password must contain uppercase letter',
83
(value) => /[0-9]/.test(value) || 'Password must contain a number'
84
]);
85
86
// Field with custom configuration
87
const { value: username, meta, setValue, resetField } = useField('username',
88
(value) => value.length >= 3 || 'Username too short',
89
{
90
initialValue: '',
91
validateOnMount: true,
92
label: 'Username',
93
bails: false // Continue validation even after first error
94
}
95
);
96
97
// Checkbox field
98
const { value: isSubscribed, handleChange } = useField('newsletter', undefined, {
99
type: 'checkbox',
100
checkedValue: true,
101
uncheckedValue: false,
102
initialValue: false
103
});
104
105
// Reactive field path
106
const fieldPath = ref('user.email');
107
const { value, errorMessage } = useField(fieldPath, 'required|email');
108
109
// Programmatic field manipulation
110
const updateField = () => {
111
setValue('new-value');
112
setTouched(true);
113
};
114
115
const validateField = async () => {
116
const result = await validate();
117
if (!result.valid) {
118
console.log('Field errors:', result.errors);
119
}
120
};
121
122
// Reset field to initial state
123
const resetToDefault = () => {
124
resetField({
125
value: '',
126
errors: [],
127
touched: false
128
});
129
};
130
```
131
132
### useFieldArray Composable
133
134
Manages dynamic arrays of form fields with manipulation methods for adding, removing, and reordering items.
135
136
```typescript { .api }
137
/**
138
* Manages dynamic arrays of form fields
139
* @param arrayPath - Path to the array field, can be reactive
140
* @returns Field array context with fields and manipulation methods
141
*/
142
function useFieldArray<TValue = unknown>(
143
arrayPath: MaybeRefOrGetter<string>
144
): FieldArrayContext<TValue>;
145
146
interface FieldArrayContext<TValue = unknown> {
147
fields: Ref<FieldEntry<TValue>[]>; // Reactive array of field entries
148
remove(idx: number): void; // Remove item at index
149
replace(newArray: TValue[]): void; // Replace entire array
150
update(idx: number, value: TValue): void; // Update item at index
151
push(value: TValue): void; // Add item to end
152
swap(indexA: number, indexB: number): void; // Swap two items
153
insert(idx: number, value: TValue): void; // Insert item at index
154
prepend(value: TValue): void; // Add item to beginning
155
move(oldIdx: number, newIdx: number): void; // Move item to new position
156
}
157
158
interface FieldEntry<TValue = unknown> {
159
value: TValue; // Entry value
160
key: string | number; // Unique key for tracking
161
isFirst: boolean; // True if first entry
162
isLast: boolean; // True if last entry
163
}
164
```
165
166
**Usage Examples:**
167
168
```typescript
169
import { useFieldArray, useField } from "vee-validate";
170
171
// Basic field array for a list of strings
172
const { fields, push, remove } = useFieldArray<string>('tags');
173
174
// Add new tag
175
const addTag = () => {
176
push('');
177
};
178
179
// Remove tag by index
180
const removeTag = (index: number) => {
181
remove(index);
182
};
183
184
// Complex field array for objects
185
interface User {
186
name: string;
187
email: string;
188
age: number;
189
}
190
191
const {
192
fields: userFields,
193
push: addUser,
194
remove: removeUser,
195
swap: swapUsers,
196
move: moveUser
197
} = useFieldArray<User>('users');
198
199
// Add new user
200
const addNewUser = () => {
201
addUser({
202
name: '',
203
email: '',
204
age: 0
205
});
206
};
207
208
// Remove user by index
209
const removeUserAt = (index: number) => {
210
removeUser(index);
211
};
212
213
// Swap two users
214
const swapUserPositions = (indexA: number, indexB: number) => {
215
swapUsers(indexA, indexB);
216
};
217
218
// Move user to new position
219
const moveUserToPosition = (fromIndex: number, toIndex: number) => {
220
moveUser(fromIndex, toIndex);
221
};
222
223
// Render field array in template
224
// Template usage with individual field validation
225
const renderUserFields = () => {
226
return userFields.value.map((entry, index) => {
227
// Create individual fields for each user property
228
const { value: name, errorMessage: nameError } = useField(
229
`users[${index}].name`,
230
'required'
231
);
232
const { value: email, errorMessage: emailError } = useField(
233
`users[${index}].email`,
234
'required|email'
235
);
236
const { value: age, errorMessage: ageError } = useField(
237
`users[${index}].age`,
238
(value) => value >= 18 || 'Must be at least 18'
239
);
240
241
return {
242
key: entry.key,
243
index,
244
fields: {
245
name: { value: name, error: nameError },
246
email: { value: email, error: emailError },
247
age: { value: age, error: ageError }
248
},
249
isFirst: entry.isFirst,
250
isLast: entry.isLast
251
};
252
});
253
};
254
255
// Bulk operations
256
const replaceAllUsers = (newUsers: User[]) => {
257
replace(newUsers);
258
};
259
260
const updateUser = (index: number, updatedUser: User) => {
261
update(index, updatedUser);
262
};
263
264
// Insert user at specific position
265
const insertUserAt = (index: number, user: User) => {
266
insert(index, user);
267
};
268
269
// Add user to beginning
270
const prependUser = (user: User) => {
271
prepend(user);
272
};
273
```
274
275
## Field State Management
276
277
### FieldMeta Interface
278
279
Provides metadata about an individual field's validation and interaction state.
280
281
```typescript { .api }
282
interface FieldMeta<TValue> {
283
touched: boolean; // True if field has been interacted with
284
dirty: boolean; // True if value differs from initial value
285
valid: boolean; // True if field passes validation
286
validated: boolean; // True if field has been validated at least once
287
required: boolean; // True if field is required by validation rules
288
pending: boolean; // True if validation is in progress
289
initialValue?: TValue; // Original field value
290
}
291
```
292
293
### FieldState Interface
294
295
Complete field state structure for reset and setState operations.
296
297
```typescript { .api }
298
interface FieldState<TValue = unknown> {
299
value: TValue; // Current field value
300
touched: boolean; // Field interaction state
301
errors: string[]; // Field error messages
302
}
303
```
304
305
**State Management Examples:**
306
307
```typescript
308
import { useField } from "vee-validate";
309
310
const { value, meta, setState, setTouched, setErrors, setValue } = useField('username');
311
312
// Check field state
313
const isFieldReady = computed(() => {
314
return meta.validated && meta.valid && !meta.pending;
315
});
316
317
const fieldStatus = computed(() => {
318
if (meta.pending) return 'validating';
319
if (meta.errors.length > 0) return 'error';
320
if (meta.valid && meta.touched) return 'success';
321
return 'default';
322
});
323
324
// Programmatic state updates
325
const markFieldAsTouched = () => {
326
setTouched(true);
327
};
328
329
const setCustomError = () => {
330
setErrors(['This username is already taken']);
331
};
332
333
const updateFieldValue = (newValue: string) => {
334
setValue(newValue, true); // true = trigger validation
335
};
336
337
const resetFieldState = () => {
338
setState({
339
value: '',
340
touched: false,
341
errors: []
342
});
343
};
344
```
345
346
## Advanced Field Patterns
347
348
### Conditional Field Validation
349
350
Dynamic validation rules based on form state or other conditions.
351
352
```typescript
353
import { useField, useFormContext } from "vee-validate";
354
355
const form = useFormContext();
356
357
// Conditional validation based on other field values
358
const { value: confirmPassword, errorMessage } = useField(
359
'confirmPassword',
360
computed(() => {
361
return (value) => {
362
if (!value) return 'Please confirm password';
363
const password = form.values.password;
364
if (value !== password) return 'Passwords do not match';
365
return true;
366
};
367
})
368
);
369
370
// Dynamic validation rules based on field type
371
const fieldType = ref('email');
372
const { value: inputValue, errorMessage: inputError } = useField(
373
'userInput',
374
computed(() => {
375
if (fieldType.value === 'email') {
376
return [(value) => !!value || 'Email required', (value) => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value) || 'Invalid email'];
377
} else if (fieldType.value === 'phone') {
378
return [(value) => !!value || 'Phone required', (value) => /^\d{10}$/.test(value) || 'Invalid phone'];
379
}
380
return (value) => !!value || 'This field is required';
381
})
382
);
383
```
384
385
### Cross-Field Validation
386
387
Validation that depends on multiple field values.
388
389
```typescript
390
import { useField, useFormContext } from "vee-validate";
391
392
const form = useFormContext();
393
394
// Price range validation
395
const { value: minPrice } = useField('minPrice', (value) => {
396
if (!value) return 'Minimum price is required';
397
const maxPrice = form.values.maxPrice;
398
if (maxPrice && value >= maxPrice) return 'Minimum must be less than maximum';
399
return true;
400
});
401
402
const { value: maxPrice } = useField('maxPrice', (value) => {
403
if (!value) return 'Maximum price is required';
404
const minPrice = form.values.minPrice;
405
if (minPrice && value <= minPrice) return 'Maximum must be greater than minimum';
406
return true;
407
});
408
409
// Date range validation
410
const { value: startDate } = useField('startDate', (value) => {
411
if (!value) return 'Start date is required';
412
const endDate = form.values.endDate;
413
if (endDate && new Date(value) >= new Date(endDate)) {
414
return 'Start date must be before end date';
415
}
416
return true;
417
});
418
419
const { value: endDate } = useField('endDate', (value) => {
420
if (!value) return 'End date is required';
421
const startDate = form.values.startDate;
422
if (startDate && new Date(value) <= new Date(startDate)) {
423
return 'End date must be after start date';
424
}
425
return true;
426
});
427
```