親から子コンポーネントに propsを使って値を送信し、子から親へ emitを使って変更を通知するような実装ってよくありますよね!
この時に受け渡す値の数だけ props
と emit
が増えて行くので、やりたいことの割にコードが煩雑になってどうにかならないかという悩みを .sync演算子を使って解決しましたのでその備忘録として残します。
また、 普段はnuxtにvue-property-decoratorを使って実装している場合についても記載します。
実行環境
vue: ^2.6.12
propsとemitを使ったやり方
親からpropsを使って値を子に渡し、子は値の変更を watchして emit
を使い親に値を渡しています。
<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>
<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.vue
で input
イベントで emit
を使い通信を行うようにしました。
<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>
<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
<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>
<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.vue
にemit
を書くこともなくなるのでスッキリしますね!