Sử dụng v-model trên component lồng nhau

Cách viết dùng v-model để tự đồng bộ giá trị khi lồng các component
Bạn có component nhận vào prop
, muốn sử dụng v-model
để nó tự cập nhập giá trị khi có thay đổi
// Address.vue <template> <div> <input name="street" v-model="street"> <input name="city" v-model="city"> <input name="state" v-model="state"> <input name="zip" v-model="zip"> </div> </template> <script> export default { props: ['street', 'city', 'state', 'zip'] } </script>
Bạn truyền nó vào như thế này, với hy vọng mọi thứ chạy ngon lành
// Form.vue <template> <form> <input name="name" v-model="name"> <input name="email" v-model="email"> <mailing-address :street="address.street" :city="address.city" :state="address.state" :zip="address.zip" /> </form> </template> <script> import MailingAddress from './Address.vue'; export default { components: { MailingAddress }, data() { return { address: { street: '', city: '', state: '', zip: '' } } } } </script>
Nhưng không 😭 nó sẽ thông báo trong console, “Avoid mutating a prop directly since the value will be overwritten whenever the parent component re-renders. “
Về nguyên tắc, chúng ta ko được thay đổi giá trị của prop
, nếu không lúc re-render nó sẽ ko còn đúng nữa
Để nó chạy ngon lành, chúng ta không dùng prop
. Khi sử dụng v-model
nó làm cho chúng ta 2 việc, bind giá trị vào biến value
, gắn handle cho sự kiện v-on:input
. Túm lại chúng ta ko cần dùng prop làm gì cả, chỉ việc dùng lùng value
bên trong component
// Address.vue <template> <div> <input name="street" v-model="value.street"> <input name="city" v-model="value.city"> <input name="state" v-model="value.state"> <input name="zip" v-model="value.zip"> </div> </template> <script> export default { props: { value: { type: Object, required: true } }, watch: { value() { this.$emit('input', this.value); } } } </script>
Khi sử dụng
<mailing-address v-model="address" />
Nếu nó thêm một cấp nữa thì sao? Ví dụ bên trong Address.vue
chúng ta nhét thêm một component cháu nội của Form
nữa
// Address.vue <template> <div> ... <state v-model="value.state" /> </div> </template> <script> import State from "./State"; export default { components: { State }, props: { value: { type: Object, required: true } }, watch: { value() { this.$emit('input', this.value); } } } </script>
State component
<template> <select v-model="value"> <option v-for="(state, abbreviation) in states" :value="abbreviation" v-html="state" ></option> </select> </template> <script type="text/babel"> export default { props: { value: { type: String, required: true } }, data() { return { states: { NY: 'New York', WI: 'Wisconsin' // + rest of the states } } } } </script>
Nó sẽ tiếp tục chửi bới chúng ta, vì chúng ta đi thay đổi prop nữa rồi, chúng ta cần đưa nó về computed
// State.vue <template> <select v-model="localState"> <option v-for="(state, abbreviation) in states" :value="abbreviation" v-html="state" ></option> </select> </template> <script type="text/babel"> export default { props: { value: { type: String, required: true } }, data() { return { states: { NY: 'New York', WI: 'Wisconsin' // + rest of the states } } }, computed: { localState: { get() {return this.value}, set(localState) { this.$emit('input', localState)} } } } </script>
Cách này giống như chúng ta dùng controlled component trong React
Station D via Vuilaptrinh