From afe4f33cd0586d591711d9633f29d505253d4d30 Mon Sep 17 00:00:00 2001 From: okxiaoliang4 <353742991@qq.com> Date: Thu, 14 Apr 2022 01:30:15 +0800 Subject: [PATCH 1/6] feat(useAsyncValidator): new function --- packages/add-ons.md | 1 + packages/contributors.json | 5 +- packages/integrations/README.md | 1 + packages/integrations/index.ts | 1 + packages/integrations/package.json | 10 ++ .../integrations/useAsyncValidator/demo.vue | 50 ++++++++ .../integrations/useAsyncValidator/index.md | 19 +++ .../useAsyncValidator/index.test.ts | 120 ++++++++++++++++++ .../integrations/useAsyncValidator/index.ts | 67 ++++++++++ pnpm-lock.yaml | 70 ++++++---- 10 files changed, 314 insertions(+), 30 deletions(-) create mode 100644 packages/integrations/useAsyncValidator/demo.vue create mode 100644 packages/integrations/useAsyncValidator/index.md create mode 100644 packages/integrations/useAsyncValidator/index.test.ts create mode 100644 packages/integrations/useAsyncValidator/index.ts diff --git a/packages/add-ons.md b/packages/add-ons.md index 343d8456709..79a6f11c6ac 100644 --- a/packages/add-ons.md +++ b/packages/add-ons.md @@ -56,6 +56,7 @@ Utilities for vue-router ## Integrations - [`@vueuse/integrations`](https://vueuse.org/integrations/README.html) Integration wrappers for utility libraries + - [`useAsyncValidator`](https://vueuse.org/integrations/useAsyncValidator/) — wrapper for [`async-validator`](https://github.com/yiminghe/async-validator) - [`useAxios`](https://vueuse.org/integrations/useAxios/) — wrapper for [`axios`](https://github.com/axios/axios) - [`useChangeCase`](https://vueuse.org/integrations/useChangeCase/) — wrapper for [`change-case`](https://github.com/blakeembrey/change-case) - [`useCookies`](https://vueuse.org/integrations/useCookies/) — wrapper for [`universal-cookie`](https://www.npmjs.com/package/universal-cookie) diff --git a/packages/contributors.json b/packages/contributors.json index 93ceb1ce5c6..4214b952b98 100644 --- a/packages/contributors.json +++ b/packages/contributors.json @@ -31,6 +31,7 @@ "marshallswain", "victortolbert", "Kingwl", + "WuLianN", "Talljack", "markthree", "AllenYu0118", @@ -39,6 +40,7 @@ "thisprojectworks", "hikariNTU", "heartbeatLV", + "danielroe", "darrylnoakes", "dblazhkun", "ryanmoyo", @@ -58,7 +60,6 @@ "darkxanter", "Shigma", "TuiMao233", - "WuLianN", "ctholho", "freakzlike", "869288142", @@ -97,7 +98,6 @@ "dospunk", "damienroche", "danmindru", - "danielroe", "DesselBane", "eggsy", "posva", @@ -146,6 +146,7 @@ "mxmvshnvsk", "AldeonMoriak", "MelihAltintas", + "michaelhue", "mdbetancourt", "edimitchel", "MikealGo", diff --git a/packages/integrations/README.md b/packages/integrations/README.md index d56bf62f68d..094e71b7bd2 100644 --- a/packages/integrations/README.md +++ b/packages/integrations/README.md @@ -14,6 +14,7 @@ npm i @vueuse/integrations + - [`useAsyncValidator`](https://vueuse.org/integrations/useAsyncValidator/) — wrapper for [`async-validator`](https://github.com/yiminghe/async-validator) - [`useAxios`](https://vueuse.org/integrations/useAxios/) — wrapper for [`axios`](https://github.com/axios/axios) - [`useChangeCase`](https://vueuse.org/integrations/useChangeCase/) — wrapper for [`change-case`](https://github.com/blakeembrey/change-case) - [`useCookies`](https://vueuse.org/integrations/useCookies/) — wrapper for [`universal-cookie`](https://www.npmjs.com/package/universal-cookie) diff --git a/packages/integrations/index.ts b/packages/integrations/index.ts index 03f46943d6e..9e0711e2fb6 100644 --- a/packages/integrations/index.ts +++ b/packages/integrations/index.ts @@ -1,3 +1,4 @@ +export * from './useAsyncValidator' export * from './useAxios' export * from './useChangeCase' export * from './useCookies' diff --git a/packages/integrations/package.json b/packages/integrations/package.json index 6c3f633feb3..51e3cdaad98 100644 --- a/packages/integrations/package.json +++ b/packages/integrations/package.json @@ -31,6 +31,11 @@ "types": "./index.d.ts" }, "./*": "./*", + "./useAsyncValidator": { + "import": "./useAsyncValidator.mjs", + "require": "./useAsyncValidator.cjs", + "types": "./useAsyncValidator.d.ts" + }, "./useAxios": { "import": "./useAxios.mjs", "require": "./useAxios.cjs", @@ -84,6 +89,7 @@ }, "sideEffects": false, "peerDependencies": { + "async-validator": "*", "axios": "*", "change-case": "*", "drauu": "*", @@ -98,6 +104,9 @@ "axios": { "optional": true }, + "async-validator": { + "optional": true + }, "drauu": { "optional": true }, @@ -128,6 +137,7 @@ "devDependencies": { "@types/nprogress": "^0.2.0", "@types/qrcode": "^1.4.2", + "async-validator": "^4.0.7", "axios": "^0.26.1", "change-case": "^4.1.2", "drauu": "^0.3.0", diff --git a/packages/integrations/useAsyncValidator/demo.vue b/packages/integrations/useAsyncValidator/demo.vue new file mode 100644 index 00000000000..1c595952e60 --- /dev/null +++ b/packages/integrations/useAsyncValidator/demo.vue @@ -0,0 +1,50 @@ + + + diff --git a/packages/integrations/useAsyncValidator/index.md b/packages/integrations/useAsyncValidator/index.md new file mode 100644 index 00000000000..ac3c9633e8f --- /dev/null +++ b/packages/integrations/useAsyncValidator/index.md @@ -0,0 +1,19 @@ +--- +category: '@Integrations' +--- + +# useAsyncValidator + +wrapper for [`async-validator`](https://github.com/yiminghe/async-validator) + +## Install + +```bash +npm i async-validator +``` + +## Usage + +```ts +import { useAsyncValidator } from '@vueuse/integrations/useAsyncValidator' +``` diff --git a/packages/integrations/useAsyncValidator/index.test.ts b/packages/integrations/useAsyncValidator/index.test.ts new file mode 100644 index 00000000000..8a437cca008 --- /dev/null +++ b/packages/integrations/useAsyncValidator/index.test.ts @@ -0,0 +1,120 @@ +import type { Rules } from 'async-validator' +import type { Ref } from 'vue-demi' +import { ref } from 'vue-demi' +import { useAsyncValidator } from '.' + +describe('useAsyncValidator', () => { + let form: { + name: string + age: number + } + + beforeEach(() => { + form = { + name: 'jelf', + age: 24, + } + }) + + it('should be defined', () => { + expect(useAsyncValidator).toBeDefined() + }) + + it('should pass', () => { + const rules: Rules = { + name: { + type: 'string', + }, + age: { + type: 'number', + }, + } + const { pass, error, isFinished, then } = useAsyncValidator(form, rules) + then(() => { + expect(isFinished.value).toBe(true) + expect(pass.value).toBe(true) + expect(error.value).toMatchObject([]) + }) + }) + + it('should async', async() => { + const rules: Rules = { + name: { + type: 'string', + }, + age: { + type: 'number', + }, + } + const { pass, error, isFinished, then } = useAsyncValidator(form, rules) + expect(isFinished.value).toBe(false) + expect(pass.value).toBe(false) + expect(error.value).toMatchObject([]) + + then(() => { + expect(isFinished.value).toBe(true) + expect(pass.value).toBe(true) + expect(error.value).toMatchObject([]) + }) + }) + + it('should can be await', async() => { + const rules: Rules = { + name: { + type: 'string', + }, + age: { + type: 'number', + }, + } + const { pass, error, isFinished } = await useAsyncValidator(form, rules) + expect(isFinished.value).toBe(true) + expect(pass.value).toBe(true) + expect(error.value).toMatchObject([]) + }) + + it('should fail to validate', async() => { + const rules: Rules = { + name: { + type: 'string', + min: 5, + max: 20, + message: 'name length must be 5-20', + }, + age: { + type: 'number', + }, + } + const { pass, error, isFinished } = await useAsyncValidator(form, rules) + expect(isFinished.value).toBe(true) + expect(pass.value).toBe(false) + expect(error.value).toMatchInlineSnapshot('[Error: Async Validation Error]') + }) + + it('should reactive', async() => { + const form = ref({ + name: 'jelf', + age: 24, + }) + + const rules = ref({ + name: { + type: 'string', + min: 5, + max: 20, + message: 'name length must be 5-20', + }, + age: { + type: 'number', + }, + }) as Ref + + const { pass, error, isFinished } = await useAsyncValidator(form, rules) + expect(isFinished.value).toBe(true) + expect(pass.value).toBe(false) + expect(error.value).toMatchInlineSnapshot('[Error: Async Validation Error]') + + form.value.name = 'okxiaoliang4' + expect(isFinished.value).toBe(false) + }) +}) diff --git a/packages/integrations/useAsyncValidator/index.ts b/packages/integrations/useAsyncValidator/index.ts new file mode 100644 index 00000000000..7d897b89514 --- /dev/null +++ b/packages/integrations/useAsyncValidator/index.ts @@ -0,0 +1,67 @@ +import type { MaybeRef } from '@vueuse/shared' +import { until } from '@vueuse/shared' +import Schema from 'async-validator' +import type { Rules, ValidateError } from 'async-validator' +import type { Ref } from 'vue-demi' +import { ref, unref, watchEffect } from 'vue-demi' + +type AsyncValidatorError = Error & { + errors: ValidateError[] + fields: Record +} + +interface UseAsyncValidatorReturn { + pass: Ref + error: Ref + isFinished: Ref +} + +/** + * Wrapper for async-validator. + * + * @see https://vueuse.org/useAsyncValidator + */ +export function useAsyncValidator(value: MaybeRef>, rules: MaybeRef): UseAsyncValidatorReturn & PromiseLike { + const error = ref() + const isFinished = ref(false) + const pass = ref(false) + + watchEffect(async() => { + isFinished.value = false + const validator = new Schema(unref(rules)) + try { + await validator.validate(unref(value)) + pass.value = true + error.value = null + } + catch (err) { + error.value = err as AsyncValidatorError + pass.value = false + } + finally { + isFinished.value = true + } + }) + + const shell = { + pass, + isFinished, + error, + } as UseAsyncValidatorReturn + + function waitUntilFinished() { + return new Promise((resolve, reject) => { + until(isFinished).toBe(true) + .then(() => resolve(shell)) + .catch(error => reject(error)) + }) + } + + return { + ...shell, + then(onFulfilled, onRejected) { + return waitUntilFinished() + .then(onFulfilled, onRejected) + }, + } +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 64c27af0d4d..3fc25ca7ba3 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -177,6 +177,7 @@ importers: '@types/qrcode': ^1.4.2 '@vueuse/core': workspace:* '@vueuse/shared': workspace:* + async-validator: ^4.0.7 axios: ^0.26.1 change-case: ^4.1.2 drauu: ^0.3.0 @@ -194,6 +195,7 @@ importers: devDependencies: '@types/nprogress': 0.2.0 '@types/qrcode': 1.4.2 + async-validator: 4.0.7 axios: 0.26.1 change-case: 4.1.2 drauu: 0.3.0 @@ -216,13 +218,13 @@ importers: local-pkg: ^0.4.1 vue-demi: 0.12.5 dependencies: - '@nuxt/kit': /@nuxt/kit-edge/3.0.0-27487836.540e23c_rollup@2.70.1+vite@2.9.1 + '@nuxt/kit': /@nuxt/kit-edge/3.0.0-27497374.59593a0_rollup@2.70.1+vite@2.9.1 '@vueuse/core': link:../core '@vueuse/metadata': link:../metadata local-pkg: 0.4.1 vue-demi: 0.12.5_ac2a42527c362575429f172d82b9d821 devDependencies: - '@nuxt/schema': /@nuxt/schema-edge/3.0.0-27487836.540e23c_rollup@2.70.1+vite@2.9.1 + '@nuxt/schema': /@nuxt/schema-edge/3.0.0-27497374.59593a0_rollup@2.70.1+vite@2.9.1 packages/router: specifiers: @@ -2314,12 +2316,12 @@ packages: '@nodelib/fs.scandir': 2.1.5 fastq: 1.13.0 - /@nuxt/kit-edge/3.0.0-27487836.540e23c_rollup@2.70.1+vite@2.9.1: - resolution: {integrity: sha512-bEDA95CLfyzFLU1wqPp5HOu7hNl87scUOyihesNBAgHdYzGU55YSZndu2LIBPt1zFnY2Z3wIjt0uC0AfqWsh7g==} + /@nuxt/kit-edge/3.0.0-27497374.59593a0_rollup@2.70.1+vite@2.9.1: + resolution: {integrity: sha512-YivYgL8gaZXSJhKlxZHF5qfQacA55CYVAJidJee4kuOM122ZIOdHf3kVTtXfhBu4dkczLbwpSK44cdZ0eGhzKA==} engines: {node: ^14.16.0 || ^16.11.0 || ^17.0.0} dependencies: - '@nuxt/schema': /@nuxt/schema-edge/3.0.0-27487836.540e23c_rollup@2.70.1+vite@2.9.1 - c12: 0.2.4 + '@nuxt/schema': /@nuxt/schema-edge/3.0.0-27497374.59593a0_rollup@2.70.1+vite@2.9.1 + c12: 0.2.5 consola: 2.15.3 defu: 6.0.0 globby: 13.1.1 @@ -2332,7 +2334,7 @@ packages: pathe: 0.2.0 pkg-types: 0.3.2 scule: 0.2.1 - semver: 7.3.5 + semver: 7.3.7 unctx: 1.1.4_rollup@2.70.1+vite@2.9.1 unimport: 0.1.4_rollup@2.70.1+vite@2.9.1 untyped: 0.4.4 @@ -2344,11 +2346,11 @@ packages: - webpack dev: false - /@nuxt/schema-edge/3.0.0-27487836.540e23c_rollup@2.70.1+vite@2.9.1: - resolution: {integrity: sha512-89kdaJWbhx97EbQEWFE8N5/Tohgd/ktxN7HBveA56KyIbeyR/IiVrOK0eMcc7DCmklEaQi01mrm/su7yf2X1Qw==} + /@nuxt/schema-edge/3.0.0-27497374.59593a0_rollup@2.70.1+vite@2.9.1: + resolution: {integrity: sha512-YokNDkFV1n6RZMFCP34L2KwoEc+Jj7JRvYpcb8g/ILNnfPy1apXGZKW/xVW2+bOpIZGUqo34KbZgZTFQ/9RJtA==} engines: {node: ^14.16.0 || ^16.11.0 || ^17.0.0} dependencies: - c12: 0.2.4 + c12: 0.2.5 create-require: 1.1.1 defu: 6.0.0 jiti: 1.13.0 @@ -3266,6 +3268,10 @@ packages: engines: {node: '>=8'} dev: true + /async-validator/4.0.7: + resolution: {integrity: sha512-Pj2IR7u8hmUEDOwB++su6baaRi+QvsgajuFB9j95foM1N2gy5HM4z60hfusIO0fBPG5uLAEl6yCJr1jNSVugEQ==} + dev: true + /async/0.9.2: resolution: {integrity: sha1-rqdNXmHB+JlhO/ZL2mbUx48v0X0=} dev: true @@ -3438,16 +3444,16 @@ packages: semver: 7.3.5 dev: true - /c12/0.2.4: - resolution: {integrity: sha512-xI2RSKS+DFzR7tMnRv7QtVO6W716PVDzAXVPpAM/NsMZbqQ17jpqcKhTmNF9X/WpeNvo6wOdFViS1GRUF7Qyrg==} + /c12/0.2.5: + resolution: {integrity: sha512-45/GsCYd63XHFgg7xq59eeR8hAbCDoj6w+kQWal9Vb8oGzMRDrHqpZexVNIL4DgeUduvCnBB+7smHiRHVAwBkQ==} dependencies: - defu: 5.0.1 - dotenv: 14.3.2 + defu: 6.0.0 + dotenv: 16.0.0 gittar: 0.1.1 jiti: 1.13.0 - mlly: 0.4.3 + mlly: 0.5.1 pathe: 0.2.0 - rc9: 1.2.0 + rc9: 1.2.2 /cac/6.7.12: resolution: {integrity: sha512-rM7E2ygtMkJqD9c7WnFU6fruFcN3xe4FM5yUmgxhZzIKJk4uHl9U/fhwdajGFQbQuv43FAUo1Fe8gX/oIKDeSA==} @@ -4012,11 +4018,9 @@ packages: object-keys: 1.1.1 dev: true - /defu/2.0.4: - resolution: {integrity: sha512-G9pEH1UUMxShy6syWk01VQSRVs3CDWtlxtZu7A+NyqjxaCA4gSlWAKDBx6QiUEKezqS8+DUlXLI14Fp05Hmpwg==} - /defu/5.0.1: resolution: {integrity: sha512-EPS1carKg+dkEVy3qNTqIdp2qV7mUP08nIsupfwQpz++slCVRw7qbQyWvSTig+kFPwz2XXp5/kIIkH+CwrJKkQ==} + dev: true /defu/6.0.0: resolution: {integrity: sha512-t2MZGLf1V2rV4VBZbWIaXKdX/mUcYW0n2znQZoADBkGGxYL8EWqCuCZBmJPJ/Yy9fofJkyuuSuo5GSwo0XdEgw==} @@ -4028,6 +4032,10 @@ packages: /destr/1.1.0: resolution: {integrity: sha512-Ev/sqS5AzzDwlpor/5wFCDu0dYMQu/0x2D6XfAsQ0E7uQmamIgYJ6Dppo2T2EOFVkeVYWjc+PCLKaqZZ57qmLg==} + dev: true + + /destr/1.1.1: + resolution: {integrity: sha512-QqkneF8LrYmwATMdnuD2MLI3GHQIcBnG6qFC2q9bSH430VTCDAVjcspPmUaKhPGtAtPAftIUFqY1obQYQuwmbg==} /detect-node/2.1.0: resolution: {integrity: sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==} @@ -4103,8 +4111,8 @@ packages: tslib: 2.3.1 dev: true - /dotenv/14.3.2: - resolution: {integrity: sha512-vwEppIphpFdvaMCaHfCEv9IgwcxMljMw2TnAQBB4VWPvzXQLTb82jwmdOKzlEVUL3gNFT4l4TPKO+Bn+sqcrVQ==} + /dotenv/16.0.0: + resolution: {integrity: sha512-qD9WU0MPM4SWLPJy/r2Be+2WgQj8plChsyrCNQzW/0WjvcJQiKQJ9mH3ZgB3fxbUUxgc/11ZJ0Fi5KiimWGz2Q==} engines: {node: '>=12'} /drauu/0.3.0: @@ -6645,7 +6653,6 @@ packages: /mlly/0.3.19: resolution: {integrity: sha512-zMq5n3cOf4fOzA4WoeulxagbAgMChdev3MgP6K51k7M0u2whTXxupfIY4VVzws4vxkiWhwH1rVQcsw7zDGfRhA==} - dev: false /mlly/0.4.3: resolution: {integrity: sha512-xezyv7hnfFPuiDS3AiJuWs0OxlvooS++3L2lURvmh/1n7UG4O2Ehz9UkwWgg3wyLEPKGVfJLlr2DjjTCl9UJTg==} @@ -6655,7 +6662,6 @@ packages: dependencies: pathe: 0.2.0 pkg-types: 0.3.2 - dev: false /mrmime/1.0.0: resolution: {integrity: sha512-a70zx7zFfVO7XpnQ2IX1Myh9yY4UYvfld/dikWRnsXxbyvMcfz+u6UfgNAtH+k2QqtJuzVpv6eLTx1G2+WKZbQ==} @@ -7122,7 +7128,6 @@ packages: jsonc-parser: 3.0.0 mlly: 0.3.19 pathe: 0.2.0 - dev: false /pluralize/8.0.0: resolution: {integrity: sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==} @@ -7303,11 +7308,11 @@ packages: safe-buffer: 5.2.1 dev: true - /rc9/1.2.0: - resolution: {integrity: sha512-/jknmhG0USFAx5uoKkAKhtG40sONds9RWhFHrP1UzJ3OvVfqFWOypSUpmsQD0fFwAV7YtzHhsn3QNasfAoxgcQ==} + /rc9/1.2.2: + resolution: {integrity: sha512-zbe8+HR2X28eZepAwohuKkebbEsA67h0DO9I7g12QrHa2CQopR9gztOLPIPXXGTvcxeUjAN4wZ+b29t3m/u05g==} dependencies: - defu: 2.0.4 - destr: 1.1.0 + defu: 6.0.0 + destr: 1.1.1 flat: 5.0.2 /react-is/16.13.1: @@ -7633,6 +7638,15 @@ packages: hasBin: true dependencies: lru-cache: 6.0.0 + dev: true + + /semver/7.3.7: + resolution: {integrity: sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==} + engines: {node: '>=10'} + hasBin: true + dependencies: + lru-cache: 6.0.0 + dev: false /sentence-case/3.0.4: resolution: {integrity: sha512-8LS0JInaQMCRoQ7YUytAo/xUu5W2XnQxV2HI/6uM6U7CITS1RqPElr30V6uIqyMKM9lJGRVFy5/4CuzcixNYSg==} From e3c1dbd98976ac635db3c42d23d6cfae371e45ca Mon Sep 17 00:00:00 2001 From: okxiaoliang4 <353742991@qq.com> Date: Fri, 15 Apr 2022 20:50:25 +0800 Subject: [PATCH 2/6] chore(useAsyncValidator): update --- packages/contributors.json | 3 +- packages/integrations/package.json | 5 ++ .../useAsyncValidator/component.ts | 26 ++++++++++ .../integrations/useAsyncValidator/demo.vue | 49 ++++++++++++++----- .../useAsyncValidator/index.test.ts | 22 ++++----- .../integrations/useAsyncValidator/index.ts | 20 +++++--- 6 files changed, 94 insertions(+), 31 deletions(-) create mode 100644 packages/integrations/useAsyncValidator/component.ts diff --git a/packages/contributors.json b/packages/contributors.json index 4214b952b98..d66bd980223 100644 --- a/packages/contributors.json +++ b/packages/contributors.json @@ -90,6 +90,7 @@ "baboon-king", "benlesh", "benatkin", + "blackhu0804", "Bobakanoosh", "MssText", "CharlesOkwuagwu", @@ -149,7 +150,6 @@ "michaelhue", "mdbetancourt", "edimitchel", - "MikealGo", "MinatoHikari", "ElMassimo", "Nicholaiii", @@ -168,6 +168,7 @@ "Rolanddoda", "romansp", "ItsRyanWu", + "stafyniaksacha", "SasanFarrokh", "kshahar", "lishangbu", diff --git a/packages/integrations/package.json b/packages/integrations/package.json index 51e3cdaad98..835b685308d 100644 --- a/packages/integrations/package.json +++ b/packages/integrations/package.json @@ -85,6 +85,11 @@ "import": "./useChangeCase.mjs", "require": "./useChangeCase.cjs", "types": "./useChangeCase.d.ts" + }, + "./useAsyncValidator/component": { + "import": "./useAsyncValidator/component.mjs", + "require": "./useAsyncValidator/component.cjs", + "types": "./useAsyncValidator/component.d.ts" } }, "sideEffects": false, diff --git a/packages/integrations/useAsyncValidator/component.ts b/packages/integrations/useAsyncValidator/component.ts new file mode 100644 index 00000000000..df95e9a4359 --- /dev/null +++ b/packages/integrations/useAsyncValidator/component.ts @@ -0,0 +1,26 @@ +import type { PropType } from 'vue-demi' +import { defineComponent, reactive } from 'vue-demi' +import { useAsyncValidator } from '@vueuse/integrations' +import type { Rules } from 'async-validator' + +export const UseAsyncValidator = defineComponent({ + name: 'UseAsyncValidator', + props: { + form: { + type: Object as PropType>, + required: true, + }, + rules: { + type: Object as PropType, + required: true, + }, + }, + setup(props, { slots }) { + const data = reactive(useAsyncValidator(props.form, props.rules)) + + return () => { + if (slots.default) + return slots.default(data) + } + }, +}) diff --git a/packages/integrations/useAsyncValidator/demo.vue b/packages/integrations/useAsyncValidator/demo.vue index 1c595952e60..9fcc4aeecc7 100644 --- a/packages/integrations/useAsyncValidator/demo.vue +++ b/packages/integrations/useAsyncValidator/demo.vue @@ -1,10 +1,11 @@ diff --git a/packages/integrations/useAsyncValidator/index.test.ts b/packages/integrations/useAsyncValidator/index.test.ts index 8a437cca008..caa26189426 100644 --- a/packages/integrations/useAsyncValidator/index.test.ts +++ b/packages/integrations/useAsyncValidator/index.test.ts @@ -29,11 +29,11 @@ describe('useAsyncValidator', () => { type: 'number', }, } - const { pass, error, isFinished, then } = useAsyncValidator(form, rules) + const { pass, errors, isFinished, then } = useAsyncValidator(form, rules) then(() => { expect(isFinished.value).toBe(true) expect(pass.value).toBe(true) - expect(error.value).toMatchObject([]) + expect(errors.value).toMatchObject([]) }) }) @@ -46,15 +46,15 @@ describe('useAsyncValidator', () => { type: 'number', }, } - const { pass, error, isFinished, then } = useAsyncValidator(form, rules) + const { pass, errors, isFinished, then } = useAsyncValidator(form, rules) expect(isFinished.value).toBe(false) expect(pass.value).toBe(false) - expect(error.value).toMatchObject([]) + expect(errors.value).toMatchObject([]) then(() => { expect(isFinished.value).toBe(true) expect(pass.value).toBe(true) - expect(error.value).toMatchObject([]) + expect(errors.value).toMatchObject([]) }) }) @@ -67,10 +67,10 @@ describe('useAsyncValidator', () => { type: 'number', }, } - const { pass, error, isFinished } = await useAsyncValidator(form, rules) + const { pass, errors, isFinished } = await useAsyncValidator(form, rules) expect(isFinished.value).toBe(true) expect(pass.value).toBe(true) - expect(error.value).toMatchObject([]) + expect(errors.value).toMatchObject([]) }) it('should fail to validate', async() => { @@ -85,10 +85,10 @@ describe('useAsyncValidator', () => { type: 'number', }, } - const { pass, error, isFinished } = await useAsyncValidator(form, rules) + const { pass, errors, isFinished } = await useAsyncValidator(form, rules) expect(isFinished.value).toBe(true) expect(pass.value).toBe(false) - expect(error.value).toMatchInlineSnapshot('[Error: Async Validation Error]') + expect(errors.value).toMatchInlineSnapshot('[Error: Async Validation Error]') }) it('should reactive', async() => { @@ -109,10 +109,10 @@ describe('useAsyncValidator', () => { }, }) as Ref - const { pass, error, isFinished } = await useAsyncValidator(form, rules) + const { pass, errors, isFinished } = await useAsyncValidator(form, rules) expect(isFinished.value).toBe(true) expect(pass.value).toBe(false) - expect(error.value).toMatchInlineSnapshot('[Error: Async Validation Error]') + expect(errors.value).toMatchInlineSnapshot('[Error: Async Validation Error]') form.value.name = 'okxiaoliang4' expect(isFinished.value).toBe(false) diff --git a/packages/integrations/useAsyncValidator/index.ts b/packages/integrations/useAsyncValidator/index.ts index 7d897b89514..64cee3ef2ea 100644 --- a/packages/integrations/useAsyncValidator/index.ts +++ b/packages/integrations/useAsyncValidator/index.ts @@ -3,7 +3,7 @@ import { until } from '@vueuse/shared' import Schema from 'async-validator' import type { Rules, ValidateError } from 'async-validator' import type { Ref } from 'vue-demi' -import { ref, unref, watchEffect } from 'vue-demi' +import { computed, ref, unref, watchEffect } from 'vue-demi' type AsyncValidatorError = Error & { errors: ValidateError[] @@ -12,8 +12,10 @@ type AsyncValidatorError = Error & { interface UseAsyncValidatorReturn { pass: Ref - error: Ref + errorInfo: Ref isFinished: Ref + errors: Ref + errorFields: Ref } /** @@ -22,21 +24,23 @@ interface UseAsyncValidatorReturn { * @see https://vueuse.org/useAsyncValidator */ export function useAsyncValidator(value: MaybeRef>, rules: MaybeRef): UseAsyncValidatorReturn & PromiseLike { - const error = ref() + const errorInfo = ref() const isFinished = ref(false) const pass = ref(false) + const errors = computed(() => errorInfo.value?.errors || []) + const errorFields = computed(() => errorInfo.value?.fields || {}) watchEffect(async() => { isFinished.value = false + pass.value = false const validator = new Schema(unref(rules)) try { await validator.validate(unref(value)) pass.value = true - error.value = null + errorInfo.value = null } catch (err) { - error.value = err as AsyncValidatorError - pass.value = false + errorInfo.value = err as AsyncValidatorError } finally { isFinished.value = true @@ -46,7 +50,9 @@ export function useAsyncValidator(value: MaybeRef>, rules: M const shell = { pass, isFinished, - error, + errorInfo, + errors, + errorFields, } as UseAsyncValidatorReturn function waitUntilFinished() { From 0573f25f7a0f69571f31c8b4c36c24d1346c3230 Mon Sep 17 00:00:00 2001 From: okxiaoliang4 <353742991@qq.com> Date: Fri, 15 Apr 2022 21:45:13 +0800 Subject: [PATCH 3/6] test(useAsyncValidator): update --- .../useAsyncValidator/index.test.ts | 23 +++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/packages/integrations/useAsyncValidator/index.test.ts b/packages/integrations/useAsyncValidator/index.test.ts index caa26189426..833ffba016e 100644 --- a/packages/integrations/useAsyncValidator/index.test.ts +++ b/packages/integrations/useAsyncValidator/index.test.ts @@ -1,6 +1,6 @@ import type { Rules } from 'async-validator' import type { Ref } from 'vue-demi' -import { ref } from 'vue-demi' +import { nextTick, ref } from 'vue-demi' import { useAsyncValidator } from '.' describe('useAsyncValidator', () => { @@ -88,7 +88,15 @@ describe('useAsyncValidator', () => { const { pass, errors, isFinished } = await useAsyncValidator(form, rules) expect(isFinished.value).toBe(true) expect(pass.value).toBe(false) - expect(errors.value).toMatchInlineSnapshot('[Error: Async Validation Error]') + expect(errors.value).toMatchInlineSnapshot(` + [ + { + "field": "name", + "fieldValue": "jelf", + "message": "name length must be 5-20", + }, + ] + `) }) it('should reactive', async() => { @@ -112,9 +120,16 @@ describe('useAsyncValidator', () => { const { pass, errors, isFinished } = await useAsyncValidator(form, rules) expect(isFinished.value).toBe(true) expect(pass.value).toBe(false) - expect(errors.value).toMatchInlineSnapshot('[Error: Async Validation Error]') + expect(errors.value).toMatchInlineSnapshot(` + [ + { + "field": "name", + "fieldValue": "jelf", + "message": "name length must be 5-20", + }, + ] + `) form.value.name = 'okxiaoliang4' - expect(isFinished.value).toBe(false) }) }) From 106451f2d7a0493f95cabf727b871fdab99c2b82 Mon Sep 17 00:00:00 2001 From: okxiaoliang4 <353742991@qq.com> Date: Fri, 15 Apr 2022 21:48:11 +0800 Subject: [PATCH 4/6] chore(useAsyncValidator): update --- packages/integrations/useAsyncValidator/index.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/integrations/useAsyncValidator/index.test.ts b/packages/integrations/useAsyncValidator/index.test.ts index 833ffba016e..87453b59cc6 100644 --- a/packages/integrations/useAsyncValidator/index.test.ts +++ b/packages/integrations/useAsyncValidator/index.test.ts @@ -1,6 +1,6 @@ import type { Rules } from 'async-validator' import type { Ref } from 'vue-demi' -import { nextTick, ref } from 'vue-demi' +import { ref } from 'vue-demi' import { useAsyncValidator } from '.' describe('useAsyncValidator', () => { From 8ca56b5a819189e7c6cdb007024395c24f204d6a Mon Sep 17 00:00:00 2001 From: Jelf <353742991@qq.com> Date: Mon, 16 May 2022 14:46:55 +0800 Subject: [PATCH 5/6] chore: fix eslint --- packages/integrations/useAsyncValidator/index.test.ts | 8 ++++---- packages/integrations/useAsyncValidator/index.ts | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/integrations/useAsyncValidator/index.test.ts b/packages/integrations/useAsyncValidator/index.test.ts index 87453b59cc6..38a3cd9ce75 100644 --- a/packages/integrations/useAsyncValidator/index.test.ts +++ b/packages/integrations/useAsyncValidator/index.test.ts @@ -37,7 +37,7 @@ describe('useAsyncValidator', () => { }) }) - it('should async', async() => { + it('should async', async () => { const rules: Rules = { name: { type: 'string', @@ -58,7 +58,7 @@ describe('useAsyncValidator', () => { }) }) - it('should can be await', async() => { + it('should can be await', async () => { const rules: Rules = { name: { type: 'string', @@ -73,7 +73,7 @@ describe('useAsyncValidator', () => { expect(errors.value).toMatchObject([]) }) - it('should fail to validate', async() => { + it('should fail to validate', async () => { const rules: Rules = { name: { type: 'string', @@ -99,7 +99,7 @@ describe('useAsyncValidator', () => { `) }) - it('should reactive', async() => { + it('should reactive', async () => { const form = ref({ name: 'jelf', age: 24, diff --git a/packages/integrations/useAsyncValidator/index.ts b/packages/integrations/useAsyncValidator/index.ts index 64cee3ef2ea..5f89111dd65 100644 --- a/packages/integrations/useAsyncValidator/index.ts +++ b/packages/integrations/useAsyncValidator/index.ts @@ -30,7 +30,7 @@ export function useAsyncValidator(value: MaybeRef>, rules: M const errors = computed(() => errorInfo.value?.errors || []) const errorFields = computed(() => errorInfo.value?.fields || {}) - watchEffect(async() => { + watchEffect(async () => { isFinished.value = false pass.value = false const validator = new Schema(unref(rules)) From 9a6cd75948b4bd675194f1ed7bc9ab3803e9eae2 Mon Sep 17 00:00:00 2001 From: Anthony Fu Date: Tue, 31 May 2022 20:44:31 +0800 Subject: [PATCH 6/6] chore: lint --- packages/integrations/package.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/integrations/package.json b/packages/integrations/package.json index d88638a382f..1c1b568f1c1 100644 --- a/packages/integrations/package.json +++ b/packages/integrations/package.json @@ -28,9 +28,9 @@ }, "./*": "./*", "./useAsyncValidator": { + "types": "./useAsyncValidator.d.ts", "import": "./useAsyncValidator.mjs", - "require": "./useAsyncValidator.cjs", - "types": "./useAsyncValidator.d.ts" + "require": "./useAsyncValidator.cjs" }, "./useAxios": { "types": "./useAxios.d.ts", @@ -83,9 +83,9 @@ "require": "./useChangeCase.cjs" }, "./useAsyncValidator/component": { + "types": "./useAsyncValidator/component.d.ts", "import": "./useAsyncValidator/component.mjs", - "require": "./useAsyncValidator/component.cjs", - "types": "./useAsyncValidator/component.d.ts" + "require": "./useAsyncValidator/component.cjs" } }, "main": "./index.cjs",