vue.jsbuefy

vuejs reactivity with variable initiated in mounted function


I have a dynamic component building multiple select, the template :

<template>
  <div>
    <b-field
      v-for="(key,index) in allSelects"
      :key="index"
      :label="key"
    >
      <b-select
        :placeholder="key"
        v-model="values[key]"
      >
        <option
          v-for="(value,index) in optionsValues(key)"
          :key="index"
          :value="value"
        >
          {{ value }}
        </option>
      </b-select>
    </b-field>
  </div>
</template>

and the script is

<script>
export default {
  name: 'Test',
  data () {
    return {
      allSelects: ['select1', 'select2'],
      values: {}
    }
  },
  computed: {
    optionsValues: function () {
      return key => { return this.getData(key) }
    }
  },
  methods: {
    getData: function (key) {
      if (key === 'select1') {
        return ['valA', 'valB', 'valC', 'valD']
      } else {
        if (this.values.select1 === 'valA') {
          return ['A', 'AA', 'AAA', 'AAAA', 'AAAAA']
        } else if (this.values.select1 === 'valB') {
          return ['B', 'BB', 'BBB', 'BBBB', 'BBBBB']
        } else if (this.values.select1 === 'valC') {
          return ['C', 'CC', 'CCC', 'CCCC', 'CCCCC']
        } else {
          return ['D', 'DD', 'DDD', 'DDDD', 'DDDDD']
        }
      }
    }
  },
  mounted () {
    // code
  }
}
</script>

In the mounted function, if I define the code as :

  mounted () {
    this.values = {
      select1: null,
      select2: null
    }
  }

Everything is working juste fine, the values of select1 change the values of select2, but if I dynamically build it as following :

  mounted () {
    for (let val of this.allSelects) {
      this.values[val] = null
    }
  }

it's no longer working ! I would like it to be dynamic, the content of allSelects is dynamic in my real component. What would be the best solution ?


Solution

  • You are using Vue 2, aren't you? To add a reactive property to an object in Vue 2 you have to set it with Vue.set() (see documentation). So this should work:

      mounted () {
        for (let val of this.allSelects) {
          Vue.set(this.values, val, null)
        }
      }
    

    Alternatively, you can initialize the object with the desired structure:

    const selectIds = ['select1', 'select2']
    export default {
      name: 'Test',
      data () {
        return {
          allSelects: selectIds.reduce( (s, i) => ({...s, [i]: null}), {}),
          values: selectIds,
        }
      },