LoginSignup
0
1

More than 1 year has passed since last update.

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

Last updated at Posted at 2021-12-23

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);

【完成形】@changekeyの引数を持たせる

以下のようにすることで複数の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ではないのでlazynumber等は使えない。
    そのため、バリデーション等は自分で書く必要がある。(patternプロパティで頑張る?)
  • それが嫌ならmountedあたりで通常の配列かオブジェクトにしてしまえばよいが、結局どこかでMapに戻す処理が必要になる。
0
1
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
0
1