1

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

3 Answers 3

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 component 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>
Sign up to request clarification or add additional context in comments.

2 Comments

Neat! Didn't know that $nextTIck even exists. :)
The $nextTick I don't like for the same reason as setTimeout. The setVehicle in my real world app is not in the component. It's at the business/store level and should not be concerned with UI workarounds. v-show worked nicely though. For some reason I thought that was using visibility: hidden so I avoided it.
0

Here's my solution. Basically you shouldn't be afraid of v-model. Copy the values into the component 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

1 Comment

Thanks. I think the problem with this solution is the values are pulled from the global store at mount time. So if global state is changed, it's not reflected in this component which is the case in my real world app. But your answer of not being afraid of v-model inspired the answer I posted!
0

Found an alternative workaround that may resolve similar issues:

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

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


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

The question of why v-model behaves different than :value/@change combo 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 completely forgot about! I may see if this behavior is the same in Vue 3.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.