LoginSignup
28
18

More than 5 years have passed since last update.

カスタムコンポーネントにv-modelでオブジェクトをバインドしてcomputedを動的に定義する[vue.js]

Posted at

どんな時に使うのか

オブジェクトをカスタムコンポーネントにv-modelでマウントする時!

components/Hello.vue
<template>
  <my-component v-model="obj" />
</template>

<script>
import MyComponent from '@/components/MyComponent';
export default {
  components: {
    'my-component': MyComponent,
  },
  data() {
    return {
      obj: {
        key1: '',
        key2: '',
        key3: '',
        key4: '',
      },
    };
  },
}
</script>
components/Mycomponent.vue
<form>
    <div class="flexForm">
      <dd>
        <input v-model="key1">
      </dd>
      <dd>
        <input v-model="key2">
      </dd>
      <dd>
        <input v-model="key3">
      </dd>
      <dd>
        <input v-model="key4">
      </dd>
    </div>
  </form>
</template>
<script>
export default {
  props: ['value'],
  computed: {
    key1: {
      get() {
        return this.value.key1
      },
      set(val) {
        this.updateValue({ key1: val });
      }
    },
    key2: {
      get() {
        return this.value.key2
      },
      set(val) {
        this.updateValue({ key2: val });
      }
    },
    key3: {
      get() {
        return this.value.key3
      },
      set(val) {
        this.updateValue({ key3: val });
      }
    },
    key4: {
      get() {
        return this.value.key4
      },
      set(val) {
        this.updateValue({ key4: val });
      }
    },
  },
  methods: {
    updateValue(diff) {
      this.$emit('input', Object.assign({}, this.value, diff));
    },
  },
}
</script>

オブジェクトのキーの数だけcomputedが増える!!
そんな馬鹿馬鹿しいことやってられるか!!

そんな時にcomputedを動的に定義させて綺麗にする

components/Mycomponent.vue
<form>
    <div class="flexForm">
      <dd>
        <input v-model="key1">
      </dd>
      <dd>
        <input v-model="key2">
      </dd>
      <dd>
        <input v-model="key3">
      </dd>
      <dd>
        <input v-model="key4">
      </dd>
    </div>
  </form>
</template>
<script>
export default {
  props: ['value'],
  beforeCreate() {
    [
      'key1',
      'key2',
      'key3',
      'key4',
    ].forEach((keyName) => {
      this.$options.computed[keyName] = {
        get() {
          return this.value[keyName];
        },
        set(val) {
          this.updateValue({ [keyName]: val });
        },
      }
    });
  },
  methods: {
    updateValue(diff) {
      this.$emit('input', Object.assign({}, this.value, diff));
    },
  },
}
</script>

ふ、ふつくしい。。。。!!!

ちなみに

  • computedを動的に定義するにはbeforeCreateじゃないと遅い
  • beforeCreateのタイミングではthis.valueが使えないし
    • Object.keys(this.$options.propsData.value)でいけるかと思ったけどいけなかった
    • なのでキー名を配列でベタ書きしてます

ってことで100点ではないけどまぁ80点かな

参考にさせていただいた記事

v-modelにオブジェクトをバインディングする場合のコンポーネント実装を考える
v-modelを受け付けるカスタムコンポーネントの書き方の好み
Generating computed properties on the fly

28
18
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
28
18