我有两个组件,一个输入和一个选择,选择显示选项取决于你在输入中写的内容,但如果选择的选项没有显示在新选项中,选择默认选项或第一个选项" Select option">
当我选择一个选项,然后在选择中更改选项,选择的选项不在新选项中,选择显示为空白,但当我将选择的选项更改为",选择不改变它(选择选项,有值"),我不知道为什么,但如果我更改为其他选项,选择更改为它…
例子:
select option 2>在输入
中写入3个字符可以看到,与选择绑定的变量option_selected
更改为",但选择没有更改为" select option">
组件自定义事件
const input_text = {
name: "input-min-length",
props: {
text: String,
is_min_length: Boolean
},
emits: ["update:text", "update:is_min_length"],
computed: {
is_min(){
return this.text.length >= 3;
}
},
watch: {
is_min(new_value){
this.$emit("update:is_min_length", this.is_min);
}
},
template: `<div><input type="text" :value="text" @input="$emit('update:text', $event.target.value)" /></div>`
};
const select_options = {
name: "select-options",
props: {
option_selected: String,
is_min_length: Boolean
},
emits: ["update:option_selected"],
data() {
return {
options: [
{text: "Select option", value: ""},
{text: "option 1", value: "1", is_min_length: true},
{text: "option 2", value: "2"},
{text: "option 3", value: "3", is_min_length: true},
]
};
},
computed: {
options_filtered(){
if(this.is_min_length == false) return this.options;
return this.options.filter((option) => option.is_min_length == true || option.value == "")
}
},
watch: {
is_min_length(){
const is_presented = this.options_filtered.some((option) => option.value == this.option_selected)
if (is_presented == false){
setTimeout(() => {
this.$emit("update:option_selected", "");
}, 0); // I try to use setTimeout, to see if it changes at all
}
}
},
template: `
<select :value="option_selected" @change="$emit('update:option_selected', $event.target.value)">
<option v-for="option in options_filtered" :value="option.value" :key="option.value">
{{ option.text }}
</option>
</select>
`
}
const app = {
components:{
"input-min-length": input_text,
"select-options": select_options
},
data() {
return {
text: "",
is_min_length: false,
option_selected: ""
}
},
template: `
<div>
<input-min-length v-model:text="text" v-model:is_min_length="is_min_length" />
<select-options v-model:option_selected="option_selected" :is_min_length="is_min_length" /><br>
<button @click="option_selected='1'">change to opt 1</button><br>
<button @click="option_selected=''">change to opt ""</button><br>
<div>
<strong>DATA:</strong><br>
<strong>text:</strong> "{{text}}"<br>
<strong>is_min_length:</strong> {{is_min_length}}<br>
<strong>option_selected:</strong> "{{option_selected}}"
</div>
</div>`
}
Vue.createApp(app)
.mount('#app')
<script src="https://unpkg.com/vue@next"></script>
<div id="app"></div>
哇,这是一个棘手的深入。
什么是"value">
所以<select>
元素没有HTMLvalue
属性,但是HTMLSelectElement
上的DOM API提供了一个el.value
属性,它给了你所选择的选项的值。Vue提供了对value
属性的绑定(通过v-model
使用)。这就是为什么我们可以简单地在<select>
元素中使用:value
。
代码问题:
当'option 2'被选中,然后通过optionsFiltered
从DOM中移除时,它将<select>
元素设置为无效状态。在无效状态下,元素的el.value
返回''
(一个空字符串)(注意:这是DOM API,不是Vue)。现在,is_min_length
上的监视器被触发并发出一个update:option_selected
事件,其值为''
(一个空字符串)。如你所知,Vue是反应性的。由于el.value
已经是''
,我认为Vue不需要更新DOM,因此从不调用el.value = ''
。(设置el.value
为空字符串,即使它已经是一个空字符串确实给出了期望的行为,DOM API似乎相当健壮,它的Vue不调用它)。
解决方案(s):
- 最简单的方法是设置默认的"选择选项"。值设置为
''
以外的值,这样它就不会与无效状态的''
冲突。例如,您可以将其设置为0
。或者是字符串'none'
。
const input_text = {
name: "input-min-length",
props: {
text: String,
is_min_length: Boolean
},
emits: ["update:text", "update:is_min_length"],
computed: {
is_min(){
return this.text.length >= 3;
}
},
watch: {
is_min(new_value){
this.$emit("update:is_min_length", this.is_min);
}
},
template: `<div><input type="text" :value="text" @input="$emit('update:text', $event.target.value)" /></div>`
};
const select_options = {
name: "select-options",
props: {
option_selected: String,
is_min_length: Boolean
},
emits: ["update:option_selected"],
data() {
return {
options: [
{text: "Select option", value: "0"},
{text: "option 1", value: "1", is_min_length: true},
{text: "option 2", value: "2"},
{text: "option 3", value: "3", is_min_length: true},
]
};
},
computed: {
options_filtered(){
if(this.is_min_length == false) return this.options;
return this.options.filter((option) => option.is_min_length == true || option.value == "0")
}
},
watch: {
is_min_length(){
const is_presented = this.options_filtered.some((option) => option.value == this.option_selected)
if (is_presented == false){
setTimeout(() => {
this.$emit("update:option_selected", '0');
}, 0); // I try to use setTimeout, to see if it changes at all
}
}
},
template: `
<select :value="option_selected" @change="$emit('update:option_selected', $event.target.value)">
<option v-for="option in options_filtered" :value="option.value" :key="option.value">
{{ option.text }}
</option>
</select>
`
}
const app = {
components:{
"input-min-length": input_text,
"select-options": select_options
},
data() {
return {
text: "",
is_min_length: false,
option_selected: "0"
}
},
template: `
<div>
<input-min-length v-model:text="text" v-model:is_min_length="is_min_length" />
<select-options v-model:option_selected="option_selected" :is_min_length="is_min_length" /><br>
<button @click="option_selected='1'">change to opt 1</button><br>
<button @click="option_selected='0'">change to opt ""</button><br>
<div>
<strong>DATA:</strong><br>
<strong>text:</strong> "{{text}}"<br>
<strong>is_min_length:</strong> {{is_min_length}}<br>
<strong>option_selected:</strong> "{{option_selected}}"
</div>
</div>`
}
Vue.createApp(app)
.mount('#app')
<script src="https://unpkg.com/vue@next"></script>
<div id="app"></div>
- 另一个解决方案是在发出事件的同时调用
el.value = ''
,但我不建议这样做,因为这会使您的代码对其他Vue开发人员更难理解(即使您是唯一一个在项目上工作的人,您也应该坚持良好的编码实践):
const input_text = {
name: "input-min-length",
props: {
text: String,
is_min_length: Boolean
},
emits: ["update:text", "update:is_min_length"],
computed: {
is_min(){
return this.text.length >= 3;
}
},
watch: {
is_min(new_value){
this.$emit("update:is_min_length", this.is_min);
}
},
template: `<div><input type="text" :value="text" @input="$emit('update:text', $event.target.value)" /></div>`
};
const select_options = {
name: "select-options",
props: {
option_selected: String,
is_min_length: Boolean
},
emits: ["update:option_selected"],
data() {
return {
options: [
{text: "Select option", value: ""},
{text: "option 1", value: "1", is_min_length: true},
{text: "option 2", value: "2"},
{text: "option 3", value: "3", is_min_length: true},
]
};
},
computed: {
options_filtered(){
if(this.is_min_length == false) return this.options;
return this.options.filter((option) => option.is_min_length == true || option.value == "")
}
},
watch: {
is_min_length(){
const is_presented = this.options_filtered.some((option) => option.value == this.option_selected)
if (is_presented == false){
setTimeout(() => {
this.$emit("update:option_selected", "");
document.getElementById("myselect").value = "";
}, 0); // I try to use setTimeout, to see if it changes at all
}
}
},
template: `
<select id="myselect" :value="option_selected" @change="$emit('update:option_selected', $event.target.value)">
<option v-for="option in options_filtered" :value="option.value" :key="option.value">
{{ option.text }}
</option>
</select>
`
}
const app = {
components:{
"input-min-length": input_text,
"select-options": select_options
},
data() {
return {
text: "",
is_min_length: false,
option_selected: ""
}
},
template: `
<div>
<input-min-length v-model:text="text" v-model:is_min_length="is_min_length" />
<select-options v-model:option_selected="option_selected" :is_min_length="is_min_length" /><br>
<button @click="option_selected='1'">change to opt 1</button><br>
<button @click="option_selected=''">change to opt ""</button><br>
<div>
<strong>DATA:</strong><br>
<strong>text:</strong> "{{text}}"<br>
<strong>is_min_length:</strong> {{is_min_length}}<br>
<strong>option_selected:</strong> "{{option_selected}}"
</div>
</div>`
}
Vue.createApp(app)
.mount('#app')
<script src="https://unpkg.com/vue@next"></script>
<div id="app"></div>