Computed property setter creates Maximus stack exceeded

Viewed 43

I have such code:

<div id="app">
   <b-form-group label="Sorting">
     <b-form-checkbox-group
        v-model="sorting"
        :options="filterData.sorting"
      />
   </b-form-group>
</div>

new Vue({
  el: '#app',
  computed: {
    sorting: {
      get: function () {
        return this.filterInput.sorting
      },
      set: function (value) {
        // this array needs to always have only one value
        this.filterInput.sorting = [value[0]]
      }
    }
  },
  data () {
    return {
      filterData: {
        sorting: ['PRICE_ASC', 'PRICE_DESC']
      },
      filterInput: {
          sorting: []
      }
    }
  }
})

https://jsfiddle.net/pum86bsx/1/

Error seems to be in computed getter. When I comment it out it's all good. I have no idea why it's like that.

1 Answers

You setting the value of the v-model (the value returned in the getter), which will cause the setter to fire again.. and again.. and again.. Causing your stack to overflow.

Using checkboxes

You could instead use the @change event, which should only fire when the value actually changes.

new Vue({
  el: '#app',
  data () {
   return {
     filterData: {
       sorting: ['PRICE_ASC', 'PRICE_DESC']
      },
      filterInput: {
       sorting: []
      }
    }
  },
  methods: {
   onChange(value) {
     if(value.length === 0) {
    this.filterInput.sorting = value
      } else {
       this.filterInput.sorting = [value[value.length - 1]]
      }
    }
  }
})
<link href="https://unpkg.com/bootstrap@4.5.0/dist/css/bootstrap.min.css" rel="stylesheet" />
<link href="https://unpkg.com/bootstrap-vue@2.15.0/dist/bootstrap-vue.css" rel="stylesheet" />

<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.11/vue.js"></script>
<script src="https://unpkg.com/bootstrap-vue@2.15.0/dist/bootstrap-vue.js"></script>

<div id="app">
  <b-form-group label="Sorting">
     <b-form-checkbox-group
      v-model="filterInput.sorting"
      :options="filterData.sorting"
      @change="onChange"
      ></b-form-checkbox-group>
  </b-form-group>
</div>

Using radio buttons

Alternatively you could use radio buttons, which only allows one to be selected at a time, so you don't have to handle that yourself.

new Vue({
  el: '#app',
  data () {
   return {
     filterData: {
       sorting: ['PRICE_ASC', 'PRICE_DESC']
      },
      filterInput: {
       sorting: []
      }
    }
  }
})
<link href="https://unpkg.com/bootstrap@4.5.0/dist/css/bootstrap.min.css" rel="stylesheet" />
<link href="https://unpkg.com/bootstrap-vue@2.15.0/dist/bootstrap-vue.css" rel="stylesheet" />

<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.11/vue.js"></script>
<script src="https://unpkg.com/bootstrap-vue@2.15.0/dist/bootstrap-vue.js"></script>

<div id="app">
  <b-form-group label="Sorting">
     <b-form-radio-group
      v-model="filterInput.sorting"
      :options="filterData.sorting"
      ></b-form-radio-group>
  </b-form-group>
</div>

Related