Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

vue3-form-validation

Package Overview
Dependencies
Maintainers
1
Versions
59
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

vue3-form-validation - npm Package Compare versions

Comparing version 2.1.2 to 3.0.0

79

dist/composable/useValidation.d.ts

@@ -1,2 +0,2 @@

import { Ref, UnwrapRef } from 'vue';
import { Ref, ComputedRef, UnwrapRef } from 'vue';
import Form from '../Form';

@@ -9,5 +9,5 @@ export declare type SimpleRule<T = any> = (value: T) => Promise<unknown> | unknown;

export declare type Rule<T = any> = SimpleRule<T> | KeyedRule<T>;
export declare type Field<T> = {
$value: Ref<T> | T;
$rules?: Rule<T>[];
export declare type Field<TValue> = {
$value: TValue extends Ref ? TValue | UnwrapRef<TValue> : TValue extends Record<string, unknown> | any[] ? RefUnref<TValue> : Ref<TValue> | TValue;
$rules?: Rule<UnwrapRef<TValue>>[];
};

@@ -21,39 +21,58 @@ export declare type TransformedField<T> = {

};
declare type UnwrapNestedRefs<T> = T extends Ref ? T : UnwrapRef<T>;
declare type ValidateFormData<T> = {
export declare type RefUnref<T extends Record<string, unknown> | any[]> = {
[K in keyof T]: T[K] extends Ref ? T[K] | UnwrapRef<T[K]> : T[K] extends Array<infer TArray> ? RefUnref<TArray[]> : T[K] extends Record<string, unknown> ? RefUnref<T[K]> : Ref<T[K]> | T[K];
};
export declare type ValidateInput<T extends object | any[]> = {
[K in keyof T]: T[K] extends {
$value: infer TValue;
} ? TValue extends Ref<infer TRef> ? Field<TRef> : Field<TValue> : T[K] extends Array<infer TArray> ? ValidateFormData<TArray>[] : T[K] extends Record<string, unknown> ? ValidateFormData<T[K]> : unknown;
} ? Field<TValue> : T[K] extends Array<infer TArray> ? ValidateInput<TArray[]> : T[K] extends Record<string, unknown> ? ValidateInput<T[K]> : unknown;
};
declare type TransformedFormData<T> = T extends any ? {
[K in keyof T]: T[K] extends Field<infer TValue> ? TransformedField<TValue> : T[K] extends Array<infer TArray> ? TransformedFormData<TArray>[] : T[K] extends Record<string, unknown> ? TransformedFormData<T[K]> : T[K];
export declare type TransformedFormData<T extends object> = T extends any ? {
[K in keyof T]: T[K] extends {
$value: infer TValue;
} ? TransformedField<UnwrapRef<TValue>> : T[K] extends Array<infer TArray> ? TransformedFormData<TArray[]> : T[K] extends Record<string, unknown> ? TransformedFormData<T[K]> : T[K];
} : never;
declare type FormData<T> = T extends any ? {
[K in keyof T as T[K] extends any[] ? K : T[K] extends Record<string, unknown> ? K : never]: T[K] extends Field<infer TValue> ? UnwrapNestedRefs<TValue> : T[K] extends Array<infer TArray> ? FormData<TArray>[] : T[K] extends Record<string, unknown> ? FormData<T[K]> : never;
export declare type FormData<T extends object> = T extends any ? {
[K in keyof T]: T[K] extends {
$value: infer TValue;
} ? UnwrapRef<TValue> : T[K] extends Array<infer TArray> ? FormData<TArray[]> : T[K] extends Record<string, unknown> ? FormData<T[K]> : T[K];
} : never;
declare type Keys = readonly (string | number)[];
declare type DeepIndex<T, Ks extends Keys> = Ks extends [infer First, ...infer Rest] ? First extends keyof T ? Rest extends Keys ? DeepIndex<T[First], Rest> : undefined : undefined : T;
/**
*
* @param form - Form object with methods like `registerField` and `validate`.
* @param formData - The form data to transform.
* @description In place transformation of a given form data object.
* Recursively add's some metadata to every form field.
*/
export declare type Keys = readonly (string | number)[];
export declare type DeepIndex<T, Ks extends Keys> = Ks extends [
infer First,
...infer Rest
] ? First extends keyof T ? Rest extends Keys ? DeepIndex<T[First], Rest> : undefined : undefined : T;
export declare function transformFormData(form: Form, formData: any): void;
export declare function cleanupForm(form: Form, formData: any): void;
export declare function getResultFormData(formData: any, resultFormData: any): void;
declare type UseValidation<T extends object> = {
form: TransformedFormData<T>;
submitting: Ref<boolean>;
errors: ComputedRef<string[]>;
validateFields(): Promise<FormData<T>>;
resetFields(): void;
add<Ks extends Keys>(pathToArray: readonly [...Ks], value: DeepIndex<ValidateInput<T>, Ks> extends Array<infer TArray> ? TArray : never): void;
remove<Ks extends Keys>(pathToArray: readonly [...Ks], index: DeepIndex<ValidateInput<T>, Ks> extends any[] ? number : never): void;
};
/**
*
* @param formData - The structure of your Form Data
* @hint
* To get the best IntelliSense when using TypeScript, consider defining the structure
* of your `formData` upfront and pass it as the generic parameter `T`.
* @param formData The structure of your Form Data.
* @description
* Vue composition function for Form Validation.
* @docs
* https://github.com/JensDll/vue3-form-validation
* @typescript
* For best type inference, consider defining the structure
* of your `formData` upfront and pass it as the generic parameter `T`. For example:
* ```
* type FormData = {
* name: Field<string>,
* email: Field<string>,
* password: Field<string>
* }
*
* const { ... } = useValidation<FormData>({ ... })
* ```
*/
export declare function useValidation<T>(formData: T & ValidateFormData<T>): {
form: TransformedFormData<T>;
onSubmit(success: (formData: FormData<T>) => void, error?: (() => void) | undefined): void;
add<Ks extends Keys>(pathToArray: readonly [...Ks], value: DeepIndex<ValidateFormData<T>, Ks> extends (infer TArray)[] ? TArray : never): void;
remove<Ks_1 extends Keys>(pathToArray: readonly [...Ks_1], index: DeepIndex<ValidateFormData<T>, Ks_1> extends any[] ? number : never): void;
};
export declare function useValidation<T extends object>(formData: T & ValidateInput<T>): UseValidation<T>;
export {};

@@ -1,2 +0,2 @@

import { isReactive, reactive, watch } from 'vue';
import { reactive, watch, ref } from 'vue';
import useUid from './useUid';

@@ -12,9 +12,2 @@ import Form from '../Form';

: false;
/**
*
* @param form - Form object with methods like `registerField` and `validate`.
* @param formData - The form data to transform.
* @description In place transformation of a given form data object.
* Recursively add's some metadata to every form field.
*/
export function transformFormData(form, formData) {

@@ -25,6 +18,6 @@ Object.entries(formData).forEach(([key, value]) => {

const uid = useUid();
const formField = form.registerField(uid, (_a = value.$rules) !== null && _a !== void 0 ? _a : []);
const formField = form.registerField(uid, (_a = value.$rules) !== null && _a !== void 0 ? _a : [], value.$value);
formData[key] = reactive({
$uid: uid,
$value: value.$value,
$value: formField.modelValue,
$errors: formField.getErrors(),

@@ -39,15 +32,7 @@ $validating: formField.validating(),

});
formField.modelValue = formData[key].$value;
const watchHandler = async (modelValue) => {
formField.modelValue = modelValue;
watch(formField.modelValue, () => {
if (formField.touched) {
await form.validate(uid);
form.validate(uid);
}
};
if (isReactive(formData[key].$value)) {
watch(formData[key].$value, watchHandler);
}
else {
watch(() => formData[key].$value, watchHandler);
}
});
return;

@@ -77,12 +62,13 @@ }

}
resultFormData[key] = {};
if (typeof value == 'object') {
resultFormData[key] = {};
}
else {
resultFormData[key] = value;
return;
}
if (Array.isArray(value)) {
resultFormData[key] = [];
}
if (typeof value === 'object') {
getResultFormData(value, resultFormData[key]);
}
else {
delete resultFormData[key];
}
getResultFormData(value, resultFormData[key]);
});

@@ -92,27 +78,46 @@ }

*
* @param formData - The structure of your Form Data
* @hint
* To get the best IntelliSense when using TypeScript, consider defining the structure
* of your `formData` upfront and pass it as the generic parameter `T`.
* @param formData The structure of your Form Data.
* @description
* Vue composition function for Form Validation.
* @docs
* https://github.com/JensDll/vue3-form-validation
* @typescript
* For best type inference, consider defining the structure
* of your `formData` upfront and pass it as the generic parameter `T`. For example:
* ```
* type FormData = {
* name: Field<string>,
* email: Field<string>,
* password: Field<string>
* }
*
* const { ... } = useValidation<FormData>({ ... })
* ```
*/
export function useValidation(formData) {
const form = new Form();
const submitting = ref(false);
transformFormData(form, formData);
const reactiveFormData = reactive(formData);
const transformedFormData = reactive(formData);
return {
form: reactiveFormData,
onSubmit(success, error) {
form.validateAll().then(hasError => {
if (hasError) {
error === null || error === void 0 ? void 0 : error();
}
else {
const resultFormData = {};
getResultFormData(reactiveFormData, resultFormData);
success(resultFormData);
}
});
form: transformedFormData,
submitting,
errors: form.getErrors(),
async validateFields() {
submitting.value = true;
const hasError = await form.validateAll();
if (hasError) {
submitting.value = false;
throw undefined;
}
const resultFormData = {};
getResultFormData(transformedFormData, resultFormData);
submitting.value = false;
return resultFormData;
},
resetFields() {
form.resetFields();
},
add(pathToArray, value) {
const xs = path(pathToArray, reactiveFormData);
const xs = path(pathToArray, transformedFormData);
if (Array.isArray(xs)) {

@@ -124,3 +129,3 @@ transformFormData(form, value);

remove(pathToArray, index) {
const xs = path(pathToArray, reactiveFormData);
const xs = path(pathToArray, transformedFormData);
if (Array.isArray(xs)) {

@@ -127,0 +132,0 @@ const deleted = xs.splice(index, 1);

import { Rule } from './composable/useValidation';
import FormField from './FormField';
declare type ValidateResult = void | string;
export default class Form {
private simpleValidators;
private keyedValidators;
registerField(uid: number, rules: Rule[]): FormField;
private reactiveFormFields;
private trySetKeyed;
private tryGetSimple;
private tryGetKeyed;
registerField(uid: number, rules: Rule[], modelValue?: unknown): FormField;
getErrors(): import("vue").ComputedRef<string[]>;
resetFields(): void;
validateAll(): Promise<boolean>;
validate(uid: number): Promise<PromiseSettledResult<string | void>[]>;
validate(uid: number): Promise<PromiseSettledResult<ValidateResult>[]>;
onDelete(uid: number): void;
private getValidatorsFor;
private getValidateResultsFor;
private isEveryFormFieldTouchedWith;
private makeValidate;
}
export {};

@@ -0,1 +1,2 @@

import { computed, reactive, unref } from 'vue';
import FormField from './FormField';

@@ -8,23 +9,25 @@ import { tryGet, trySet } from './utils';

this.keyedValidators = new Map();
this.reactiveFormFields = reactive(new Map());
this.trySetKeyed = trySet(this.keyedValidators);
this.tryGetSimple = tryGet(this.simpleValidators);
this.tryGetKeyed = tryGet(this.keyedValidators);
}
registerField(uid, rules) {
const formField = new FormField(rules);
const simple = rules.reduce((acc, rule, index) => {
registerField(uid, rules, modelValue) {
const formField = new FormField(rules, modelValue);
const simple = rules.reduce((simple, rule, index) => {
const validate = this.makeValidate(formField, rule, index);
if (isSimpleRule(rule)) {
acc.validators.push(validate);
simple.validators.push(validate);
}
else {
const entry = { formField, validator: validate };
acc.keys.push(rule.key);
trySet(this.keyedValidators)({
failure: validators => {
validators.add(entry);
}
simple.keys.push(rule.key);
this.trySetKeyed({
failure: keyed => keyed.add(entry)
})(rule.key, new Set([entry]));
acc.rollback.push(() => {
tryGet(this.keyedValidators)({
success: validators => {
validators.delete(entry);
if (!validators.size) {
simple.rollback.push(() => {
this.tryGetKeyed({
success: keyed => {
keyed.delete(entry);
if (!keyed.size) {
this.keyedValidators.delete(rule.key);

@@ -36,3 +39,3 @@ }

}
return acc;
return simple;
}, {

@@ -45,4 +48,19 @@ formField,

this.simpleValidators.set(uid, simple);
this.reactiveFormFields.set(uid, formField);
return formField;
}
getErrors() {
return computed(() => {
const errors = [];
for (const formField of this.reactiveFormFields.values()) {
errors.push(...formField.getErrors().value);
}
return errors;
});
}
resetFields() {
for (const { formField } of this.simpleValidators.values()) {
formField.reset();
}
}
async validateAll() {

@@ -56,6 +74,6 @@ for (const { formField } of this.simpleValidators.values()) {

});
const errors = await Promise.all(promises);
for (const promiseResults of errors) {
for (const result of promiseResults) {
if (result.status === 'rejected') {
const allSettledResults = await Promise.all(promises);
for (const settledResults of allSettledResults) {
for (const settledResult of settledResults) {
if (settledResult.status === 'rejected') {
return true;

@@ -69,3 +87,3 @@ }

let promise = Promise.allSettled([]);
tryGet(this.simpleValidators)({
this.tryGetSimple({
success: ({ formField, keys, validators }) => {

@@ -75,3 +93,3 @@ if (formField.touched) {

...validators.map(v => v()),
...this.getValidatorsFor(keys).map(v => v())
...this.getValidateResultsFor(keys)
]);

@@ -84,3 +102,3 @@ }

onDelete(uid) {
tryGet(this.simpleValidators)({
this.tryGetSimple({
success({ rollback }) {

@@ -91,15 +109,10 @@ rollback.forEach(r => r());

this.simpleValidators.delete(uid);
this.reactiveFormFields.delete(uid);
}
getValidatorsFor(keys) {
getValidateResultsFor(keys) {
return keys.reduce((promises, key) => {
tryGet(this.keyedValidators)({
success(validators) {
let everyFormFieldIsTouched = true;
validators.forEach(({ formField }) => {
if (!formField.touched) {
everyFormFieldIsTouched = false;
}
});
if (everyFormFieldIsTouched) {
promises.push(...[...validators.values()].map(({ validator }) => validator));
this.tryGetKeyed({
success: keyed => {
if (this.isEveryFormFieldTouchedWith(key)) {
promises.push(...[...keyed.values()].map(({ validator }) => validator()));
}

@@ -111,2 +124,16 @@ }

}
isEveryFormFieldTouchedWith(key) {
let everyFormFieldIsTouched = true;
this.tryGetKeyed({
success: keyed => {
for (const { formField } of keyed) {
if (!formField.touched) {
everyFormFieldIsTouched = false;
break;
}
}
}
})(key);
return everyFormFieldIsTouched;
}
makeValidate(formField, rule, index) {

@@ -117,3 +144,3 @@ const validator = formField => (index, rule) => async () => {

formField.incrementWaiting(index);
error = await rule(formField.modelValue);
error = await rule(unref(formField.modelValue));
}

@@ -126,3 +153,3 @@ catch (err) {

}
if (formField.nooneIsWaiting(index)) {
if (formField.nooneIsWaiting(index) && formField.touched) {
if (typeof error === 'string') {

@@ -129,0 +156,0 @@ formField.setError(index, error);

@@ -0,1 +1,2 @@

import { reactive, ref } from 'vue';
import { Rule } from './composable/useValidation';

@@ -7,5 +8,6 @@ export default class FormField {

private errorCount;
modelValue: unknown;
modelValue: ReturnType<typeof ref> | ReturnType<typeof reactive>;
private initialModelValue;
touched: boolean;
constructor(rules: Rule[]);
constructor(rules: Rule[], modelValue: unknown);
setError(index: number, error: string | null): void;

@@ -18,2 +20,3 @@ incrementWaiting(index: number): void;

validating(): import("vue").ComputedRef<boolean>;
reset(): void;
}

@@ -1,13 +0,25 @@

import { computed, ref } from 'vue';
import { computed, isRef, reactive, ref } from 'vue';
const notNull = (value) => value !== null;
export default class FormField {
constructor(rules) {
constructor(rules, modelValue) {
this.totalWaiting = ref(0);
this.errorCount = 0;
this.touched = false;
this.errors = ref(rules.map(() => null));
this.errors = reactive(rules.map(() => null));
this.waiting = rules.map(() => 0);
if (isRef(modelValue)) {
this.modelValue = modelValue;
this.initialModelValue = modelValue.value;
}
else if (typeof modelValue === 'object' && modelValue !== null) {
this.modelValue = reactive(modelValue);
this.initialModelValue = JSON.parse(JSON.stringify(this.modelValue));
}
else {
this.modelValue = ref(modelValue);
this.initialModelValue = modelValue;
}
}
setError(index, error) {
const willBeSet = this.errors.value[index];
const willBeSet = this.errors[index];
if (willBeSet === null && typeof error === 'string') {

@@ -19,3 +31,3 @@ this.errorCount++;

}
this.errors.value[index] = error;
this.errors[index] = error;
}

@@ -37,3 +49,3 @@ incrementWaiting(index) {

getErrors() {
return computed(() => this.errors.value.filter(notNull));
return computed(() => this.errors.filter(notNull));
}

@@ -43,2 +55,14 @@ validating() {

}
reset() {
this.touched = false;
if (isRef(this.modelValue)) {
this.modelValue.value = this.initialModelValue;
}
else {
Object.assign(this.modelValue, this.initialModelValue);
this.initialModelValue = JSON.parse(JSON.stringify(this.initialModelValue));
}
Object.assign(this.errors, this.errors.map(() => null));
this.errorCount = 0;
}
}
{
"name": "vue3-form-validation",
"version": "2.1.2",
"description": "Form validation for Vue 3",
"version": "3.0.0",
"description": "Vue composition function for Form Validation",
"author": {

@@ -11,3 +11,3 @@ "name": "jens",

"type": "git",
"url": "https://github.com/JensD98/vue3-form-validation.git"
"url": "https://github.com/JensDll/vue3-form-validation#readme"
},

@@ -25,29 +25,42 @@ "main": "dist/index.js",

"form",
"validation"
"validation",
"utils",
"vue-use"
],
"scripts": {
"build": "npx tsc -p tsconfig.main.json",
"build": "npx eslint --rule=\"no-console:error\" --fix-dry-run main && npx tsc --project ./main",
"lint": "npx prettier --write .",
"test": "jest",
"dev": "vite"
"test": "jest --config ./main/jest.config.ts",
"test-dts": "npx tsd",
"test-all": "npm run build && npm run test && npm run test-dts",
"dev": "vite",
"_postinstall": "husky install",
"prepublishOnly": "pinst --disable",
"postpublish": "pinst --enable"
},
"devDependencies": {
"@types/jest": "^26.0.15",
"@types/node": "^14.14.10",
"@typescript-eslint/eslint-plugin": "^4.8.2",
"@typescript-eslint/parser": "^4.8.2",
"@vue/compiler-sfc": "^3.0.3",
"eslint": "^7.14.0",
"eslint-config-prettier": "^6.15.0",
"eslint-plugin-vue": "^7.1.0",
"husky": "^4.3.0",
"@types/jest": "^26.0.20",
"@types/node": "^14.14.27",
"@typescript-eslint/eslint-plugin": "^4.15.0",
"@typescript-eslint/parser": "^4.15.0",
"@vitejs/plugin-vue": "^1.0.6",
"@vue/compiler-sfc": "^3.0.5",
"autoprefixer": "^10.2.4",
"eslint": "^7.18.0",
"eslint-config-prettier": "^7.2.0",
"eslint-plugin-vue": "^7.4.1",
"husky": "^5.0.9",
"jest": "^26.6.3",
"lint-staged": "^10.5.2",
"lint-staged": "^10.5.3",
"pinst": "^2.1.4",
"postcss": "^8.2.6",
"prettier": "^2.2.1",
"tailwindcss": "npm:@tailwindcss/postcss7-compat@^2.0.1",
"ts-jest": "^26.4.4",
"typescript": "^4.1.2",
"vite": "^1.0.0-rc.13",
"vue": "^3.0.3",
"vue-router": "^4.0.0-rc.5"
"tailwindcss": "^2.0.3",
"ts-jest": "^26.5.1",
"ts-node": "^9.1.1",
"tsd": "^0.14.0",
"typescript": "^4.1.5",
"vite": "^2.0.0-beta.69",
"vue": "^3.0.5",
"vue-router": "^4.0.3"
},

@@ -60,7 +73,5 @@ "prettier": {

},
"husky": {
"hooks": {
"pre-commit": "npx eslint --rule=\"no-console:error\" --fix-dry-run main"
}
"tsd": {
"directory": "test-dts"
}
}

@@ -1,31 +0,44 @@

# Form validation for Vue 3
Easy to use opinionated Form validation for Vue 3.
# Form Validation for Vue 3
* :milky_way: **Written in TypeScript**
* :ocean: **Dynamic Form support**
* :fallen_leaf: **Light weight**
[![npm](https://img.shields.io/npm/v/vue3-form-validation)](https://www.npmjs.com/package/vue3-form-validation)
Opinionated Vue composition function for Form Validation.
- :milky_way: **Written in TypeScript**
- :ocean: **Dynamic Form support**
- :fallen_leaf: **Light weight**
```bash
npm i vue3-form-validation
npm install vue3-form-validation
```
Validation is async and is utilising `Promise.allSettled`, [which](https://developer.mozilla.org/de/docs/Web/JavaScript/Reference/Global_Objects/Promise/allSettled) has not yet reached cross-browser stability. Example usage can be found in this [Code Sandbox](https://codesandbox.io/s/vue-3-form-validation-demo-busd9?file=/src/LoginForm.vue).
Validation is async and is utilising `Promise.allSettled`, [which](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/allSettled) has not yet reached cross-browser stability. Example usage can be found in this [Code Sandbox](https://codesandbox.io/s/vue-3-form-validation-demo-busd9?file=/src/LoginForm.vue).
## API
This package exports one function `useValidation`, plus some type definitions for when using TypeScript.
### useValidation
```ts
const { form, add, remove, onSubmit } = useValidation<T>(formData)
const {
form,
errors,
submitting,
validateFields,
resetFields,
add,
remove
} = useValidation<T>(formData);
```
* `useValidation` takes the following parameters:
### `useValidation` takes the following parameters:
Parameters | Type | Required | Description
---|:-:|:-:|---
formData | `object` | `true` | The structure of your Form data.
- `formData`
- **Type** - `object`
- **Required** - `true`
- **Description** - The structure of your `formData`.
The `formData` object has a structure that is similar to any other object you would write for `v-model` data binding. The only difference being that together with every value you can provide rules to display validation errors.
Let's look at an example how the structure of some `formData` object, can be converted to an object with the addition of rules:
Let's look at an example how the structure of some `formData` object can be converted to an object with the addition of rules:
```ts

@@ -36,3 +49,3 @@ const formData = {

password: ''
}
};

@@ -51,8 +64,10 @@ // The above can be converted to the following

$value: '',
$rules: [pw => pw.length > 7 || 'Password has to be longer than 7 characters']
$rules: [
pw => pw.length > 7 || 'Password has to be longer than 7 characters'
]
}
}
};
```
The `formData` object can contain arrays and can be deeply nested. At the leaf level, the object should contain Form Fields whose type definition looks like the following:
The `formData` object can contain arrays and can be deeply nested. At the leaf level, the object should contain Form Fields whose simplified type definition looks like the following:

@@ -66,3 +81,3 @@ ```ts

To get the best IntelliSense while writing the `useValidation` function, it's recommended to define the structure of your `formData` upfront and pass it as the generic parameter `T`. The type for the example above is pretty straightforward:
To get better type inference while writing the `useValidation` function, it's recommended to define the structure of your `formData` upfront and pass it as the generic parameter `T`. The type for the example above is pretty straightforward:

@@ -74,15 +89,19 @@ ```ts

password: Field<string>;
}
};
```
* `useValidation` exposes the following state:
### `useValidation` exposes the following state:
State | Type | Description
---|:-:|---
form | `object` | Transformed `formData` object with added metadata to every Form Field.
- `form`
- **Type** - `object`
- **Description** - Transformed `formData` object.
- `submitting`
- **Type** - `Ref<boolean>`
- **Description** - `True` during validation after calling `validateFields`.
- `errors`
- **Type** - `ComputedRef<string[]>`
- **Description** - Array of all current validation error messages.
`Form` is a reactive object with identical structure as the `formData` input, but with added metadata to every Form Field.
**Typing:**
```ts

@@ -102,27 +121,43 @@ type TransformedField<T> = {

password: TransformedField<string>;
}
};
```
As you may have noticed, all of the properties are prefixed with the `$` symbol, which is to distinguish them from other properties but also to avoid naming conflicts.
Key | Value | Description
---|:-:|---
uid | `number` | Unique identifier of the Form Field. For dynamic Forms this can be used as the `key` attribute in `v-for`.
value | `T` | The `modelValue` of the Form Field which is meant to be used together with `v-model`.
errors | `string[]` | Array of validation error messages.
validating | `boolean` | `True` while atleast one rule is validating.
onBlur | `function` | Function which will mark this Form Field as touched. When a Form Field has been touched it will validate all it's rules after every input. Before it will not do any validation.
* `useValidation` exposes the following methods:
- `$uid`
- **Type** - `number`
- **Description** - Unique identifier of the Form Field. For dynamic Forms this can be used as the `key` attribute in `v-for`.
- `$value`
- **Type** - `T`
- **Description** - The `modelValue` of the Form Field which is meant to be used together with `v-model`.
- `$errors`
- **Type** - `string[]`
- **Description** - Array of validation error messages.
- `$validating`
- **Type** - `boolean`
- **Description** - `True` while at least one rule is validating.
- `$onBlur`
- **Type** - `function`
- **Description** - Function which will mark this Form Field as touched. When a Form Field has been touched it will validate all it's rules after every input. Before it will not do any validation.
Signature | Parameters | Description
--- | :-: | ---
`onSubmit(success, error?)` | | Function that will validate all Form Fields. It takes two parameters, a `success` callback and an optional `error` callback.
|| `success` | Will be called if there are no validation errors. Receives the `formData` as it's first argument.
|| `error?` | Will be called if there are validation errors. Receives no arguments.
`add(pathToArray, value)` || Utility function for writing dynamic Forms. It takes two parameters, a `pathToArray` of type `(string \| number)[]` and a `value`.
|| `pathToArray` | Tupel of `string` and `numbers` representing the path to an array in the `formData`.
|| `value` | The `value` that will be pushed to the array at the given path.
`remove(pathToArray, index)` || Identical to `add` but instead of providing a `value` you provide an `index` that will be removed.
### `useValidation` exposes the following methods:
- `validateFields() -> Promise`
- **Description** - Validate all Form Fields.
- **Returns** - A `Promise` which will reject if there are validation errors, and resolve with the `formData` otherwise.
- `resetFields() -> void`
- **Description** - Reset all Form Fields to their original values.
- `add(pathToArray: (string | number)[], value: any) -> void`
- **Description** - Utility function for writing dynamic Forms.
- **Parameters**
- `pathToArray` - Tuple representing the path to an array in the `formData`.
- `value` - The value that will be pushed to the array at the given path.
- `remove(pathToArray: (string | number)[], index: number) -> void`
- **Description** - Utility function for writing dynamic Forms.
- **Parameters**
- `pathToArray` - Tuple representing the path to an array in the `formData`.
- `index` - Array index that will be remove.
## Writing Rules
Rules are functions that should return a `string` when the validation fails. They can be written purely as a function or together with a `key` property in an object.

@@ -132,2 +167,3 @@ They can also alternatively return a `Promise` when you have a rule that requires asynchronous code.

**Typing:**
```ts

@@ -139,27 +175,17 @@ type SimpleRule<T = any> = (value: T) => Promise<unknown> | unknown;

Keyed rules that share the same `key` will be executed together, this can be useful in a situation where rules are dependent on another. For example the `Password` and `Repeat password` fields in a Login Form.
Keyed rules that share the same `key` will be executed together, this can be useful in a situation where rules are dependent on another. For example the `Password` and `Repeat Password` fields in a Login Form.
Rules will always be called with the latest `modelValue`, to determine if a call should result in an error, it will check if the rule's return value is of type `string`.
This allows you to write many rules in one line:
`main/Form.ts`
```ts
// Somewhere at the bottom of the file
let error: unknown;
// ...
error = await rule(formField.modelValue);
// ...
if (typeof error === 'string') {
// report validation error
}
// ...
const required = value => !value && 'This field is required';
const min = value =>
value.length > 3 || 'This field has to be longer than 3 characters';
const max = value =>
value.length < 7 || 'This field is too long (maximum is 6 characters)';
```
This allows you to write many rules in one line:
Async rules allow you to perform network requests, for example checking if a username exists in the database:
```ts
const required = value => !value && 'This field is required';
const min = value => value.length > 3 || 'This field has to be longer than 3 characters';
const max = value => value.length < 7 || 'This field is too long (maximum is 6 characters)';
```
Async rules allow you to perform network requests, for example checking if a username already exists in the database:
```ts
const isNameTaken = name =>

@@ -174,9 +200,11 @@ new Promise(resolve => {

}, 2000);
})
});
```
## Contributing
[Contributing](https://github.com/JensD98/vue3-form-validation/blob/master/.github/contributing.md)
## License
[MIT](https://github.com/JensD98/vue3-form-validation/blob/master/LICENSE)

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc