Я учусь сама vue 3. Я читаю статью за статьей о v-model и каждый раз, когда я думаю, что понимаю, как это работает, я снова путаюсь.
Моя цель: я создал собственный компонент раскрывающегося списка. Мне нужна возможность контролировать значение этого раскрывающегося списка от родителя. Когда раскрывающийся список изменяется, я хочу, чтобы родитель знал новое значение и индекс.
Дочерний компонент.vue
<div>
<select
:value = "modelValue"
@input = "$emit('update:modelValue', $event.target.value)"
>
<option v-for = "option in options" :key = "option">
{{ option }}
</option>
</select>
</div>
</template>
<script>
export default {
props: ["options", "modelValue"],
emits: ["update:modelValue"],
methods: {
selected() {
//??????
//want to emit this to the parent
let selectedIndex = this.$event.target.selectedIndex + 1
//this.$emit(value, selectedIndex)
},
},
};
</script>
родитель.vue
<template>
<my-drop-down :options = "options" v-model = "selectedOption" />
</template>
<script>
import myDropDown from "./components/base_dropdown.vue";
export default {
name: "App",
data: () => ({
selectedOption: "2 Line",
selectedIndex: 0,
options: ["1 Line", "2 Line", "3 Line"],
}),
components: {
myDropDown,
},
methods: {
//How can I call this when the select value changes??
onSelectChange(selected, index) {
console.info(`Parent L3rd Change, name: ${selected}, index: ${index} `);
},
},
};
</script>
Двусторонняя привязка работает корректно. Я могу контролировать значение выпадающего списка либо из дочернего, либо из родительского элементов. Но как мне вызвать метод onSelectChange в моем дочернем компоненте
Кроме того, и это может быть глупый вопрос, но...
v-model = "selectedOption" это то же самое, что написать :value = "modelValue" @input = "$emit('update:modelValue', $event.target.value)"
так почему родитель так пишется <my-drop-down :v-model = "selectedOption" />
а ребенок так написал <select :value = "modelValue" @input = "$emit('update:modelValue', $event.target.value)">
и не просто
<select :v-model = "selectedOption />



![Безумие обратных вызовов в javascript [JS]](https://i.imgur.com/WsjO6zJb.png)


Похоже, вам нужен наблюдатель в вашем родительском компоненте, который следит за изменениями в свойстве selectedOption, а затем использует новое значение для получения индекса из массива параметров и добавляет к нему единицу, а также использует новое значение для установки свойство selectedIndex.
Согласно Раздел Vuejs API о Наблюдателях:
Computed properties allow us to declaratively compute derived values. However, there are cases where we need to perform "side effects" in reaction to state changes - for example, mutating the DOM, or changing another piece of state based on the result of an async operation.
With Options API, we can use the watch option to trigger a function whenever a reactive property changes.
Итак, для вашего кода это может выглядеть примерно так:
watch: {
selectedOption(newValue, oldValue) {
console.info('In watcher. New value: ' + newValue)
// get the index of the String in the array and use it to set
// the selectedIndex:
this.selectedIndex = this.options.findIndex(i => i === newValue) + 1;
console.info('selectedIndex: ' + this.selectedIndex)
}
},
Что касается "второй части" вашего вопроса,
why is the parent written like this
<my-drop-down :v-model = "selectedOption" />and the child written like this
<select :value = "modelValue" @input = "$emit('update:modelValue', $event.target.value)">and not simply
<select :v-model = "selectedOption />
Вероятно, лучше задать это как отдельный вопрос, чтобы сохранить специфичность вопроса в соответствии с требованиями сайта, но, насколько я понимаю, selectedOption — это нет, действующий как модель для тега select, и на самом деле selectedOption даже не свойство дочернего компонента и не должно им быть.
Если вы хотите вызвать метод внутри вашего родительского компонента при «изменении значения выбора», лучше вызвать его внутри Vue смотреть, как показано ниже:
Parent component:
<template>
<my-drop-down :options = "options" v-model = "selectedOption" />
</template>
<script>
import myDropDown from "../components/baseDropdown.vue";
export default {
name: "parentModel",
data: () => ({
selectedOption: "2 Line",
// selectedIndex: 0,
options: ["1 Line", "2 Line", "3 Line"],
}),
components: {
myDropDown,
},
computed: {
/* It is better to use computed property for "selectedIndex", because it is related to "selectedOption" and changes accordingly. */
selectedIndex: function () {
return this.options.indexOf(this.selectedOption)
}
},
watch: {
selectedOption(newSelect, oldSelect) {
this.onSelectChange(this.selectedOption, this.selectedIndex)
}
},
methods: {
//How can I call this when the select value changes??
onSelectChange(selected, index) {
console.info(`Parent L3rd Change, name: ${selected}, index: ${index} `);
},
},
}
</script>
<style scoped>
</style>Child component:
<template>
<div>
<select
:value = "modelValue"
@change = "$emit('update:modelValue', $event.target.value)"
>
<!-- You can use v-model also here. But it only changes the value of "modelValue" and does not emit anything to parent component. -->
<!-- <select v-model = "modelValue">-->
<option v-for = "option in options" :key = "option">
{{ option }}
</option>
</select>
</div>
</template>
<script>
export default {
name: "baseDropdown",
props: ["options", "modelValue"],
emits: ["update:modelValue"],
/* --------------------------------- */
/* You don't need this method, because "$emit('update:modelValue', $event.target.value)" that is used in "select" tag itself is enough to emit data to the parent component. */
/* --------------------------------- */
// methods: {
// selected() {
//
// //??????
// //want to emit this to the parent
// let selectedIndex = this.$event.target.selectedIndex + 1
// //this.$emit(value, selectedIndex)
// },
// },
}
</script>
<style scoped>
</style>А по поводу второй части вашего вопроса:
v-model = "selectedOption" is the same as writing :value = "modelValue" @input = "$emit('update:modelValue', $event.target.value)"
На мой взгляд, это неверное утверждение по двум причинам:
v-model = "selectedOption" is the same as writing :value = "selectedOption" @input = "event => selectedOption = event.target.value"
вы не видите никаких $emit в приведенном выше заявлении. Но в вашем случае вы хотите передать данные родительскому компоненту.
change as an event вместо тега <select>.