概要
Nuxt.jsの2系でモーダルを実装する際にモーダルの開閉フラグをv-modelで管理する際のイベント発火させる動作がややこしかったので今回はそれをまとめます。
結論
inputイベントでなくても v-model
を使う場合は、
親コンポーネントで指定した v-model="hoge"
の値を子コンポーネントでは propsのvalueで受け取って、
emit('input', 送りたい値)
で親に送り返す必要がある。
実装例
/shared/Modal/index.vue
子コンポーネント(モーダルコンポーネント)では propsにvalue
という値を定義して、inputイベントで送りたい値をemitする
<template> <div v-if="value" class="modal" v-bind="$attrs" @click.stop="close"> <section class="modal-window" @click.stop> <button v-if="!hiddenCloseButton" class="nn-icon-button close-button" @click="close"> <IconClose class="nn-icon" /> </button> <header v-if="title" class="title-container"> <h1 class="title">{{ title }}</h1> </header> <div> <slot name="body" /> </div> <footer> <slot name="footer" /> </footer> </section> </div> </template> <script lang="ts"> import Vue from "vue"; import IconClose from "@/assets/icons/close.svg"; export default Vue.extend({ name: "Modal", components: { IconClose, }, props: { value: { // vue.js 2系の場合は、「value」でpropsの値を定義する必要がある type: Boolean, required: true, }, hiddenCloseButton: { type: Boolean, default: false, }, title: { type: String, default: "", }, }, methods: { close() { this.$emit("input", false); }, }, }); </script>
/pages/plan/index.vue
v-model="modalVisible"
でモーダルの開閉フラグを渡す
<Modal v-model="modalVisible" :title="'プレミアムプランの解約'"> <template #body> <p class="description"> この操作は取り消すことができません </p> <CancellationFormContainer /> </template> <template #footer> <div class="action-container"> <div class="button-container"> <button class="primary-button type-negative type-small" @click="switchModalDisplay"> キャンセル </button> <button class="primary-button type-primary type-small" type="submit"> 解約する </button> </div> </div> </template> </Modal> <script lang="ts"> import Vue from "vue"; import Modal from "@/components/shared/Modal/index.vue"; <中略> data() { return { modalVisible: false, }; }, <中略> methods: { switchModalDisplay(): void { this.modalVisible = !this.modalVisible; }, }, }); </script>
Vue.js 3系での変更点
引数のないすべての v-model について、プロパティとイベントの名前をそれぞれ modelValue と update:modelValue に置き換えてください。
<ChildComponent v-model="pageTitle" />
// ChildComponent.vue export default { props: { modelValue: String // 以前は `value:String` でした }, emits: ['update:modelValue'], methods: { changePageTitle(title) { this.$emit('update:modelValue', title) // 以前は `this.$emit('input', title)` でした } } }