3
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Vue.js で大きなオブジェクトをv-modelでやりとりするコンポーネントを作る

Last updated at Posted at 2018-10-20

はじめに

インプット要素を使いまわしたいなあと思っていたところ、ひらめいたので実装したらうまくったので紹介します。

目的

v-model='hugeObject' ができるカスタムコンポーネントを作る

方法

Reduxの考え方に影響を受けています。

インプットエレメントをラップした「インプットコンポーネント」を作る

以下のように、インプットエレメントをラップします。

FormText.vue
<template>
  <label class="FormText" :for="name">
    <span>{{ label }}</span>
    <input type="text" :id="id" :name="name" v-model="text">
  </label>
</template>

<script>
export default {
  name: 'FoundationFormText',
  props: {
    id: String, // input要素のid
    label: String, // フォームのラベルに表示するテキスト
    name: String,  // input要素のname
    value: String, // input要素のvalue
  },

  computed: {
    text: {
      get() { return this.value },
      set(text) { this.$emit('input', text) },
    },
  },
}
</script>

このようにすることで、インプットエレメントをラップしたインプットコンポーネントを作成することができます。

「インプットコンポーネント」を複数所有するコンポーネントを作る

次に、上記で作成したコンポーネントたちを組み合わせたコンポーネントを作成します。

次い、 lastNamefirstName を処理するコンポーネントを作ります。

CustomerForm.vue
<template>
  <div>
    <FormText v-model="lastName" id="last" name="last" label="姓" />
    <FormText v-model="firstName" id="first" name="first" label="名" />
  </div>
</template>

<script>
import FormText from './FormText.vue'

export default {
  name: 'CustomerForm',
  components: { FormText },
  
  props: {
    value: {
      type: Object,
      validator: customer => {
        if (typeof customer.lastName !== 'string') return false
        if (typeof customer.firstName !== 'string') return false
        return true
      },
    },
  },

  computed: {
    lastName: {
      get() { return this.value.lastName },
      set(lastName) { this.$emit('input', { ...this.value, ...{ lastName }}),
    },

    firstName: {
      get() { return this.value.firstName },
      set(firstName) { this.$emit('input', { ...this.value, ...{ firstName }}),
    },
  },
}

子コンポーネントに渡したいデータごとにcomputed propertyを作成します。
また、上記の例では 親コンポーネントから value propへ渡される値(Customer型)は、 lastNamefirstName 以外のデータを受け取っても問題ないです。

このように定義したコンポーネントを使います。

RegisterForm.vue
<template>
  <div>
    <CustomerForm v-model="customer" />
    <OtherForm v-model="customer.otherObject" />
  </div>
</template>

<script>
import CustomerForm from './CustomerForm.vue'
import OtherForm from './OtherForm.vue'

export default {
  name: 'RegisterForm',
  components: { CustomerForm, OtherForm },
  
  data() {
    return {
      customer: {
        lastName: '',
        firstName: '',
        otherObject: {},
      },
    },
  },
}
</script>

まとめ

v-modelにコミュニケーションをまとめることで、再利用性の高いコンポーネントを作成できたと言えます。

3
4
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
3
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?