Vue 3 - 选择的绑定问题,如果所选选项未显示在新值中,则选择为空白,但不会更改为" "



我有两个组件,一个输入和一个选择,选择显示选项取决于你在输入中写的内容,但如果选择的选项没有显示在新选项中,选择默认选项或第一个选项" 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>

相关内容

  • 没有找到相关文章

最新更新