or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

form-validation.mdhttp-client.mdindex.mdsearch-data.mdstorage-state.mdui-interactions.mdvisual-effects.md

form-validation.mddocs/

0

# Form Validation

1

2

Async form validation using async-validator with reactive validation state and declarative component support.

3

4

## Capabilities

5

6

### useAsyncValidator

7

8

Reactive form validation with async-validator integration and detailed error reporting.

9

10

```typescript { .api }

11

/**

12

* Reactive form validation with async-validator integration

13

* @param value - Form data object to validate

14

* @param rules - Validation rules object

15

* @param options - Validation configuration options

16

* @returns Validation state and control methods

17

*/

18

function useAsyncValidator(

19

value: MaybeRefOrGetter<Record<string, any>>,

20

rules: MaybeRefOrGetter<Rules>,

21

options?: UseAsyncValidatorOptions

22

): UseAsyncValidatorReturn & PromiseLike<UseAsyncValidatorReturn>;

23

24

interface UseAsyncValidatorReturn {

25

/** Whether all validations pass */

26

pass: ShallowRef<boolean>;

27

/** Whether validation has completed */

28

isFinished: ShallowRef<boolean>;

29

/** Array of validation errors */

30

errors: ComputedRef<AsyncValidatorError['errors'] | undefined>;

31

/** Complete error information object */

32

errorInfo: ShallowRef<AsyncValidatorError | null>;

33

/** Errors organized by field name */

34

errorFields: ComputedRef<AsyncValidatorError['fields'] | undefined>;

35

/** Execute validation manually */

36

execute: () => Promise<UseAsyncValidatorExecuteReturn>;

37

}

38

39

interface UseAsyncValidatorExecuteReturn {

40

/** Whether validation passed */

41

pass: boolean;

42

/** Array of validation errors */

43

errors: AsyncValidatorError['errors'] | undefined;

44

/** Complete error information */

45

errorInfo: AsyncValidatorError | null;

46

/** Errors organized by field */

47

errorFields: AsyncValidatorError['fields'] | undefined;

48

}

49

50

interface UseAsyncValidatorOptions {

51

/** Options passed to async-validator */

52

validateOption?: ValidateOption;

53

/** Execute validation immediately */

54

immediate?: boolean; // default: true

55

/** Manual validation mode (disable auto-validation) */

56

manual?: boolean;

57

}

58

59

type AsyncValidatorError = Error & {

60

errors: ValidateError[];

61

fields: Record<string, ValidateError[]>;

62

};

63

64

// async-validator types

65

interface Rules {

66

[key: string]: RuleItem | RuleItem[];

67

}

68

69

interface RuleItem {

70

type?: RuleType;

71

required?: boolean;

72

pattern?: RegExp | string;

73

min?: number;

74

max?: number;

75

len?: number;

76

enum?: Array<string | number | boolean | null | undefined>;

77

whitespace?: boolean;

78

fields?: Rules;

79

defaultField?: RuleItem;

80

transform?: (value: any) => any;

81

message?: string | ((a?: string) => string);

82

asyncValidator?: (rule: InternalRuleItem, value: any, callback: (error?: string | Error) => void, source: Values, options: ValidateOption) => void;

83

validator?: (rule: InternalRuleItem, value: any, callback: (error?: string | Error) => void, source: Values, options: ValidateOption) => void;

84

}

85

86

type RuleType = 'string' | 'number' | 'boolean' | 'method' | 'regexp' | 'integer' | 'float' | 'array' | 'object' | 'enum' | 'date' | 'url' | 'hex' | 'email' | 'any';

87

88

interface ValidateError {

89

message?: string;

90

fieldValue?: any;

91

field?: string;

92

}

93

94

interface ValidateOption {

95

suppressWarning?: boolean;

96

first?: boolean;

97

firstFields?: boolean | string[];

98

}

99

```

100

101

**Usage Examples:**

102

103

```typescript

104

import { useAsyncValidator } from "@vueuse/integrations/useAsyncValidator";

105

import { ref, reactive } from 'vue';

106

107

// Basic form validation

108

const form = reactive({

109

name: '',

110

email: '',

111

age: 0

112

});

113

114

const rules = {

115

name: { type: 'string', required: true, min: 2 },

116

email: { type: 'email', required: true },

117

age: { type: 'number', required: true, min: 18 }

118

};

119

120

const { pass, errors, errorFields, execute } = useAsyncValidator(form, rules);

121

122

// Check validation state

123

watchEffect(() => {

124

if (pass.value) {

125

console.log('Form is valid!');

126

} else {

127

console.log('Validation errors:', errors.value);

128

}

129

});

130

131

// Manual validation

132

const handleSubmit = async () => {

133

const result = await execute();

134

if (result.pass) {

135

// Submit form

136

console.log('Submitting:', form);

137

} else {

138

console.log('Validation failed:', result.errors);

139

}

140

};

141

142

// Advanced validation with custom rules

143

const advancedRules = {

144

username: [

145

{ required: true, message: 'Username is required' },

146

{ min: 3, max: 20, message: 'Username must be 3-20 characters' },

147

{ pattern: /^[a-zA-Z0-9_]+$/, message: 'Username can only contain letters, numbers, and underscores' }

148

],

149

password: {

150

required: true,

151

validator: (rule, value, callback) => {

152

if (value.length < 8) {

153

callback(new Error('Password must be at least 8 characters'));

154

} else if (!/(?=.*[a-z])(?=.*[A-Z])(?=.*\d)/.test(value)) {

155

callback(new Error('Password must contain uppercase, lowercase, and numbers'));

156

} else {

157

callback();

158

}

159

}

160

},

161

confirmPassword: {

162

required: true,

163

validator: (rule, value, callback) => {

164

if (value !== form.password) {

165

callback(new Error('Passwords do not match'));

166

} else {

167

callback();

168

}

169

}

170

}

171

};

172

173

// Async validation

174

const asyncRules = {

175

email: {

176

required: true,

177

type: 'email',

178

asyncValidator: async (rule, value, callback) => {

179

try {

180

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

181

const result = await response.json();

182

if (!result.available) {

183

callback(new Error('Email is already taken'));

184

} else {

185

callback();

186

}

187

} catch (error) {

188

callback(new Error('Failed to validate email'));

189

}

190

}

191

}

192

};

193

194

// Manual validation mode

195

const { pass, execute } = useAsyncValidator(form, rules, {

196

immediate: false,

197

manual: true

198

});

199

200

// Display field-specific errors

201

const getFieldError = (fieldName: string) => {

202

return errorFields.value?.[fieldName]?.[0]?.message;

203

};

204

```

205

206

### UseAsyncValidator Component

207

208

Declarative validation component for template-based usage.

209

210

```typescript { .api }

211

/**

212

* Declarative validation component

213

*/

214

const UseAsyncValidator = defineComponent({

215

name: 'UseAsyncValidator',

216

props: {

217

/** Form data object to validate */

218

form: {

219

type: Object as PropType<Record<string, any>>,

220

required: true

221

},

222

/** Validation rules object */

223

rules: {

224

type: Object as PropType<Rules>,

225

required: true

226

},

227

/** Validation options */

228

options: {

229

type: Object as PropType<UseAsyncValidatorOptions>,

230

default: () => ({})

231

}

232

},

233

slots: {

234

default: (props: {

235

pass: boolean;

236

errors: AsyncValidatorError['errors'] | undefined;

237

errorFields: AsyncValidatorError['fields'] | undefined;

238

isFinished: boolean;

239

execute: () => Promise<UseAsyncValidatorExecuteReturn>;

240

}) => any;

241

}

242

});

243

```

244

245

**Component Usage:**

246

247

```vue

248

<template>

249

<UseAsyncValidator :form="form" :rules="rules" v-slot="{ pass, errors, errorFields, execute }">

250

<form @submit.prevent="execute">

251

<div>

252

<label>Name:</label>

253

<input v-model="form.name" />

254

<span v-if="errorFields?.name" class="error">

255

{{ errorFields.name[0].message }}

256

</span>

257

</div>

258

259

<div>

260

<label>Email:</label>

261

<input v-model="form.email" type="email" />

262

<span v-if="errorFields?.email" class="error">

263

{{ errorFields.email[0].message }}

264

</span>

265

</div>

266

267

<button type="submit" :disabled="!pass">

268

Submit

269

</button>

270

271

<div v-if="errors">

272

<h4>Validation Errors:</h4>

273

<ul>

274

<li v-for="error in errors" :key="error.field">

275

{{ error.message }}

276

</li>

277

</ul>

278

</div>

279

</form>

280

</UseAsyncValidator>

281

</template>

282

283

<script setup>

284

import { UseAsyncValidator } from "@vueuse/integrations/useAsyncValidator";

285

import { reactive } from 'vue';

286

287

const form = reactive({

288

name: '',

289

email: ''

290

});

291

292

const rules = {

293

name: { required: true, min: 2 },

294

email: { required: true, type: 'email' }

295

};

296

</script>

297

```