Vue2系でカスタムコンポーネントにて親子間の双方向データバインディングを実装する際の備忘録
前提として親子間のデータ受け渡しは、親→子の際にはProps、子から親へはemitで単方向で渡すことが前提になっており、親から渡された値を子コンポーネントにて直接編集してはいけない。
ただし実はオブジェクトを渡した際にはエラーを出さずに直接変更できたりする。(非推奨)
ズルするパターン
Child.vue
<template>
<div>
<v-text-field v-model="form.someText1" label="field1"></v-text-field>
<v-text-field v-model="form.someText2" label="field2"></v-text-field>
</div>
</template>
<script>
export default {
name: 'Child',
props: {
form: {
type: Object,
default: () => ({
someText1: '',
someText2: '',
}),
},
},
}
</script>
Parent.vue
<template>
<div>
<Child :form="form" />
</div>
</template>
<script>
export default {
name: 'Parent',
data:() => ({
form: {
someText1: '',
someText2: '',
},
}),
}
</script>
上記でも動かないことはないけれど今回はちゃんとやるケースを下記に記載する。
きちんとやるパターン
Child.vue
<template>
<div>
<v-text-field v-model="someText1" label="field1"></v-text-field>
<v-text-field v-model="someText2" label="field2"></v-text-field>
</div>
</template>
<script>
export default {
name: 'Child',
props: {
value: {
type: Object,
default: () => ({
someText1: '',
someText2: '',
}),
},
},
computed: {
someText1: {
get() {
return this.value.someText1
},
set(v) {
const Obj = JSON.parse(JSON.stringify(this.value)) // deep copy
Obj.someText1 = v
this.$emit('input', Obj)
},
},
someText2: {
get() {
return this.value.someText2
},
set(v) {
const Obj = JSON.parse(JSON.stringify(this.value))
Obj.someText2 = v
this.$emit('input', Obj)
},
},
}
}
</script>
Parent.vue
<template>
<div>
<Child v-model="form" />
</div>
</template>
<script>
export default {
name: 'Parent',
data:() => ({
form: {
someText1: '',
someText2: '',
},
}),
}
</script>