使えそうなケース
- コンポーネントをロジックとスタイリングを分割させているときに、スタイリング用のコンポーネントでは見た目のレイアウトに専念させ、ロジック部分は$emitさせて親のコンポーネントのメソッドを差し込める。
- たとえば、一方の親コンポーネントではボタンをクリックしたら"GOOD"をalertさせ、もう一方の親コンポーネントでは"BAD"をalertさせるには、以下のように実装する
子コンポーネント
// components/ChildComponent.vue
<template>
<button @click="clickButton"></button>
</template>
<script>
export default = {
method: {
clickButton() {
this.$emit('click-button') // click-buttonというカスタムイベントを作る
}
}
}
</script>
親コンポーネント
// components/goodComponent.vue
<template>
<child-component
@click-button="alertGood" // カスタムイベントなので@(v-on)でイベントをハンドルする
></child-component>
</template>
<script>
import ChildCompoent from '@/components/ChildComponent'
export default {
components: {
Childcomponent
},
methods: {
alertGood() {
alert('GOOD');
}
}
}
</script>
- 同様に
// components/BadComponent.vue
<template>
<child-component
@click-button="alertBad"
></child-component>
</template>
<script>
import ChildCompoent from '@/components/ChildComponent'
export default {
components: {
Childcomponent
},
methods: {
alertBad() {
alert('BAD');
}
}
}
</script>
引数も送りたいとき
// child
method: {
clickButton() {
this.$emit('click-button', "data") // $emitの引数に入れる
}
}
//parent
<child-component @click-button="alertData"
></child-component>
...
methods: {
alertData(data) { // ここで受け取る
alert(data);
}
}
双方向バインディングを実現する
- 簡単なloginフォームを想定します。
// ChildComponents.vue
<template>
<form>
<input type="email" v-model="localEmail">
<input type="password" v-model="localPassword">
<button @click="onSubmit">
</form>
</template>
<script>
export default {
props: {
email: {
type: String,
required: true,
},
password: {
type: String,
required: true,
},
},
computed: {
// 算出プロパティにgetterとsetterを作ってv-modelをつくる
localEmail: {
get() {
return this.email
},
set(val) {
// 親側でupdateイベントで発火し、双方向バインディングを作れる
this.$emit('update:email', val)
},
},
localPassword: {
get() {
return this.password
},
set(val) {
this.$emit('update:password', val)
},
},
methods: {
onSubmit(event) {
this.$emit('submit', event); // カスタムイベントsubmitを登録
}
}
}
</script>
vue.js
// ParentComponent.vue
<template>
<child-component
// .sync修飾子で以下の2行を1行で書ける
// :email="form.email"
// @update:email="form.email = $event"
:email.sync="form.email"
:password.sync="form.password"
@submit.prevent.stop="login"
></child-component>
</template>
<script>
import ChildComponent from '@/components/ChildComponent'
export default {
components: {
ChildComponent
},
data() {
return {
form: {
email: '',
password: '',
},
}
},
methods: {
login(event) {
// apiにpostとか...
}
},
参考資料
https://jp.vuejs.org/v2/guide/components-custom-events.html
https://www.amazon.co.jp/%E5%9F%BA%E7%A4%8E%E3%81%8B%E3%82%89%E5%AD%A6%E3%81%B6-Vue-js-MIO-ebook/dp/B07D9BYHMZ