Vue.jsで数値(number)が絡むフォーム入力バインディング(v-model)を行うときにハマったこと


Vue.jsで数値が絡むフォーム入力バインディングを行うときにハマったことについて解説していきます。

目的の処理

  1. 数値を入力するフォーム( type="number" )を3つ作成
  2. この3つのフォームに与えられた数値の合計をtotalとしてフロントに反映

この処理を行うためにVue.jsにあるフォーム入力バイディング(v-model)と算出プロパティ(computed)を使用していきます。

間違ったコード

TotalCounter.vue
<template>
    <div>
        <div>
            <label for="first-num">First: </label> 
            <input type="number" name="first-num" v-model="firstNum">
        </div>
        <div>
            <label for="second-num">Second: </label>
            <input type="number" name="second-num" v-model="secondNum">
        </div>
        <div>
            <label for="third-num">Third: </label> 
            <input type="number" name="third-num" v-model="thirdNum">
        </div>
        <div>total: {{ totalNum }}</div>
    </div>
</template>

<script>
export default {

    data() {
        return {
            firstNum: 0,
            secondNum: 0,
            thirdNum: 0,
        }
    },

    computed: {
        totalNum() {
            return this.firstNum + this.secondNum + this.thirdNum
        }
    }

}
</script>

これは、実際に最初に書いたコードです。

初期値0の3つのデータ(firstNum, secondNum, thirdNum)をv-modelを用いてフォームの入力の変更を反映、compoutedでそれぞれの合計をtotalNumとして処理をするという内容になっています。

一見正しい記述内容に見えますが、これを確認してみると、以下のようになります。


目的の処理とは違う挙動になってしまいました....

間違いの原因

この間違った挙動は、v-modelが返す値が要因となっています。

公式ドキュメントによると、v-modelは入力された値の形式にかかわらず文字列として返して性質があるようです。

今回の処理で説明すると、フォームの形式がtype="number"になっているのにもかかわらずv-modelで返ってくる値が文字列になってしまったために、totalNum内で、

return 2 + 3 + 7

というような処理をしたかったのに実際は、

return "2" + "3" + "7"

このような処理になってしまったということです。

解決策

公式ドキュメントを参考に以下のように修正します。

TotalCounter.vue
<template>
    <div>
        <div>
            <label for="first-num">First: </label> 
            <input type="number" name="first-num" v-model.number="firstNum">
        </div>
        <div>
            <label for="second-num">Second: </label>
            <input type="number" name="second-num" v-model.number="secondNum">
        </div>
        <div>
            <label for="third-num">Third: </label> 
            <input type="number" name="third-num" v-model.number="thirdNum">
        </div>
        <div>total: {{ totalNum }}</div>
    </div>
</template>

<script>
export default {

    data() {
        return {
            firstNum: 0,
            secondNum: 0,
            thirdNum: 0,
        }
    },

    computed: {
        totalNum() {
            return this.firstNum + this.secondNum + this.thirdNum
        }
    }

}
</script>

v-modelに末尾に.numberを追加することで、v-modelは数値として値を返してくれるようになります。

これで目的の処理になりました!

結論

Vue.jsの標準で搭載されているv-modelはバイディングを行う際にとても便利な機能ですが、仕組みや使い方を間違ってしまうと、このような初歩的な部分でつまづいてしまいそうになりますね...

これからは、公式ドキュメントの参照を心がけます!

同じようなところでつまづいている方々の参考になれたならなと思います。

参考サイト