Skip to content

Commit 9170df2

Browse files
feat($Validation): add field validation
1 parent f890d6f commit 9170df2

File tree

3 files changed

+78
-11
lines changed

3 files changed

+78
-11
lines changed

src/requests/vuetify-demo/index.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,6 @@ import * as Axios from '@/plugins/axios'
33
import { GetDemoPayload } from '@/requests/vuetify-demo/payload/get-demo-payload'
44

55
export const vuetifyDemoApi = {
6-
getDemo: (getDemoPayload: GetDemoPayload) => Axios.get('/api/get', getDemoPayload)
6+
getDemo: (getDemoPayload: GetDemoPayload) => Axios.get('/api/getDemo', getDemoPayload),
7+
delay: (delayTime: number) => Axios.get('/api/delay', delayTime)
78
}

src/shims-tsx.d.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,3 +16,9 @@ declare global {
1616
}
1717
}
1818
}
19+
20+
/**
21+
* A better solution would be to create a type with the intersection so that it can be reused across multiple components.
22+
* @see <a href='https://stackoverflow.com/questions/52109471/typescript-in-vue-property-validate-does-not-exist-on-type-vue-element'>Stack Overflow</a>
23+
*/
24+
export type VForm = Vue & { validate: () => boolean }

src/views/VuetifyDemo.vue

Lines changed: 70 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -118,11 +118,12 @@
118118
</v-container>
119119
<v-form ref="form" v-model="formValidation">
120120
<v-text-field label="Title" v-model="getDemoPayload.title" :rules="titleRules" clearable/>
121-
<v-text-field label="Text" v-model="getDemoPayload.text" clearable/>
122-
<v-text-field label="Rating" v-model="getDemoPayload.rating"/>
123-
<v-text-field label="Email" v-model="getDemoPayload.email"/>
124-
<v-text-field label="Domain Name" v-model="getDemoPayload.site"/>
125-
<v-btn v-throttled-click="handleThrottledClickRendRequest" small>Send request</v-btn>
121+
<v-text-field label="Text" v-model="getDemoPayload.text" :rules="textRules" clearable/>
122+
<v-text-field label="Rating" v-model="getDemoPayload.rating" :rules="ratingRules"/>
123+
<v-text-field label="Email" v-model="getDemoPayload.email" :rules="emailRules"/>
124+
<v-text-field label="Domain Name" v-model="getDemoPayload.site" :rules="siteRules"/>
125+
<v-btn v-debounced-click:300="handleDebouncedClickRendRequest" :loading="sendRequestLoading" small>Send request
126+
</v-btn>
126127
</v-form>
127128
</v-container>
128129
</div>
@@ -133,9 +134,17 @@ import Vue from 'vue'
133134
import { vuetifyDemoApi } from '@/requests/vuetify-demo'
134135
// eslint-disable-next-line no-unused-vars
135136
import { GetDemoPayload } from '@/requests/vuetify-demo/payload/get-demo-payload'
137+
import validator from 'validator'
138+
// eslint-disable-next-line no-unused-vars
139+
import { VForm } from '@/shims-tsx'
136140
137141
export default Vue.extend({
138142
name: 'VuetifyDemo',
143+
computed: {
144+
form (): VForm {
145+
return this.$refs.form as VForm
146+
}
147+
},
139148
data: () => ({
140149
colors: ['indigo', 'warning', 'pink darken-2', 'red lighten-1', 'deep-purple accent-4'],
141150
slides: ['First', 'Second', 'Third', 'Fourth', 'Fifth'],
@@ -150,9 +159,50 @@ export default Vue.extend({
150159
formValidation: false,
151160
getDemoPayload: new GetDemoPayload(),
152161
titleRules: [
153-
(v: string) => !!v || 'Title is required',
154-
(v: string) => (v && (10 <= v.length || v.length <= 20)) || 'Name mustn\'t less than 10 characters and more than 20 characters!'
155-
]
162+
(v: string) => !!v || 'Title is required.',
163+
(v: string) => {
164+
if (v && !validator.isLength(v, 10, 20)) {
165+
return 'The length of title must not be less than 10 characters and not be more than 20 characters!'
166+
}
167+
return true
168+
}
169+
],
170+
textRules: [
171+
(v: string) => !!v || 'Test is required.',
172+
(v: string) => {
173+
if (v && !validator.contains('hello', v)) {
174+
return 'The text need to contain `hello`!'
175+
}
176+
return true
177+
}
178+
],
179+
ratingRules: [
180+
(v: number) => !!v || 'Rating is required.',
181+
(v: number) => {
182+
if (v && (v < 0 || v > 10)) {
183+
return 'The value range of rating should be [0, 10]!'
184+
}
185+
return true
186+
}
187+
],
188+
emailRules: [
189+
(v: string) => !!v || 'Email is required.',
190+
(v: string) => {
191+
if (v && !validator.isEmail(v)) {
192+
return 'Invalid email!'
193+
}
194+
return true
195+
}
196+
],
197+
siteRules: [
198+
(v: string) => {
199+
if (v && !validator.isFQDN(v)) {
200+
return 'Invalid domain name!'
201+
}
202+
return true
203+
}
204+
],
205+
sendRequestLoading: false
156206
}),
157207
mounted () {
158208
this.onResize()
@@ -182,9 +232,19 @@ export default Vue.extend({
182232
handleDebouncedClickButton (event: Event) {
183233
console.info('handleDebouncedClickButton', event)
184234
},
185-
async handleThrottledClickRendRequest () {
235+
async handleDebouncedClickRendRequest (event: Event) {
236+
console.info('handleDebouncedClickRendRequest', event)
237+
this.form.validate()
238+
if (!this.formValidation) {
239+
this.$toast.warning('The form is incorrect!')
240+
return
241+
}
242+
this.sendRequestLoading = true
186243
const response = await vuetifyDemoApi.getDemo(this.getDemoPayload)
187-
console.info('Response', response)
244+
this.$toast.success(response.message)
245+
setTimeout(() => {
246+
this.sendRequestLoading = false
247+
}, 2000)
188248
},
189249
onResize () {
190250
this.windowSize = { x: window.innerWidth, y: window.innerHeight }

0 commit comments

Comments
 (0)