dynamically change select options in vue.js changes the display value

13,337

Whenever you are iterating with v-for you should add a key property.

When Vue is updating a list of elements rendered with v-for, it by default uses an “in-place patch” strategy. If the order of the data items has changed, instead of moving the DOM elements to match the order of the items, Vue will simply patch each element in-place and make sure it reflects what should be rendered at that particular index.

If you change your template to the following it should work the way you expect.

<select :value="value" 
  ref= 'input'
  v-on:input="onChange($event.target.value)">
  <option value=""> {{firstOption}} </option>
  <option v-for="t in getHours()" :value="t" :key="t">
    {{ t }}
  </option>
</select>

Updated fiddle.

The code still has a bug, however. If you pick a start date and end date, and then pick a start date that makes the end date invalid (because the start now starts after the end date), then your Vue (what you're calling vm) will contain an invalid end date, even though the end date select list will look like the selected end date was cleared. This is why I recommended above that the end date be cleared when the start date changes. If you do not, then you will need to check the validity of the end date whenever the start date changes.

Share:
13,337
Circuit Breaker
Author by

Circuit Breaker

Updated on June 29, 2022

Comments

  • Circuit Breaker
    Circuit Breaker almost 2 years

    I have created a custom component for select that shows all the time for a single day in 15 min interval. I can also pass a optional min-start-time value. In my html, I have used two of these components. I have defined the component as

    Vue.component('time-select', {
       template: `
        <select :value="value" 
          ref= 'input'
          v-on:input="onChange($event.target.value)">
            <option value=""> {{firstOption}} </option>
            <option v-for="t in getHours()" :value="t">
              {{ t }}
            </option>
        </select>
     `,
     props: {
        value: String,
        startTime: String,
        firstOption: {
          type: String,
          default: "Select"
        },
        step: {
          type: Number,
          default: 15
        }
     },
     methods: {
       getHours: function() {    
          var startHr = 0;
          var startMin = 0;
          if (this.startTime) {
             var timeParts = this.startTime.split(/[\s:]+/);
             startHr = parseInt(timeParts[0]);
             startMin = parseInt(timeParts[1]);    
             var ampm = timeParts[2];
             if(ampm == "PM" && startHr < 12)
               startHr = startHr + 12;
             else if(ampm == "AM" && startHr === 12)
               startHr = startHr - 12;
         }      
    
         var hours = [];
         var start = new Date(1,1,1,startHr,startMin);  
         var end = new Date(1,1,2,0,0);
         if(this.startTime)
         {
            start.setMinutes(start.getMinutes() + this.step);
         }
         for (var d = start; d < end; d.setMinutes(d.getMinutes() + this.step)) 
         {          
            hours.push(this.format(d));
         }
    
          return hours;
       },
       format: function(date)
       {
          var hours = date.getHours();
          var minutes = date.getMinutes();
          var ampm = hours < 12? "AM" : (hours=hours%12,"PM");
          hours =  hours == 0? 12 : hours < 10? ("0" + hours) : hours;
          minutes = minutes < 10 ? ("0" + minutes) : minutes;
          var ret = hours + ":" + minutes + " " + ampm;
          return ret;
       },
       onChange: function(val) {
          this.$emit('input', val);
       },
    }
    });
    

    JsFiddle link is https://jsfiddle.net/xsnswxu1/1/

    Now, if you select any value (e.g. 01:30 am) from the first drop down and any value (e.g. 03:30 am) from the second dropdown, and then again change the value of first dropdown to 12:30 am, the value of second dropdown automatically changes (for this example, it is 02:30 am). VM data retains the same though. When I change the first dropdown, the second dropdown value shouldn't be changed. Any idea what I'm doing wrong. VM is not updated which is good but the second dropdown display value is changing if I change the first dropdown.

  • Circuit Breaker
    Circuit Breaker almost 7 years
    Thank you so much. It works now. And yes, I would clear the end date when it becomes invalid.
  • Circuit Breaker
    Circuit Breaker almost 7 years
    I added a method to clear the end time when it is invalid. Fiddle Link
  • Circuit Breaker
    Circuit Breaker almost 7 years
    I have found that getHours() method is executed each time I change the dropdown value. Is there any better way to do it?