or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

configuration-rules.mdcore-validation.mdfield-management.mdform-actions.mdform-management.mdindex.mdstate-access.mdvue-components.md

field-management.mddocs/

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

```