最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Vue.js - select element not updating when using :value, v-model is fine - Stack Overflow

programmeradmin0浏览0评论

I have two <select> elements. When I select a value in the first, I want to default the value for the second. It works with v-model, but not with :value.

One is a list of vehicles.

<select :value="vehicle" @change="setVehicle($event.target.value)">
  <option value="CAR">Car</option>
  <option value="PLANE">Plane</option>
</select>

The other is a list of parts. The WINGS part is only visible if PLANE is selected.

<select :value="part" @change="setPart($event.target.value)">
  <option value="ENGINE">Engine</option>
  <option value="WINDOWS">Wheels</option>
  <option v-if="vehicle === 'PLANE'" value="WINGS">Wings</option>
</select>

The setter for vehicle is trivial, but the default part for a plane is the conditionally rendered WINGS.

setVehicle: function(newVehicle) {
  this.vehicle = newVehicle;
  if (this.vehicle === "PLANE") {
    this.part = "WINGS";
  } else {
    this.part = "ENGINE";
  }
}

The reactive part data is set to the correct value, e.g. you can display it with {{ part }}, but the <select> element is not properly updated. If I change the part's select to use v-model it works fine:

<select v-model="part">

Using v-model is not an acceptable workaround because in my real world app the part is readonly and can only be mutated with a setter (using a store-like architecture). Another workaround is using a setTimeout(.., 1) in the setVehicle setter. I really do not like that solution. What I really would like to know is why v-model behaves differently than :value/@change when the documentation suggest v-model is simply syntactic sugar.

Demo: =/src/App.vue

I have two <select> elements. When I select a value in the first, I want to default the value for the second. It works with v-model, but not with :value.

One is a list of vehicles.

<select :value="vehicle" @change="setVehicle($event.target.value)">
  <option value="CAR">Car</option>
  <option value="PLANE">Plane</option>
</select>

The other is a list of parts. The WINGS part is only visible if PLANE is selected.

<select :value="part" @change="setPart($event.target.value)">
  <option value="ENGINE">Engine</option>
  <option value="WINDOWS">Wheels</option>
  <option v-if="vehicle === 'PLANE'" value="WINGS">Wings</option>
</select>

The setter for vehicle is trivial, but the default part for a plane is the conditionally rendered WINGS.

setVehicle: function(newVehicle) {
  this.vehicle = newVehicle;
  if (this.vehicle === "PLANE") {
    this.part = "WINGS";
  } else {
    this.part = "ENGINE";
  }
}

The reactive part data is set to the correct value, e.g. you can display it with {{ part }}, but the <select> element is not properly updated. If I change the part's select to use v-model it works fine:

<select v-model="part">

Using v-model is not an acceptable workaround because in my real world app the part is readonly and can only be mutated with a setter (using a store-like architecture). Another workaround is using a setTimeout(.., 1) in the setVehicle setter. I really do not like that solution. What I really would like to know is why v-model behaves differently than :value/@change when the documentation suggest v-model is simply syntactic sugar.

Demo: https://codesandbox.io/s/eager-liskov-2yor4?file=/src/App.vue

Share Improve this question asked May 9, 2020 at 7:06 nogridbagnogridbag 3,7135 gold badges44 silver badges54 bronze badges
Add a ment  | 

3 Answers 3

Reset to default 5

Since the third option is not in the DOM yet when you are updating this.part, it is not selected at that time. So you have two option:

1) Use nextTick() to wait for ponent update, then set the new value:

setVehicle: function(newVehicle) {
  this.vehicle = newVehicle;
  this.$nextTick(()=>{
    if (this.vehicle === "PLANE") {
      this.part = "WINGS";
    } else {
      this.part = "ENGINE";
    }
  })
}

or,

2) Keep the third option rendered, but hide it when the vehicle is car. In such way it will get selected, at the moment of changing the vehicle, and showed later when the DOM will be updated. You will do it with v-show instead of v-if.

<select :value="part" @change="setPart($event.target.value)">
  <option value="ENGINE">Engine</option>
  <option value="WINDOWS">Wheels</option>
  <option v-show="vehicle === 'PLANE'" value="WINGS">Wings</option>
</select>

Here's my solution. Basically you shouldn't be afraid of v-model. Copy the values into the ponent itself and use them as you please.

But I agree. :value is behaving weirdly, even if using .sync prop. (I suspect it's because of setting property vehicle to PLANE and v-if renders right after setting part WINGS. But not quite sure.)

My solution

Found an alternative workaround that may resolve similar issues:

Bind the <select> element using a puted property that simply returns the reactive part property.

<select v-model="partComputed" @change="setPart($event.target.value)">


puted: {
  partComputed: function () {
    return this.part;
  }
}

The question of why v-model behaves different than :value/@change bo is still open if anyone has insight into that. I tried Googling it, but the closest match was my own bug report I filed several years ago that I pletely forgot about! I may see if this behavior is the same in Vue 3.

发布评论

评论列表(0)

  1. 暂无评论