[Vuejs]-Stop multiple method recursive calls from dependent inputs in Vue.js

0👍

Instead of calling update in the watchers for shop and nonShop, just add a change event handler to the selects to call update that way instead:

<select v-model="input.value" @change="update">...</select>

This way, the update method will be called whenever the user changes the value of an input, not when the value of an input gets updated by Vue.

Here’s a full working example:

Vue.config.devtools = false;
Vue.config.productionTip = false;

let vueApp = new Vue ({
  el: '#app',
  data() {
    return {
      inputs: [{
        name: 'shop', 
        value: 'all', 
        options: [
          {id: 'all', name: 'All shops'}, 
          {id: 1, name: 'Shop 1'}, 
          {id: 2, name: 'Shop 2'}, 
          {id: 3, name: 'Shop 3'}
        ]
      }, {
        name: 'country', 
        value: 'all', 
        options: [
          {id: 'all', name: 'All countries'}, 
          {id: 1, name: 'Country 1'}, 
          {id: 2, name: 'Country 2'}, 
          {id: 3, name: 'Country 3'}
        ]
      }, {
        name: 'gender', 
        value: 'all', 
        options: [
          {id: 'all', name: 'All genders'}, 
          {id: 1, name: 'Gender 1'}, 
          {id: 2, name: 'Gender 2'}, 
          {id: 3, name: 'Gender 3'}
        ]
      }]
    };
  },
  computed: {
    shop() {
      return this.inputs.filter(d => d.name === 'shop')[0].value;
    }
  },
  watch: {
    shop() {
      $.when((async function() {
        setTimeout(() => {
          let newCountries = [];
          for (let i = 1; i < 4; i++) {
            let rand = Math.floor(Math.random() * 100);
            newCountries.push({id: rand, name: 'Country: ' + rand});
            let countryObject = vueApp.inputs.filter(d => d.name === 'country')[0];
            countryObject.options = [{id: 'all', name: 'All countries'}, ...newCountries];
            countryObject.value = 'all';
          }					
        }, 200);
      })(), (async function() {
        setTimeout(() => {
          let newGenders = [];
          for (let i = 1; i < 4; i++) {
            let rand = Math.floor(Math.random() * 100);
            newGenders.push({id: rand, name: 'Gender: ' + rand});
            let genderObject = vueApp.inputs.filter(d => d.name === 'gender')[0];
            genderObject.options = [{id: 'all', name: 'All genders'}, ...newGenders];
            genderObject.value = 'all';
          }					
        }, 100);			 
      })());
    },
  },
  methods: {
    update() {
      console.log('updating');
    }
  }
});
<script src="https://unpkg.com/vue/dist/vue.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

<div id='app'>
  <div style='list-style-type: none' v-for="input in inputs">
    <p><b>{{ input.name }}</b></p>
    <select v-model="input.value" @change="update">
      <option v-for="option in input.options" :value="option.id" >{{ option.name }}</option>
    </select>
  </div>
</div>

If you need the country and gender values to change before calling the update method, then add a separate method as a handler for the change event to set the new country and gender values and then call the update method after that has finished.

Here’s and example of that:

Vue.config.devtools = false;
Vue.config.productionTip = false;

let vueApp = new Vue ({
  el: '#app',
  data() {
    return {
      inputs: [{
        name: 'shop', 
        value: 'all', 
        options: [
          {id: 'all', name: 'All shops'}, 
          {id: 1, name: 'Shop 1'}, 
          {id: 2, name: 'Shop 2'}, 
          {id: 3, name: 'Shop 3'}
        ]
      }, {
        name: 'country', 
        value: 'all', 
        options: [
          {id: 'all', name: 'All countries'}, 
          {id: 1, name: 'Country 1'}, 
          {id: 2, name: 'Country 2'}, 
          {id: 3, name: 'Country 3'}
        ]
      }, {
        name: 'gender', 
        value: 'all', 
        options: [
          {id: 'all', name: 'All genders'}, 
          {id: 1, name: 'Gender 1'}, 
          {id: 2, name: 'Gender 2'}, 
          {id: 3, name: 'Gender 3'}
        ]
      }]
    };
  },
  computed: {
    shop() {
      return this.inputs.filter(d => d.name === 'shop')[0].value;
    }
  },
  methods: {
    onChange(name) {
      if (name === 'shop') {
        $.when((async function() {
          setTimeout(() => {
            let newCountries = [];
            for (let i = 1; i < 4; i++) {
              let rand = Math.floor(Math.random() * 100);
              newCountries.push({id: rand, name: 'Country: ' + rand});
              let countryObject = vueApp.inputs.filter(d => d.name === 'country')[0];
              countryObject.options = [{id: 'all', name: 'All countries'}, ...newCountries];
              countryObject.value = 'all';
            }					
          }, 200);
        })(), (async function() {
          setTimeout(() => {
            let newGenders = [];
            for (let i = 1; i < 4; i++) {
              let rand = Math.floor(Math.random() * 100);
              newGenders.push({id: rand, name: 'Gender: ' + rand});
              let genderObject = vueApp.inputs.filter(d => d.name === 'gender')[0];
              genderObject.options = [{id: 'all', name: 'All genders'}, ...newGenders];
              genderObject.value = 'all';
            }					
          }, 100);			 
        })()).then(this.update);
      } else {
        this.update()
      }
    },
    update() {
      console.log('updating');
    }
  }
});
<script src="https://unpkg.com/vue/dist/vue.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

<div id='app'>
  <div style='list-style-type: none' v-for="input in inputs">
    <p><b>{{ input.name }}</b></p>
    <select v-model="input.value" @change="onChange(input.name)">
      <option v-for="option in input.options" :value="option.id" >{{ option.name }}</option>
    </select>
  </div>
</div>

Leave a comment