Mapオブジェクトはv-modelで扱えないので強引にリアクティブにする

# v-modelにMapは設定できない？

`v-model`を使ってフォームの値をリアクティブに扱うのは通常行っていることと思うが

```vue
<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ではない](https://stackoverflow.com/questions/68870514/v-for-using-map-data-structure-and-v-model)）ので、このままでは書き変わらず、次のリアクティブ動作が発火すると元の値に戻されてしまう。

## v-modelは諦めて`value`、`@change`で対応してみる

仕方ないので手動で`change`イベントで`set`をしてやることにする。


```vue
<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`により手動で発火させる必要がある。

## リアクティブにしてみる

```vue
<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オブジェクトを作成してもよい、

```js
      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を舐めていくときに使える。

```vue
<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に戻す処理が必要になる。