LoginSignup
6
3

More than 3 years have passed since last update.

[nuxt].sync(propSync)を使って簡単に親子間コンポーネントで通信する

Posted at

親から子コンポーネントに propsを使って値を送信し、子から親へ emitを使って変更を通知するような実装ってよくありますよね!

この時に受け渡す値の数だけ propsemitが増えて行くので、やりたいことの割にコードが煩雑になってどうにかならないかという悩みを .sync演算子を使って解決しましたのでその備忘録として残します。

また、 普段はnuxtにvue-property-decoratorを使って実装している場合についても記載します。

実行環境

vue: ^2.6.12

propsとemitを使ったやり方

親からpropsを使って値を子に渡し、子は値の変更を watchして emitを使い親に値を渡しています。

parent.vue
<template>
  <div class="container">
    <h1>親コンポーネント</h1>
    <child :name="name" :email="email" @name-change="setName" @email-change="setEmail" />
    <button @click="submit">送信</button>
  </div>
</template>

<script>
import Child from '../components/Child';

const initName = '桃太郎'
const initEmail = 'momotaro@test.test'

export default {
  components: {
    Child
  },
  data() {
    return {
      name: initName,
      email: initEmail,
    }
  },
  methods: {
    submit() {
      alert(`name: ${this.name}, email: ${this.email}`)
    },
    setName(val) {
      this.name = val;
    },
    setEmail(val) {
      this.email = val;
    }
  }
}
</script>
Child.vue
<template>
  <div class="child-container">
    <h2>子コンポーネント</h2>
    <div class="form">
      <label>
        名前
        <input type="text" v-model="nameValue" />
      </label>
      <label>
        メールアドレス
        <input type="text" v-model="emailValue" />
      </label>
    </div>
  </div>
</template>

<script>
export default {
  props: {
    name: {
      type: String,
      default: ''
    },
    email: {
      type: String,
      default: ''
    }
  },
  data() {
    return {
      nameValue: this.name,
      emailValue: this.email,
    }
  },
  watch: {
    nameValue: function(val) {
      this.$emit('name-change', val);
    },
    emailValue: function(val) {
      this.$emit('email-change', val);
    }
  }
}
</script>

ただ親子で値を共有したいだけなのに、 親data()props子data()とバケツリレーを行うことでやっと達成できました。
やっていることは簡単なのにコードが煩雑です。
このやり方でフォームの値がどんどん増えて行くと、大変なことになります。。。

.sync演算子を使った場合

.sync演算子を使うことによって、parent.vueで$emitで受け取った値を更新する methodが必要なくなります。
また、 Child.vueinputイベントで emitを使い通信を行うようにしました。

parent.vue
<template>
  <div class="container">
    <h1>親コンポーネント</h1>
    <child :name.sync="name" :email.sync="email" />
    <button @click="submit">送信</button>
  </div>
</template>

<script>
import Child from '../../components/sync/Child';

const initName = '桃太郎'
const initEmail = 'momotaro@test.test'

export default {
  components: {
    Child
  },
  data() {
    return {
      name: initName,
      email: initEmail,
    }
  },
  methods: {
    submit() {
      alert(`name: ${this.name}, email: ${this.email}`)
    },
  }
}
</script>
Child.vue
<template>
  <div class="child-container">
    <h2>子コンポーネント</h2>
    <div class="form">
      <label>
        名前
        <input type="text" :value="name" @input="$emit('update:name', $event.target.value)" />
      </label>
      <label>
        メールアドレス
        <input type="text" :value="email" @input="$emit('update:email', $event.target.value)" />
      </label>
    </div>
  </div>
</template>

<script>
export default {
  props: {
    name: {
      type: String,
      default: ''
    },
    email: {
      type: String,
      default: ''
    }
  },
}
</script>

スッキリしましたね!
同じ値が入っているpropertyや同じ動作を行うmethodを減らすことができました。

vue-property-decoratorでpropSyncを使った場合

vue-property-decoratorでも .sync演算子が使えますので備忘録として残します。

実行環境

nuxt: ^2.14.0
vue-property-decorater: ^9.0.0

parent.vue
<template>
  <div>
    <h1>親コンポーネント</h1>
    <child :nameValue.sync="name" :emailValue.sync="email" />
    <button @click="submit">
      送信
    </button>
  </div>
</template>

<script lang="ts">
import {Vue, Component} from 'vue-property-decorator';
import Child from '../components/child.vue';

const initName = '桃太郎'
const initEmail = 'momotaro@test.test'

@Component({
  components: {
    Child
  }
})
export default class Parent extends Vue {
  name: string = initName;
  email: string = initEmail;

  submit(): void {
    alert(`name: ${this.name}, email: ${this.email}`);
  }
}
</script>
Child.vue
<template>
  <div>
    <h2>子コンポーネント</h2>
    <div>
      <label>
        名前
        <input type="text" v-model="name" />
      </label>
      <label>
        メールアドレス
        <input type="text" v-model="email" />
      </label>
    </div>
  </div>
</template>

<script lang="ts">
import {Vue, Component, PropSync} from 'vue-property-decorator';

@Component
export default class Child extends Vue {
  @PropSync('nameValue', { type: String }) name!: string
  @PropSync('emailValue', { type: String }) email!: string
}
</script>

Child.vueemitを書くこともなくなるのでスッキリしますね!

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