Skip to content

Commit

Permalink
feat(useAsyncValidator): new function (#1497)
Browse files Browse the repository at this point in the history
Co-authored-by: Anthony Fu <anthonyfu117@hotmail.com>
  • Loading branch information
okxiaoliang4 and antfu committed May 31, 2022
1 parent 5a1b932 commit f422638
Show file tree
Hide file tree
Showing 10 changed files with 352 additions and 0 deletions.
1 change: 1 addition & 0 deletions packages/add-ons.md
Expand Up @@ -69,6 +69,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)
Expand Down
1 change: 1 addition & 0 deletions packages/integrations/README.md
Expand Up @@ -14,6 +14,7 @@ npm i <b>@vueuse/integrations</b>

<!--GENERATED LIST, DO NOT MODIFY MANUALLY-->
<!--FUNCTIONS_LIST_STARTS-->
- [`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)
Expand Down
1 change: 1 addition & 0 deletions packages/integrations/index.ts
@@ -1,3 +1,4 @@
export * from './useAsyncValidator'
export * from './useAxios'
export * from './useChangeCase'
export * from './useCookies'
Expand Down
15 changes: 15 additions & 0 deletions packages/integrations/package.json
Expand Up @@ -27,6 +27,11 @@
"require": "./index.cjs"
},
"./*": "./*",
"./useAsyncValidator": {
"types": "./useAsyncValidator.d.ts",
"import": "./useAsyncValidator.mjs",
"require": "./useAsyncValidator.cjs"
},
"./useAxios": {
"types": "./useAxios.d.ts",
"import": "./useAxios.mjs",
Expand Down Expand Up @@ -76,6 +81,11 @@
"types": "./useChangeCase.d.ts",
"import": "./useChangeCase.mjs",
"require": "./useChangeCase.cjs"
},
"./useAsyncValidator/component": {
"types": "./useAsyncValidator/component.d.ts",
"import": "./useAsyncValidator/component.mjs",
"require": "./useAsyncValidator/component.cjs"
}
},
"main": "./index.cjs",
Expand All @@ -84,6 +94,7 @@
"jsdelivr": "./index.iife.min.js",
"types": "./index.d.ts",
"peerDependencies": {
"async-validator": "*",
"axios": "*",
"change-case": "*",
"drauu": "*",
Expand All @@ -98,6 +109,9 @@
"axios": {
"optional": true
},
"async-validator": {
"optional": true
},
"change-case": {
"optional": true
},
Expand Down Expand Up @@ -131,6 +145,7 @@
"devDependencies": {
"@types/nprogress": "^0.2.0",
"@types/qrcode": "^1.4.2",
"async-validator": "^4.0.7",
"axios": "^0.27.2",
"change-case": "^4.1.2",
"drauu": "^0.3.0",
Expand Down
26 changes: 26 additions & 0 deletions 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<Record<string, any>>,
required: true,
},
rules: {
type: Object as PropType<Rules>,
required: true,
},
},
setup(props, { slots }) {
const data = reactive(useAsyncValidator(props.form, props.rules))

return () => {
if (slots.default)
return slots.default(data)
}
},
})
75 changes: 75 additions & 0 deletions packages/integrations/useAsyncValidator/demo.vue
@@ -0,0 +1,75 @@
<script setup lang="ts">
import { reactive } from 'vue'
import type { Rules } from 'async-validator'
import { UseAsyncValidator } from './component'
import { useAsyncValidator } from '.'
const form = reactive({ email: '', name: '', age: '' })
const rules: Rules = {
name: {
type: 'string',
min: 5,
max: 20,
required: true,
},
age: {
type: 'number',
required: true,
},
email: [
{
type: 'email',
required: true,
},
],
}
const { pass, isFinished, errorFields } = useAsyncValidator(form, rules)
</script>

<template>
<div>
pass:
<BooleanDisplay :value="pass" />
</div>
<div>
isFinished:
<BooleanDisplay :value="isFinished" />
</div>

<div class="bg-base border-main rounded shadow max-w-96 p-8">
<div>
email:
<input
v-model="form.email" :class="{ '!border-red': errorFields.email?.length > 0 }" type="text"
placeholder="email"
>
<div v-if="errorFields.email?.length > 0" text-red>
{{ errorFields.email[0].message }}
</div>
</div>
<div>
name:
<input
v-model="form.name" :class="{ '!border-red': errorFields.name?.length > 0 }" type="text"
placeholder="name"
>
<div v-if="errorFields.name?.length > 0" text-red>
{{ errorFields.name[0].message }}
</div>
</div>
<div>
age:
<input
v-model="form.age" :class="{ '!border-red': errorFields.age?.length > 0 }" type="number"
placeholder="age"
>
<div v-if="errorFields.age?.length > 0" text-red>
{{ errorFields.age[0].message }}
</div>
</div>
<button :disabled="!pass">
submit
</button>
</div>
</template>
19 changes: 19 additions & 0 deletions 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'
```
135 changes: 135 additions & 0 deletions packages/integrations/useAsyncValidator/index.test.ts
@@ -0,0 +1,135 @@
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, errors, isFinished, then } = useAsyncValidator(form, rules)
then(() => {
expect(isFinished.value).toBe(true)
expect(pass.value).toBe(true)
expect(errors.value).toMatchObject([])
})
})

it('should async', async () => {
const rules: Rules = {
name: {
type: 'string',
},
age: {
type: 'number',
},
}
const { pass, errors, isFinished, then } = useAsyncValidator(form, rules)
expect(isFinished.value).toBe(false)
expect(pass.value).toBe(false)
expect(errors.value).toMatchObject([])

then(() => {
expect(isFinished.value).toBe(true)
expect(pass.value).toBe(true)
expect(errors.value).toMatchObject([])
})
})

it('should can be await', async () => {
const rules: Rules = {
name: {
type: 'string',
},
age: {
type: 'number',
},
}
const { pass, errors, isFinished } = await useAsyncValidator(form, rules)
expect(isFinished.value).toBe(true)
expect(pass.value).toBe(true)
expect(errors.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, errors, isFinished } = await useAsyncValidator(form, rules)
expect(isFinished.value).toBe(true)
expect(pass.value).toBe(false)
expect(errors.value).toMatchInlineSnapshot(`
[
{
"field": "name",
"fieldValue": "jelf",
"message": "name length must be 5-20",
},
]
`)
})

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<Rules>

const { pass, errors, isFinished } = await useAsyncValidator(form, rules)
expect(isFinished.value).toBe(true)
expect(pass.value).toBe(false)
expect(errors.value).toMatchInlineSnapshot(`
[
{
"field": "name",
"fieldValue": "jelf",
"message": "name length must be 5-20",
},
]
`)

form.value.name = 'okxiaoliang4'
})
})

0 comments on commit f422638

Please sign in to comment.