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

configuration-rules.mddocs/

0

# Configuration and Rules

1

2

Global configuration and custom validation rule definition for VeeValidate. These functions allow you to customize validation behavior and define reusable validation rules across your application.

3

4

## Capabilities

5

6

### defineRule Function

7

8

Registers a custom validation rule globally that can be used throughout the application.

9

10

```typescript { .api }

11

/**

12

* Registers a custom validation rule globally

13

* @param id - Unique rule identifier

14

* @param validator - Rule implementation function

15

*/

16

function defineRule<TValue = unknown, TParams extends any[] = any[]>(

17

id: string,

18

validator: ValidationRuleFunction<TValue, TParams> | SimpleValidationRuleFunction<TValue, TParams>

19

): void;

20

21

type ValidationRuleFunction<TValue, TParams extends any[]> = (

22

value: TValue,

23

params: TParams,

24

ctx: FieldValidationMetaInfo

25

) => MaybePromise<boolean | string>;

26

27

type SimpleValidationRuleFunction<TValue, TParams extends any[]> = (

28

value: TValue,

29

...params: TParams

30

) => MaybePromise<boolean | string>;

31

32

interface FieldValidationMetaInfo {

33

field: string; // Field name

34

name: string; // Field display name

35

label?: string; // Field label

36

form: Record<string, unknown>; // Current form values

37

rule?: { // Rule information

38

name: string; // Rule name

39

params?: Record<string, unknown> | unknown[]; // Rule parameters

40

};

41

}

42

```

43

44

**defineRule Examples:**

45

46

```typescript

47

import { defineRule } from "vee-validate";

48

49

// Simple validation rule

50

defineRule('required', (value: any) => {

51

if (!value || (Array.isArray(value) && value.length === 0)) {

52

return 'This field is required';

53

}

54

return true;

55

});

56

57

// Rule with parameters

58

defineRule('min', (value: string | number, [min]: [number]) => {

59

if (!value) return true; // Don't validate empty values

60

61

const length = typeof value === 'string' ? value.length : value;

62

if (length < min) {

63

return `This field must be at least ${min} characters`;

64

}

65

return true;

66

});

67

68

// Rule with multiple parameters

69

defineRule('between', (value: number, [min, max]: [number, number]) => {

70

if (value == null) return true;

71

72

if (value < min || value > max) {

73

return `This field must be between ${min} and ${max}`;

74

}

75

return true;

76

});

77

78

// Advanced rule with form context

79

defineRule('password_confirmation', (value: string, [], ctx) => {

80

const password = ctx.form.password;

81

82

if (value !== password) {

83

return 'Password confirmation does not match';

84

}

85

return true;

86

});

87

88

// Async validation rule

89

defineRule('unique_email', async (value: string) => {

90

if (!value) return true;

91

92

try {

93

const response = await fetch(`/api/check-email?email=${value}`);

94

const { available } = await response.json();

95

96

return available || 'This email is already taken';

97

} catch (error) {

98

return 'Unable to verify email availability';

99

}

100

});

101

102

// Rule with custom error messages based on parameters

103

defineRule('phone', (value: string, [format]: [string] = ['US']) => {

104

if (!value) return true;

105

106

const patterns = {

107

US: /^\(\d{3}\) \d{3}-\d{4}$/,

108

UK: /^\+44 \d{4} \d{6}$/,

109

INTERNATIONAL: /^\+\d{1,3} \d{1,14}$/

110

};

111

112

const pattern = patterns[format as keyof typeof patterns] || patterns.US;

113

114

if (!pattern.test(value)) {

115

return `Please enter a valid ${format} phone number`;

116

}

117

return true;

118

});

119

120

// Complex validation with multiple conditions

121

defineRule('strong_password', (value: string) => {

122

if (!value) return true;

123

124

const errors: string[] = [];

125

126

if (value.length < 8) {

127

errors.push('at least 8 characters');

128

}

129

130

if (!/[A-Z]/.test(value)) {

131

errors.push('one uppercase letter');

132

}

133

134

if (!/[a-z]/.test(value)) {

135

errors.push('one lowercase letter');

136

}

137

138

if (!/\d/.test(value)) {

139

errors.push('one number');

140

}

141

142

if (!/[!@#$%^&*(),.?":{}|<>]/.test(value)) {

143

errors.push('one special character');

144

}

145

146

if (errors.length > 0) {

147

return `Password must contain ${errors.join(', ')}`;

148

}

149

150

return true;

151

});

152

153

// Conditional validation rule

154

defineRule('required_if', (value: any, [targetField, targetValue]: [string, any], ctx) => {

155

const fieldValue = ctx.form[targetField];

156

157

if (fieldValue === targetValue && !value) {

158

return `This field is required when ${targetField} is ${targetValue}`;

159

}

160

161

return true;

162

});

163

```

164

165

### configure Function

166

167

Sets global validation configuration that affects all forms and fields.

168

169

```typescript { .api }

170

/**

171

* Sets global validation configuration

172

* @param config - Configuration object with validation settings

173

*/

174

function configure(config: Partial<VeeValidateConfig>): void;

175

176

interface VeeValidateConfig {

177

bails?: boolean; // Stop validation on first error

178

generateMessage?: ValidationMessageGenerator; // Custom message generator function

179

validateOnInput?: boolean; // Validate on input events

180

validateOnChange?: boolean; // Validate on change events

181

validateOnBlur?: boolean; // Validate on blur events

182

validateOnModelUpdate?: boolean; // Validate on v-model updates

183

}

184

185

type ValidationMessageGenerator = (ctx: ValidationMessageGeneratorContext) => string;

186

187

interface ValidationMessageGeneratorContext {

188

field: string; // Field name

189

name: string; // Field display name

190

label?: string; // Field label

191

value: unknown; // Field value

192

form: Record<string, unknown>; // Form values

193

rule: { // Rule information

194

name: string; // Rule name

195

params?: Record<string, unknown> | unknown[]; // Rule parameters

196

};

197

}

198

```

199

200

**configure Examples:**

201

202

```typescript

203

import { configure } from "vee-validate";

204

205

// Basic configuration

206

configure({

207

validateOnBlur: true,

208

validateOnChange: false,

209

validateOnInput: false,

210

validateOnModelUpdate: true,

211

bails: true

212

});

213

214

// Custom message generator

215

configure({

216

generateMessage: (ctx) => {

217

const messages: Record<string, string> = {

218

required: `${ctx.label || ctx.field} is required`,

219

email: `${ctx.label || ctx.field} must be a valid email`,

220

min: `${ctx.label || ctx.field} must be at least ${ctx.rule.params?.[0]} characters`,

221

max: `${ctx.label || ctx.field} must not exceed ${ctx.rule.params?.[0]} characters`,

222

confirmed: `${ctx.label || ctx.field} confirmation does not match`

223

};

224

225

return messages[ctx.rule.name] || `${ctx.label || ctx.field} is invalid`;

226

}

227

});

228

229

// Internationalization support

230

const messages = {

231

en: {

232

required: (field: string) => `${field} is required`,

233

email: (field: string) => `${field} must be a valid email`,

234

min: (field: string, params: any[]) => `${field} must be at least ${params[0]} characters`

235

},

236

es: {

237

required: (field: string) => `${field} es requerido`,

238

email: (field: string) => `${field} debe ser un email válido`,

239

min: (field: string, params: any[]) => `${field} debe tener al menos ${params[0]} caracteres`

240

}

241

};

242

243

const currentLocale = ref('en');

244

245

configure({

246

generateMessage: (ctx) => {

247

const locale = messages[currentLocale.value as keyof typeof messages];

248

const generator = locale[ctx.rule.name as keyof typeof locale];

249

250

if (generator) {

251

return generator(ctx.label || ctx.field, ctx.rule.params || []);

252

}

253

254

return `${ctx.label || ctx.field} is invalid`;

255

}

256

});

257

258

// Performance optimization configuration

259

configure({

260

// Only validate on blur to reduce validation calls

261

validateOnInput: false,

262

validateOnChange: false,

263

validateOnBlur: true,

264

265

// Stop on first error to improve performance

266

bails: true

267

});

268

269

// Development vs production configuration

270

const isDevelopment = process.env.NODE_ENV === 'development';

271

272

configure({

273

validateOnInput: isDevelopment, // More responsive validation in development

274

validateOnBlur: true,

275

bails: !isDevelopment, // Show all errors in development

276

277

generateMessage: (ctx) => {

278

if (isDevelopment) {

279

// Detailed error messages in development

280

return `[${ctx.rule.name}] ${ctx.field}: ${ctx.value} failed validation`;

281

}

282

283

// User-friendly messages in production

284

return getProductionMessage(ctx);

285

}

286

});

287

288

const getProductionMessage = (ctx: ValidationMessageGeneratorContext): string => {

289

// Production-friendly error messages

290

const field = ctx.label || humanizeFieldName(ctx.field);

291

292

switch (ctx.rule.name) {

293

case 'required':

294

return `Please enter your ${field.toLowerCase()}`;

295

case 'email':

296

return `Please enter a valid ${field.toLowerCase()}`;

297

case 'min':

298

return `${field} is too short`;

299

case 'max':

300

return `${field} is too long`;

301

default:

302

return `Please check your ${field.toLowerCase()}`;

303

}

304

};

305

306

const humanizeFieldName = (field: string): string => {

307

return field

308

.replace(/([A-Z])/g, ' $1') // Add space before capital letters

309

.replace(/^./, str => str.toUpperCase()) // Capitalize first letter

310

.replace(/_/g, ' '); // Replace underscores with spaces

311

};

312

```

313

314

## Built-in Rule Integration

315

316

### Using defineRule with Popular Validation Libraries

317

318

Integrating VeeValidate with existing validation rule libraries.

319

320

```typescript

321

import { defineRule } from "vee-validate";

322

323

// Integration with validator.js

324

import validator from "validator";

325

326

defineRule('email', (value: string) => {

327

if (!value) return true;

328

return validator.isEmail(value) || 'Please enter a valid email address';

329

});

330

331

defineRule('url', (value: string) => {

332

if (!value) return true;

333

return validator.isURL(value) || 'Please enter a valid URL';

334

});

335

336

defineRule('credit_card', (value: string) => {

337

if (!value) return true;

338

return validator.isCreditCard(value) || 'Please enter a valid credit card number';

339

});

340

341

// Custom business rules

342

defineRule('business_email', (value: string) => {

343

if (!value) return true;

344

345

const personalDomains = [

346

'gmail.com', 'yahoo.com', 'hotmail.com',

347

'outlook.com', 'aol.com', 'icloud.com'

348

];

349

350

const domain = value.split('@')[1];

351

352

if (personalDomains.includes(domain)) {

353

return 'Please use a business email address';

354

}

355

356

return validator.isEmail(value) || 'Please enter a valid email address';

357

});

358

359

// File validation rules

360

defineRule('file_size', (files: FileList | File[], [maxSize]: [number]) => {

361

if (!files || files.length === 0) return true;

362

363

const fileArray = Array.from(files);

364

const oversizedFiles = fileArray.filter(file => file.size > maxSize);

365

366

if (oversizedFiles.length > 0) {

367

const maxSizeMB = (maxSize / (1024 * 1024)).toFixed(1);

368

return `File size must not exceed ${maxSizeMB}MB`;

369

}

370

371

return true;

372

});

373

374

defineRule('file_type', (files: FileList | File[], allowedTypes: string[]) => {

375

if (!files || files.length === 0) return true;

376

377

const fileArray = Array.from(files);

378

const invalidFiles = fileArray.filter(file =>

379

!allowedTypes.some(type => file.type.startsWith(type))

380

);

381

382

if (invalidFiles.length > 0) {

383

return `Only ${allowedTypes.join(', ')} files are allowed`;

384

}

385

386

return true;

387

});

388

389

// Date validation rules

390

defineRule('date_after', (value: string, [afterDate]: [string]) => {

391

if (!value) return true;

392

393

const inputDate = new Date(value);

394

const compareDate = new Date(afterDate);

395

396

if (inputDate <= compareDate) {

397

return `Date must be after ${compareDate.toLocaleDateString()}`;

398

}

399

400

return true;

401

});

402

403

defineRule('date_before', (value: string, [beforeDate]: [string]) => {

404

if (!value) return true;

405

406

const inputDate = new Date(value);

407

const compareDate = new Date(beforeDate);

408

409

if (inputDate >= compareDate) {

410

return `Date must be before ${compareDate.toLocaleDateString()}`;

411

}

412

413

return true;

414

});

415

416

// Age validation

417

defineRule('min_age', (birthDate: string, [minAge]: [number]) => {

418

if (!birthDate) return true;

419

420

const birth = new Date(birthDate);

421

const today = new Date();

422

const age = today.getFullYear() - birth.getFullYear();

423

const monthDiff = today.getMonth() - birth.getMonth();

424

425

const actualAge = monthDiff < 0 || (monthDiff === 0 && today.getDate() < birth.getDate())

426

? age - 1

427

: age;

428

429

if (actualAge < minAge) {

430

return `You must be at least ${minAge} years old`;

431

}

432

433

return true;

434

});

435

```

436

437

## Rule Usage Patterns

438

439

### String-based Rule Usage

440

441

Using defined rules in field validation.

442

443

```typescript

444

import { useField } from "vee-validate";

445

446

// Single rule

447

const { value: email } = useField('email', 'required');

448

449

// Multiple rules with pipe syntax

450

const { value: password } = useField('password', 'required|min:8|strong_password');

451

452

// Rules with parameters

453

const { value: age } = useField('age', 'required|between:18,100');

454

455

// Complex rule combinations

456

const { value: businessEmail } = useField(

457

'businessEmail',

458

'required|business_email'

459

);

460

461

// File upload validation

462

const { value: avatar } = useField(

463

'avatar',

464

'required|file_size:2097152|file_type:image/jpeg,image/png'

465

);

466

```

467

468

### Object-based Rule Usage

469

470

Using rules with object syntax for complex parameter passing.

471

472

```typescript

473

import { useField } from "vee-validate";

474

475

// Object rule syntax

476

const { value: username } = useField('username', {

477

required: true,

478

min: 3,

479

unique_email: true

480

});

481

482

// Mixed rule formats

483

const { value: phoneNumber } = useField('phoneNumber', [

484

'required',

485

{ phone: 'US' },

486

(value) => value.startsWith('+1') || 'Phone number must include country code'

487

]);

488

```

489

490

### Conditional Rule Application

491

492

Applying rules based on dynamic conditions.

493

494

```typescript

495

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

496

497

const form = useFormContext();

498

499

// Conditional validation based on other field values

500

const { value: confirmPassword } = useField(

501

'confirmPassword',

502

computed(() => {

503

const password = form.values.password;

504

return password ? 'required|password_confirmation' : '';

505

})

506

);

507

508

// Dynamic rule parameters

509

const { value: discountCode } = useField(

510

'discountCode',

511

computed(() => {

512

const userTier = form.values.userTier;

513

const rules = ['required'];

514

515

if (userTier === 'premium') {

516

rules.push('min:8');

517

} else {

518

rules.push('min:4');

519

}

520

521

return rules.join('|');

522

})

523

);

524

```