v-modelにMapは設定できない?
v-model
を使ってフォームの値をリアクティブに扱うのは通常行っていることと思うが
<template>
<div>
<p>値:<input v-model="hoge" type="text" /></p>
<p>配列:<input v-model="arrHoge[0]" type="text" /></p>
<p>オブジェクト:<input v-model="objHoge.fuga" type="text" /></p>
<p>Map:<input v-model="mapHoge.get('fuga')" type="text" /></p>
<p>
Map Data<br />
fuga:{{ mapHoge.get("fuga") }}<br />
piyo:{{ mapHoge.get("piyo") }}
</p>
</div>
</template>
<script>
export default {
data() {
return {
hoge: "fuga",
arrHoge: ["fuga"],
objHoge: {
fuga: "piyo"
},
mapHoge: new Map([
["fuga", "test"],
["piyo", "test"]
])
};
}
};
</script>
とすれば一見動作するように見える。
実際、フォームを表示して入力して見るところまでは行ける。
が、Mapオブジェクトの値の設定にはset
が必要になる(assignableではない)ので、このままでは書き変わらず、次のリアクティブ動作が発火すると元の値に戻されてしまう。
v-modelは諦めてvalue
、@change
で対応してみる
仕方ないので手動でchange
イベントでset
をしてやることにする。
<template>
<div>
<p>値:<input v-model="hoge" type="text" /></p>
<p>配列:<input v-model="arrHoge[0]" type="text" /></p>
<p>オブジェクト:<input v-model="objHoge.fuga" type="text" /></p>
<p>Map:<input :value="mapHoge.get('fuga')" type="text" @change="onChange" /></p>
<p>
Map Data<br />
fuga:{{ mapHoge.get("fuga") }}<br />
piyo:{{ mapHoge.get("piyo") }}
</p>
</div>
</template>
<script>
export default {
data() {
return {
hoge: "fuga",
arrHoge: ["fuga"],
objHoge: {
fuga: "piyo"
},
mapHoge: new Map([
["fuga", "test"],
["piyo", "test"]
])
};
},
methods: {
onChange($event) {
let val = $event.target.value;
this.mapHoge.set("fuga", val);
this.mapHoge.set("piyo", "test2");
}
}
};
</script>
これで値は書き換わるが、リアクティブではないため、Map Data
に表示される値が変わらない。
そのため、$set
により手動で発火させる必要がある。
リアクティブにしてみる
<template>
<div>
<p>値:<input v-model="hoge" type="text" /></p>
<p>配列:<input v-model="arrHoge[0]" type="text" /></p>
<p>オブジェクト:<input v-model="objHoge.fuga" type="text" /></p>
<p>
Map:<input :value="mapHoge.get('fuga')" type="text" @change="onChange" />
</p>
<p>
Map Data<br />
fuga:{{ mapHoge.get("fuga") }}<br />
piyo:{{ mapHoge.get("piyo") }}
</p>
</div>
</template>
<script>
export default {
data() {
return {
hoge: "fuga",
arrHoge: ["fuga"],
objHoge: {
fuga: "piyo"
},
mapHoge: new Map([
["fuga", "test"],
["piyo", "test"]
])
};
},
methods: {
onChange($event) {
let val = $event.target.value;
this.mapHoge.set("fuga", val);
this.mapHoge.set("piyo", "test2");
this.$set(this, "mapHoge", new Map(this.mapHoge));
}
}
};
</script>
ポイントはnew Map
をしているところ。
普通にthis.mapHoge
だけをセットすると値に変更がない(旧mapHoge === 新mapHogeなので)ため、発火しない。
もちろん、以下のように先に新しいMapオブジェクトを作成してもよい、
let NewMap = new Map(this.mapHoge);
NewMap.set("fuga", val);
NewMap.set("piyo", "test2");
this.$set(this, "mapHoge", NewMap);
【完成形】@change
にkey
の引数を持たせる
以下のようにすることで複数のMapに対応できるようになる。
v-for
等でMapを舐めていくときに使える。
<template>
<div>
<p>値:<input v-model="hoge" type="text" /></p>
<p>配列:<input v-model="arrHoge[0]" type="text" /></p>
<p>オブジェクト:<input v-model="objHoge.fuga" type="text" /></p>
<p>
Map<br>
fuga:<input :value="mapHoge.get('fuga')" type="text" @change="onChange($event, 'fuga')" /><br>
piyo:<input :value="mapHoge.get('piyo')" type="text" @change="onChange($event, 'piyo')" />
</p>
<p>
Map Data<br />
fuga:{{ mapHoge.get("fuga") }}<br />
piyo:{{ mapHoge.get("piyo") }}
</p>
</div>
</template>
<script>
export default {
data() {
return {
hoge: "fuga",
arrHoge: ["fuga"],
objHoge: {
fuga: "piyo"
},
mapHoge: new Map([
["fuga", "test"],
["piyo", "test"]
])
};
},
methods: {
onChange($event, key) {
let val = $event.target.value;
this.mapHoge.set(key, val);
this.$set(this, "mapHoge", new Map(this.mapHoge));
}
}
};
</script>
備考
-
v-model
ではないのでlazy
やnumber
等は使えない。
そのため、バリデーション等は自分で書く必要がある。(pattern
プロパティで頑張る?) - それが嫌なら
mounted
あたりで通常の配列かオブジェクトにしてしまえばよいが、結局どこかでMapに戻す処理が必要になる。