Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: logaretm/vee-validate
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: v4.6.6
Choose a base ref
...
head repository: logaretm/vee-validate
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: v4.6.7
Choose a head ref
  • 3 commits
  • 17 files changed
  • 2 contributors

Commits on Aug 27, 2022

  1. Copy the full SHA
    91e97aa View commit details
  2. Copy the full SHA
    8c82079 View commit details
  3. chore(release): publish

    logaretm committed Aug 27, 2022
    Copy the full SHA
    3c9d79f View commit details
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -3,6 +3,13 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.

## [4.6.7](https://github.com/logaretm/vee-validate/compare/v4.6.6...v4.6.7) (2022-08-27)

### Bug Fixes

* allow generics for generic function type ([91e97aa](https://github.com/logaretm/vee-validate/commit/91e97aa41bca278970780973fcbf90e17fb29920))
* handle validation races for async validations ([#3908](https://github.com/logaretm/vee-validate/issues/3908)) ([8c82079](https://github.com/logaretm/vee-validate/commit/8c82079dac8535678e45428ad8e5afe7dcd3da63))

## [4.6.6](https://github.com/logaretm/vee-validate/compare/v4.6.5...v4.6.6) (2022-08-16)

### Bug Fixes
2 changes: 1 addition & 1 deletion lerna.json
Original file line number Diff line number Diff line change
@@ -3,7 +3,7 @@
"packages/*"
],
"npmClient": "yarn",
"version": "4.6.6",
"version": "4.6.7",
"useWorkspaces": true,
"command": {
"version": {
4 changes: 4 additions & 0 deletions packages/i18n/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -3,6 +3,10 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.

## [4.6.7](https://github.com/logaretm/vee-validate/compare/v4.6.6...v4.6.7) (2022-08-27)

**Note:** Version bump only for package @vee-validate/i18n

## [4.6.6](https://github.com/logaretm/vee-validate/compare/v4.6.5...v4.6.6) (2022-08-16)

**Note:** Version bump only for package @vee-validate/i18n
2 changes: 1 addition & 1 deletion packages/i18n/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@vee-validate/i18n",
"version": "4.6.6",
"version": "4.6.7",
"description": "Localization module for VeeValidate",
"author": "Abdelrahman Awad <logaretm1@gmail.com>",
"homepage": "https://vee-validate.logaretm.com/v4/guide/i18n",
4 changes: 4 additions & 0 deletions packages/rules/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -3,6 +3,10 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.

## [4.6.7](https://github.com/logaretm/vee-validate/compare/v4.6.6...v4.6.7) (2022-08-27)

**Note:** Version bump only for package @vee-validate/rules

## [4.6.6](https://github.com/logaretm/vee-validate/compare/v4.6.5...v4.6.6) (2022-08-16)

**Note:** Version bump only for package @vee-validate/rules
2 changes: 1 addition & 1 deletion packages/rules/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@vee-validate/rules",
"version": "4.6.6",
"version": "4.6.7",
"description": "Form Validation for Vue.js",
"author": "Abdelrahman Awad <logaretm1@gmail.com>",
"license": "MIT",
7 changes: 7 additions & 0 deletions packages/vee-validate/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -3,6 +3,13 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.

## [4.6.7](https://github.com/logaretm/vee-validate/compare/v4.6.6...v4.6.7) (2022-08-27)

### Bug Fixes

* allow generics for generic function type ([91e97aa](https://github.com/logaretm/vee-validate/commit/91e97aa41bca278970780973fcbf90e17fb29920))
* handle validation races for async validations ([#3908](https://github.com/logaretm/vee-validate/issues/3908)) ([8c82079](https://github.com/logaretm/vee-validate/commit/8c82079dac8535678e45428ad8e5afe7dcd3da63))

## [4.6.6](https://github.com/logaretm/vee-validate/compare/v4.6.5...v4.6.6) (2022-08-16)

### Bug Fixes
2 changes: 1 addition & 1 deletion packages/vee-validate/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "vee-validate",
"version": "4.6.6",
"version": "4.6.7",
"description": "Form Validation for Vue.js",
"author": "Abdelrahman Awad <logaretm1@gmail.com>",
"license": "MIT",
4 changes: 2 additions & 2 deletions packages/vee-validate/src/types.ts
Original file line number Diff line number Diff line change
@@ -109,8 +109,8 @@ export interface PrivateFieldContext<TValue = unknown> {

export type FieldContext<TValue = unknown> = Omit<PrivateFieldContext<TValue>, 'id' | 'instances'>;

export type GenericValidateFunction = (
value: unknown,
export type GenericValidateFunction<TValue = unknown> = (
value: TValue,
ctx: FieldValidationMetaInfo
) => boolean | string | Promise<boolean | string>;

62 changes: 34 additions & 28 deletions packages/vee-validate/src/useField.ts
Original file line number Diff line number Diff line change
@@ -36,6 +36,7 @@ import {
resolveNextCheckboxValue,
isYupValidator,
applyModelModifiers,
withLatest,
} from './utils';
import { isCallable } from '../../shared';
import { FieldContextKey, FormContextKey, IS_ABSENT } from './symbols';
@@ -61,8 +62,8 @@ export interface FieldOptions<TValue = unknown> {
export type RuleExpression<TValue> =
| string
| Record<string, unknown>
| GenericValidateFunction
| GenericValidateFunction[]
| GenericValidateFunction<TValue>
| GenericValidateFunction<TValue>[]
| YupValidator<TValue>
| undefined;

@@ -147,42 +148,47 @@ function _useField<TValue = unknown>(
});
}

async function validateWithStateMutation(): Promise<ValidationResult> {
meta.pending = true;
meta.validated = true;
const result = await validateCurrentValue('validated-only');
if (markedForRemoval) {
result.valid = true;
result.errors = [];
}
const validateWithStateMutation = withLatest(
async () => {
meta.pending = true;
meta.validated = true;

return validateCurrentValue('validated-only');
},
result => {
if (markedForRemoval) {
result.valid = true;
result.errors = [];
}

setState({ errors: result.errors });
meta.pending = false;
setState({ errors: result.errors });
meta.pending = false;

return result;
}

async function validateValidStateOnly(): Promise<ValidationResult> {
const result = await validateCurrentValue('silent');
if (markedForRemoval) {
result.valid = true;
return result;
}
);

meta.valid = result.valid;
const validateValidStateOnly = withLatest(
async () => {
return validateCurrentValue('silent');
},
result => {
if (markedForRemoval) {
result.valid = true;
}

return result;
}
meta.valid = result.valid;

function validate(opts?: Partial<ValidationOptions>) {
if (!opts?.mode || opts?.mode === 'force') {
return validateWithStateMutation();
return result;
}
);

if (opts?.mode === 'validated-only') {
return validateWithStateMutation();
function validate(opts?: Partial<ValidationOptions>) {
if (opts?.mode === 'silent') {
return validateValidStateOnly();
}

return validateValidStateOnly();
return validateWithStateMutation();
}

// Common input/change event handler
125 changes: 65 additions & 60 deletions packages/vee-validate/src/useForm.ts
Original file line number Diff line number Diff line change
@@ -46,6 +46,7 @@ import {
isFormSubmitEvent,
debounceAsync,
isEmptyContainer,
withLatest,
} from './utils';
import { FormContextKey } from './symbols';
import { validateYupSchema, validateObjectSchema } from './validate';
@@ -156,6 +157,70 @@ export function useForm<TValues extends Record<string, any> = Record<string, any
const meta = useFormMeta(fieldsByPath, formValues, originalInitialValues, errors);

const schema = opts?.validationSchema;

/**
* Batches validation runs in 5ms batches
* Must have two distinct batch queues to make sure they don't override each other settings #3783
*/
const debouncedSilentValidation = debounceAsync(_validateSchema, 5);
const debouncedValidation = debounceAsync(_validateSchema, 5);

const validateSchema = withLatest(
async (mode: SchemaValidationMode) => {
return (await mode) === 'silent' ? debouncedSilentValidation() : debouncedValidation();
},
(formResult, [mode]) => {
// fields by id lookup
const fieldsById = formCtx.fieldsByPath.value || {};
// errors fields names, we need it to also check if custom errors are updated
const currentErrorsPaths = keysOf(formCtx.errorBag.value);
// collect all the keys from the schema and all fields
// this ensures we have a complete keymap of all the fields
const paths = [
...new Set([...keysOf(formResult.results), ...keysOf(fieldsById), ...currentErrorsPaths]),
] as string[];

// aggregates the paths into a single result object while applying the results on the fields
return paths.reduce(
(validation, path) => {
const field = fieldsById[path];
const messages = (formResult.results[path] || { errors: [] as string[] }).errors;
const fieldResult = {
errors: messages,
valid: !messages.length,
};
validation.results[path as keyof TValues] = fieldResult;
if (!fieldResult.valid) {
validation.errors[path as keyof TValues] = fieldResult.errors[0];
}

// field not rendered
if (!field) {
setFieldError(path, messages);

return validation;
}

// always update the valid flag regardless of the mode
applyFieldMutation(field, f => (f.meta.valid = fieldResult.valid));
if (mode === 'silent') {
return validation;
}

const wasValidated = Array.isArray(field) ? field.some(f => f.meta.validated) : field.meta.validated;
if (mode === 'validated-only' && !wasValidated) {
return validation;
}

applyFieldMutation(field, f => f.setState({ errors: fieldResult.errors }));

return validation;
},
{ valid: formResult.valid, results: {}, errors: {} } as FormValidationResult<TValues>
);
}
);

const formCtx: PrivateFormContext<TValues> = {
formId,
fieldsByPath,
@@ -660,66 +725,6 @@ export function useForm<TValues extends Record<string, any> = Record<string, any
return formResult;
}

/**
* Batches validation runs in 5ms batches
* Must have two distinct batch queues to make sure they don't override each other settings #3783
*/
const debouncedSilentValidation = debounceAsync(_validateSchema, 5);
const debouncedValidation = debounceAsync(_validateSchema, 5);

async function validateSchema(mode: SchemaValidationMode): Promise<FormValidationResult<TValues>> {
const formResult = await (mode === 'silent' ? debouncedSilentValidation() : debouncedValidation());

// fields by id lookup
const fieldsById = formCtx.fieldsByPath.value || {};
// errors fields names, we need it to also check if custom errors are updated
const currentErrorsPaths = keysOf(formCtx.errorBag.value);
// collect all the keys from the schema and all fields
// this ensures we have a complete keymap of all the fields
const paths = [
...new Set([...keysOf(formResult.results), ...keysOf(fieldsById), ...currentErrorsPaths]),
] as string[];

// aggregates the paths into a single result object while applying the results on the fields
return paths.reduce(
(validation, path) => {
const field = fieldsById[path];
const messages = (formResult.results[path] || { errors: [] as string[] }).errors;
const fieldResult = {
errors: messages,
valid: !messages.length,
};
validation.results[path as keyof TValues] = fieldResult;
if (!fieldResult.valid) {
validation.errors[path as keyof TValues] = fieldResult.errors[0];
}

// field not rendered
if (!field) {
setFieldError(path, messages);

return validation;
}

// always update the valid flag regardless of the mode
applyFieldMutation(field, f => (f.meta.valid = fieldResult.valid));
if (mode === 'silent') {
return validation;
}

const wasValidated = Array.isArray(field) ? field.some(f => f.meta.validated) : field.meta.validated;
if (mode === 'validated-only' && !wasValidated) {
return validation;
}

applyFieldMutation(field, f => f.setState({ errors: fieldResult.errors }));

return validation;
},
{ valid: formResult.valid, results: {}, errors: {} } as FormValidationResult<TValues>
);
}

const submitForm = handleSubmit((_, { evt }) => {
if (isFormSubmitEvent(evt)) {
evt.target.submit();
21 changes: 21 additions & 0 deletions packages/vee-validate/src/utils/common.ts
Original file line number Diff line number Diff line change
@@ -244,3 +244,24 @@ export function applyModelModifiers(value: unknown, modifiers: unknown) {

return value;
}

export function withLatest<TFunction extends (...args: any[]) => Promise<any>, TResult = ReturnType<TFunction>>(
fn: TFunction,
onDone: (result: Awaited<TResult>, args: Parameters<TFunction>) => void
) {
let latestRun: Promise<TResult> | undefined;

return async function runLatest(...args: Parameters<TFunction>) {
const pending = fn(...args);
latestRun = pending;
const result = await pending;
if (pending !== latestRun) {
return result;
}

latestRun = undefined;
onDone(result, args);

return result;
};
}
Loading