Vue.js composition API wrappers for popular utility libraries enabling seamless integration of third-party tools
—
Async form validation using async-validator with reactive validation state and declarative component support.
Reactive form validation with async-validator integration and detailed error reporting.
/**
* Reactive form validation with async-validator integration
* @param value - Form data object to validate
* @param rules - Validation rules object
* @param options - Validation configuration options
* @returns Validation state and control methods
*/
function useAsyncValidator(
value: MaybeRefOrGetter<Record<string, any>>,
rules: MaybeRefOrGetter<Rules>,
options?: UseAsyncValidatorOptions
): UseAsyncValidatorReturn & PromiseLike<UseAsyncValidatorReturn>;
interface UseAsyncValidatorReturn {
/** Whether all validations pass */
pass: ShallowRef<boolean>;
/** Whether validation has completed */
isFinished: ShallowRef<boolean>;
/** Array of validation errors */
errors: ComputedRef<AsyncValidatorError['errors'] | undefined>;
/** Complete error information object */
errorInfo: ShallowRef<AsyncValidatorError | null>;
/** Errors organized by field name */
errorFields: ComputedRef<AsyncValidatorError['fields'] | undefined>;
/** Execute validation manually */
execute: () => Promise<UseAsyncValidatorExecuteReturn>;
}
interface UseAsyncValidatorExecuteReturn {
/** Whether validation passed */
pass: boolean;
/** Array of validation errors */
errors: AsyncValidatorError['errors'] | undefined;
/** Complete error information */
errorInfo: AsyncValidatorError | null;
/** Errors organized by field */
errorFields: AsyncValidatorError['fields'] | undefined;
}
interface UseAsyncValidatorOptions {
/** Options passed to async-validator */
validateOption?: ValidateOption;
/** Execute validation immediately */
immediate?: boolean; // default: true
/** Manual validation mode (disable auto-validation) */
manual?: boolean;
}
type AsyncValidatorError = Error & {
errors: ValidateError[];
fields: Record<string, ValidateError[]>;
};
// async-validator types
interface Rules {
[key: string]: RuleItem | RuleItem[];
}
interface RuleItem {
type?: RuleType;
required?: boolean;
pattern?: RegExp | string;
min?: number;
max?: number;
len?: number;
enum?: Array<string | number | boolean | null | undefined>;
whitespace?: boolean;
fields?: Rules;
defaultField?: RuleItem;
transform?: (value: any) => any;
message?: string | ((a?: string) => string);
asyncValidator?: (rule: InternalRuleItem, value: any, callback: (error?: string | Error) => void, source: Values, options: ValidateOption) => void;
validator?: (rule: InternalRuleItem, value: any, callback: (error?: string | Error) => void, source: Values, options: ValidateOption) => void;
}
type RuleType = 'string' | 'number' | 'boolean' | 'method' | 'regexp' | 'integer' | 'float' | 'array' | 'object' | 'enum' | 'date' | 'url' | 'hex' | 'email' | 'any';
interface ValidateError {
message?: string;
fieldValue?: any;
field?: string;
}
interface ValidateOption {
suppressWarning?: boolean;
first?: boolean;
firstFields?: boolean | string[];
}Usage Examples:
import { useAsyncValidator } from "@vueuse/integrations/useAsyncValidator";
import { ref, reactive } from 'vue';
// Basic form validation
const form = reactive({
name: '',
email: '',
age: 0
});
const rules = {
name: { type: 'string', required: true, min: 2 },
email: { type: 'email', required: true },
age: { type: 'number', required: true, min: 18 }
};
const { pass, errors, errorFields, execute } = useAsyncValidator(form, rules);
// Check validation state
watchEffect(() => {
if (pass.value) {
console.log('Form is valid!');
} else {
console.log('Validation errors:', errors.value);
}
});
// Manual validation
const handleSubmit = async () => {
const result = await execute();
if (result.pass) {
// Submit form
console.log('Submitting:', form);
} else {
console.log('Validation failed:', result.errors);
}
};
// Advanced validation with custom rules
const advancedRules = {
username: [
{ required: true, message: 'Username is required' },
{ min: 3, max: 20, message: 'Username must be 3-20 characters' },
{ pattern: /^[a-zA-Z0-9_]+$/, message: 'Username can only contain letters, numbers, and underscores' }
],
password: {
required: true,
validator: (rule, value, callback) => {
if (value.length < 8) {
callback(new Error('Password must be at least 8 characters'));
} else if (!/(?=.*[a-z])(?=.*[A-Z])(?=.*\d)/.test(value)) {
callback(new Error('Password must contain uppercase, lowercase, and numbers'));
} else {
callback();
}
}
},
confirmPassword: {
required: true,
validator: (rule, value, callback) => {
if (value !== form.password) {
callback(new Error('Passwords do not match'));
} else {
callback();
}
}
}
};
// Async validation
const asyncRules = {
email: {
required: true,
type: 'email',
asyncValidator: async (rule, value, callback) => {
try {
const response = await fetch(`/api/check-email?email=${value}`);
const result = await response.json();
if (!result.available) {
callback(new Error('Email is already taken'));
} else {
callback();
}
} catch (error) {
callback(new Error('Failed to validate email'));
}
}
}
};
// Manual validation mode
const { pass, execute } = useAsyncValidator(form, rules, {
immediate: false,
manual: true
});
// Display field-specific errors
const getFieldError = (fieldName: string) => {
return errorFields.value?.[fieldName]?.[0]?.message;
};Declarative validation component for template-based usage.
/**
* Declarative validation component
*/
const UseAsyncValidator = defineComponent({
name: 'UseAsyncValidator',
props: {
/** Form data object to validate */
form: {
type: Object as PropType<Record<string, any>>,
required: true
},
/** Validation rules object */
rules: {
type: Object as PropType<Rules>,
required: true
},
/** Validation options */
options: {
type: Object as PropType<UseAsyncValidatorOptions>,
default: () => ({})
}
},
slots: {
default: (props: {
pass: boolean;
errors: AsyncValidatorError['errors'] | undefined;
errorFields: AsyncValidatorError['fields'] | undefined;
isFinished: boolean;
execute: () => Promise<UseAsyncValidatorExecuteReturn>;
}) => any;
}
});Component Usage:
<template>
<UseAsyncValidator :form="form" :rules="rules" v-slot="{ pass, errors, errorFields, execute }">
<form @submit.prevent="execute">
<div>
<label>Name:</label>
<input v-model="form.name" />
<span v-if="errorFields?.name" class="error">
{{ errorFields.name[0].message }}
</span>
</div>
<div>
<label>Email:</label>
<input v-model="form.email" type="email" />
<span v-if="errorFields?.email" class="error">
{{ errorFields.email[0].message }}
</span>
</div>
<button type="submit" :disabled="!pass">
Submit
</button>
<div v-if="errors">
<h4>Validation Errors:</h4>
<ul>
<li v-for="error in errors" :key="error.field">
{{ error.message }}
</li>
</ul>
</div>
</form>
</UseAsyncValidator>
</template>
<script setup>
import { UseAsyncValidator } from "@vueuse/integrations/useAsyncValidator";
import { reactive } from 'vue';
const form = reactive({
name: '',
email: ''
});
const rules = {
name: { required: true, min: 2 },
email: { required: true, type: 'email' }
};
</script>Install with Tessl CLI
npx tessl i tessl/npm-vueuse--integrations