12
13

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Vuetifyのv-text-fieldでtype="number"の時に、表示されている値を変更する

Last updated at Posted at 2020-08-19

こんにちは!
長らくプレーンJavaScriptガリゴリマンでしたが、最近仕事の関係でVue.jsを触り始めた和尚でございます:star2:

それでは早速見ていきましょう!

環境

  • Vue.js v2.6.11
  • Vuex v3.4.0
  • Vuetify v2.2.11

注意

※かなりパワープレイです。参考程度に見てください。

本日の議題

ある昼下がりのこと

Aさん「数値しか入れられないフォームに文字が入っちゃうんだけど、どうにかならない?あと、最大値が999のはずなのに、キーボードで入力すると1000以上入っちゃうからついでに直して...」

和尚「まじっすかー。ちょっと調べてみまーす!」

...

今回の要件における Input type="number" の困った仕様

  • 最大値と最小値はキーボード入力で超えることが可能
  • safariは文字列を入力すると自動削除されない
  • chromeは指数表現のe・Eが入力できる

解決しよう!

それではAさんの要件に沿って修正していきましょう!
修正前のサンプルコンポーネントとストアはこちらになります!

Count.vue
<template>
  <div>
    <v-text-field
      ref="count"
      label="カウント"
      type="number"
      v-model="count"
      max="999"
      min="1"
    />
  </div>
</template>

<script>
export default {
  computed: {
    count: {
      get() {
        return this.$store.getters['count'];
      },
      set(num) {
        this.$store.commit('changeCount', num);
      }
    }
  }
}
</script>
index.js
import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

export default new Vuex.Store({
  state: {
    count: 1
  },
  getters: {
    count: (state) => state.count
  },
  mutations: {
    changeCount(state, num) {
      if (num) state.count = num;
    }
  },
  actions: {}
})

①数値のみが入るようにする

テキストフィールドのvalueに文字列が入力された場合は空文字が返却されます。なのでsafariで文字列が入力されたままでもストアで管理している値に文字列が入ることはありません。(空文字を除く)

ということは、テキストフィールドの中にはvalueとは違う値が表示されているということです。

まずはテキストフィールドに数値のみしか入らないように修正していきましょう!
算術プロパティに記述した、countのsetterの中を少し弄ってテキストフィールドに現在のvalueの値が表示されるようにしましょう!

Count.vue
<script>
export default {
  computed: {
    count: {
      get() {
        return this.$store.getters['count'];
      },
      set(num) {
        num = parseInt(num)
        if (Number.isNaN(num) || num < 1) {
          num = 1;
        }

        this.$refs.count.lazyValue = num;
        this.$store.commit('changeCount', num);
      }
    }
  }
}
</script>
  1. valueをparseIntを使用して文字列から数値にする (Input type="number"の値はString型です)
  2. 数値ではない場合にnumに1を代入
  3. this.$refs.count.lazyValueに値を入れる (v-text-fieldの表示している値を格納しているプロパティ)
  4. ストアの値も変更する

②最大値・最小値の制御

ここまで来たらあとは簡単!
最大値と最小値の制御を入れてあげましょう!

Count.vue
<script>
export default {
  computed: {
    count: {
      get() {
        return this.$store.getters['count'];
      },
      set(num) {
        const min = Number(this.$refs.count.$attrs.min);
        const max = Number(this.$refs.count.$attrs.max);

        num = parseInt(num)
        if (Number.isNaN(num) || num < min) {
          num = min;
        } else if (num > max) {
          num = max;
        }
        
        this.$refs.count.lazyValue = num
        this.$store.commit('changeCount', num);
      }
    }
  }
}
</script>
  1. this.$refs.countの下に$attrsという属性値が格納されたプロパティがあるので、そこから最大値と最小値を取り出します。
  2. 文字もしくは最小値以下だった場合は、最小値を代入
  3. 最大値を超えた値が入力された場合は最大値を代入

完成!!

これでchromeでもsafariでも問題なく、要件を満たすことができました!

this.$refs.count.lazyValueを見つけるまでは、ネットで似たようなことをしている記事を探しては試していました。しかし完璧に要件を満たせる方法がなくて諦めかけてたところ、何となくconsole.log(this.$refs.count)を書いてプロパティみてたらそれっぽい値を発見しました!そして、うまく値を書き換えることに成功笑

結構稀な対応かとは思いますが、同様のパターンを実装されたい方は是非参考にしていただけると幸いです。
もっと良い案があればコメントください!

ではでは!

12
13
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
12
13

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?