本記事について
下記問題に当たった人向けにv-modelと$emitを使った解決方法を記載してます。
- Vuetifyを使った子コンポーネントと親コンポーネント間のデータ受け渡しで困った方
- 双方向データ受け渡しを実装したけど、「Vue warn]: Avoid mutating a prop directly since ~」って出ちゃった人
問題
例えば、<v-navigation-drawer>
をの開閉Toggleを親コンポーネント内で作成したく下記のような実装をしたとする。
- 子コンポーネント
AppNavigationDrawer.vue
<template>
<v-navigation-drawer
v-model="drawer"
absolute
temporary
>
</v-navigation-drawer>
</template>
<script>
export default {
props: {
drawer: {
type: Boolean,
default: false
}
}
}
</script>
- 親コンポーネント
AppHeader.vue
<template>
<v-app-bar>
<v-app-bar-nav-icon
@click.stop="drawer = !drawer"
/>
</v-app-bar>
<navigation-drawer
v-model="drawer"
/>
</template>
<script>
import NavigationDrawer from '@/components/AppNavigationDrawer'
export default {
components: {
AppNavigationDrawer
},
data () {
return {
drawer: false
}
}
}
</script>
結果、子コンポーネント側のdrawerが変化するような操作、例えばNavigationの外側をクリックをすると下記のようなエラーが出る。
propsを直接変更するんじゃねぇ!っていうアラートですね。
解決方法
直接変更がダメならemitを使えばいいじゃない。つまり、この辺の話をVuetify Componentに応用すれば良いだけ。
- 子コンポーネント
AppNavigationDrawer.vue
<template>
<v-navigation-drawer
:value="value"
absolute
temporary
@input="$emit('input', $event)"
>
</v-navigation-drawer>
</template>
<script>
export default {
props: {
value: {
type: Boolean,
default: false
}
}
}
</script>
- 親コンポーネント
AppHeader.vue
<template>
<v-app-bar>
<v-app-bar-nav-icon
@click.stop="drawer = !drawer"
/>
</v-app-bar>
<navigation-drawer
v-model="drawer"
/>
</template>
<script>
import NavigationDrawer from '@/components/AppNavigationDrawer'
export default {
components: {
AppNavigationDrawer
},
data () {
return {
drawer: false
}
}
}
</script>
親からのprops valueを<v-navigation-drawer></v-navigation-drawer>
の開閉状態であるvalueにbindする。
子の変化は自身のinput event契機で、親のinputイベントを$emitすることで親コンポーネントへ変化を伝播する。
他
eventとvalueが定義されているVuetifty Componentであれば何でも応用できそうです。