DevLog
Python, TypeScript, etc...
MyPage
Python, TypeScript, etc...
2022/04/27

個人的Vue.js(2.x)バリデーション付きフォーム実装の最適解

vee-validateVue.js
preview
@shiromisanta
Frontend Engineer
  •  /
目次
前提

前提

  • vee-validate ライブラリの使い方等はvee-validateでVue.jsのバリデーションここに記載。
  • vuetify
  • Type Script (Options API)

結論

<template> <div> <ValidationObserver ref="observer" v-slot="{ invalid }"> <v-form> <validation-provider v-slot="{ errors }" name="Name" rules="required"> <v-text-field v-model="name" :error-messages="errors" /> </validation-provider> <validation-provider v-slot="{ errors }" name="性別" rules="required" :skip-if-empty="false"> <v-select v-model="gender" :items="genderItems" :error-messages="errors" /> </validation-provider> <validation-provider v-slot="{ errors }" name="身長" rules="required|integer|min_value:1"> <v-text-field v-model.number="height" type="number" :error-messages="errors" /> </validation-provider> </v-form> <v-btn :disabled="invalid"> Submit </v-btn> </ValidationObserver> </div> </template> <script lang="ts"> import Vue from 'vue' import { UserInfo } from '~/model/User' import { userModule } from '~/store' export type Gender = 'm' | 'f' | '' const cloneDeep = (obj: any) => JSON.parse(JSON.stringify(obj)) export default Vue.extend({ name: 'Sample', data () { return { genderItems: [ { text: '選択', value: '' }, { text: '男性', value: 'm' }, { text: '女性', value: 'f' }, ] } }, computed: { userInfo: { get () { return userModule.userInfo }, set (userInfo: UserInfo) { userModule.setUserInfo(userInfo) } }, name: { get (): string { return this.userInfo.name }, set (name: string) { this.setUserInfo({ name }) } }, gender: { get (): Gender { return this.userInfo.gender }, set (gender: Gender) { this.setUserInfo({ gender }) } }, height: { get (): number | '' { return this.userInfo.height || '' }, set (height: number | '') { this.setUserInfo({ height: height || 0 }) } }, }, methods: { setUserInfo (arg: Partial<UserInfo>) { this.userInfo = { ...cloneDeep(this.userInfo), ...arg } } } }) </script>

解説

Text, Select

普通ですね。

storeに元となるインスタンスUserInfoが入っていて、それをcomputedでラップします。getterで中の項目を取り出して、setterでstoreの中まで更新するように。

name: { get (): string { return this.userInfo.name }, set (name: string) { this.setUserInfo({ name }) } }, gender: { get (): Gender { return this.userInfo.gender }, set (gender: Gender) { this.setUserInfo({ gender }) } },

Number

computedに数値で渡せるようにv-model.numberを使います。そうしないとtype=numberを指定してもstringで来ます。

<validation-provider v-slot="{ errors }" name="身長" rules="required|integer|min_value:1"> <v-text-field v-model.number="height" type="number" :error-messages="errors" /> </validation-provider>

v-model.numberを使っても空欄の時は普通に空文字列''が来ます。

なので、データが0のときはinputに空文字列を渡す、inputが空文字列の時はデータに0を渡す、と言ったようにここで差分を吸収しています。

height: { get (): number | '' { return this.userInfo.height || '' }, set (height: number | '') { this.setUserInfo({ height: height || 0 }) } },

関連記事

preview
@shiromisanta 2022/06/12
Nuxt.jsVue.js
preview
@shiromisanta 2022/01/11
vee-validateVue.jsNuxt.js
2022/04/27

個人的Vue.js(2.x)バリデーション付きフォーム実装の最適解

vee-validateVue.js

前提

結論

<template> <div> <ValidationObserver ref="observer" v-slot="{ invalid }"> <v-form> <validation-provider v-slot="{ errors }" name="Name" rules="required"> <v-text-field v-model="name" :error-messages="errors" /> </validation-provider> <validation-provider v-slot="{ errors }" name="性別" rules="required" :skip-if-empty="false"> <v-select v-model="gender" :items="genderItems" :error-messages="errors" /> </validation-provider> <validation-provider v-slot="{ errors }" name="身長" rules="required|integer|min_value:1"> <v-text-field v-model.number="height" type="number" :error-messages="errors" /> </validation-provider> </v-form> <v-btn :disabled="invalid"> Submit </v-btn> </ValidationObserver> </div> </template> <script lang="ts"> import Vue from 'vue' import { UserInfo } from '~/model/User' import { userModule } from '~/store' export type Gender = 'm' | 'f' | '' const cloneDeep = (obj: any) => JSON.parse(JSON.stringify(obj)) export default Vue.extend({ name: 'Sample', data () { return { genderItems: [ { text: '選択', value: '' }, { text: '男性', value: 'm' }, { text: '女性', value: 'f' }, ] } }, computed: { userInfo: { get () { return userModule.userInfo }, set (userInfo: UserInfo) { userModule.setUserInfo(userInfo) } }, name: { get (): string { return this.userInfo.name }, set (name: string) { this.setUserInfo({ name }) } }, gender: { get (): Gender { return this.userInfo.gender }, set (gender: Gender) { this.setUserInfo({ gender }) } }, height: { get (): number | '' { return this.userInfo.height || '' }, set (height: number | '') { this.setUserInfo({ height: height || 0 }) } }, }, methods: { setUserInfo (arg: Partial<UserInfo>) { this.userInfo = { ...cloneDeep(this.userInfo), ...arg } } } }) </script>

解説

Text, Select

普通ですね。

storeに元となるインスタンスUserInfoが入っていて、それをcomputedでラップします。getterで中の項目を取り出して、setterでstoreの中まで更新するように。

name: { get (): string { return this.userInfo.name }, set (name: string) { this.setUserInfo({ name }) } }, gender: { get (): Gender { return this.userInfo.gender }, set (gender: Gender) { this.setUserInfo({ gender }) } },

Number

computedに数値で渡せるようにv-model.numberを使います。そうしないとtype=numberを指定してもstringで来ます。

<validation-provider v-slot="{ errors }" name="身長" rules="required|integer|min_value:1"> <v-text-field v-model.number="height" type="number" :error-messages="errors" /> </validation-provider>

v-model.numberを使っても空欄の時は普通に空文字列''が来ます。

なので、データが0のときはinputに空文字列を渡す、inputが空文字列の時はデータに0を渡す、と言ったようにここで差分を吸収しています。

height: { get (): number | '' { return this.userInfo.height || '' }, set (height: number | '') { this.setUserInfo({ height: height || 0 }) } },

関連記事

preview
@shiromisanta
Frontend Engineer
  •  /
©︎Devlog