Vue computed setter get not triggered for array in v-model

4.7k views Asked by At

I have a string pipe of values, which I want to modify. the string pipe has a range from 0-5 values, so I made the following:

new Vue({
  el: '#app',
  data: {
    valuesString: ""
  },
  computed: {
    values: {
      get() {
       var values = this.valuesString ? this.valuesString.split("-") : [];
        if(values.length < 5)
         values.push(null);
        return values;
      },
      set(values) {
        this.valuesString = values.filter(value => value).join("-")
      }
    }
  }
});
<script src="//cdnjs.cloudflare.com/ajax/libs/vue/2.3.4/vue.min.js"></script>
<div id="app">
  <div v-for="(value, i) in values" :key="i">
    <select v-model="values[i]" style="width: 200px">
      <option></option>
      <option>1</option>
      <option>2</option>
      <option>3</option>
      <option>4</option>
      <option>5</option>
    </select>
  </div>
  <br>
  <span>valuesString: {{ valuesString }}</span>
  <br>
  <span>values: {{ values }}</span>
</div>

The problem now is, that the setter of my computed values property getn't called.

my usecase is a filter like a categories filter. I get the valuesString as parameter from my router. The user should be able to select 1-5 categories to filter. every time the filter changes the router paramter should change and a new empty select should appear until there are 5 categories set.

2

There are 2 answers

0
wendt88 On

maby my question wasn't clear. Bert's comment is right

I solved my problem with a workaround, but I'm not so satisfied. maby anyone can help me to optimize it. For this I tried to use computed setters.

new Vue({
  el: '#app',
  data: {
    values: [],
    valuesString: "",
    allValues: [...Array(5).keys()]
  },
  watch: {
    values (val) {
      if(val.toString() != this.filteredValues.toString()  && val.toString() != this.filteredValues.concat([null]).toString())
        this.$set(this, 'values', this.filteredValues);
      else if (this.valuesString != this.filteredValues.join('-'))
        this.$set(this, 'valuesString', this.filteredValues.join('-'));
    },
    valuesString (valuesString) {
      this.$set(this, 'values', valuesString.split('-'));
    }
  },
  computed: {
    filteredValues () {
      return this.values.filter(v => v);
    },
    modelArray () {
      let size = this.filteredValues.length;
      if(size < 5)
        size++;
      return new Array(size);
    }
  }
});
<script src="//cdnjs.cloudflare.com/ajax/libs/vue/2.3.4/vue.min.js"></script>
<div id="app">
  <div v-for="(value, i) in modelArray" :key="i">
    <select v-model="values[i]" style="width: 200px">
      <option></option>
      <option v-for="(v, j) in allValues" :key="j" v-if="v == values[i] || filteredValues.indexOf(v.toString()) === -1">{{ v }}</option>
    </select>
  </div>
  <br>
  <span>valuesString: <input v-model="valuesString" /></span>
  <br>
  <span>values: {{ values }}</span>
  <br>
  <span>filteredValues: {{ filteredValues }}</span>
</div>

0
retrograde On

I'm not entirely sure I understand what you are looking for, but I think this will do the trick:

  • Parse the piped string and place it in an array
    • See the computed property unpiped
  • loop through the array and place the value in the selectbox
    • the loop should occur on the . The result is bound to valuesString
  • The selected value should be pushed to a results array and the selectbox should be cleared.
    • see the pushValues method.

Jfiddle example

<div id="app">
 <div>
   <select v-model="valuesString" @change="pushValues" style="width: 200px">
    <option v-for="p in unpiped">{{p}}</option>
    </select>
  </div>
<br>
  <span>valuesString: {{ valuesString }}</span>
<br>
  <span>unpiped: {{ unpiped }}</span>
 <br>
   <span>results: {{ results }}</span>
</div>

JS:

new Vue({
el: '#app',
 data: {
  valuesString: "",
  piped: "1|2|3|4|5",
  results: []
 },
 computed: {
  unpiped(){
    var v = this.piped.split("|");
    return v;
 },
},
 methods: {
   pushValues: function(){
    this.results.push(this.valuesString)
    this.valuesString = ""
 }
}
});