Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Dynamic `v-model` for computed properties - based on route param

I am building a component that can be used to set various vuex properties, depending on the name passed in the route. Here is the naive gist of it:

<template>
  <div>
    <input v-model="this[$route.params.name]"/>
  </div>
</template>

<script>
export default {
  computed: {
    foo: {
      get(){ return this.$store.state.foo; },
      set(value){ this.$store.commit('updateValue', {name:'foo', value}); }
    },
    bar: {
      get(){ return this.$store.state.bar; },
      set(value){ this.$store.commit('updateValue', {name:'bar', value}); }
    },
  }
}
</script>

Note that I pass this[$route.params.name] to the v-model, to make it dynamic. This works for setting (component loads fine), but when trying to set a value, I get this error:

Cannot set reactive property on undefined, null, or primitive value: null

I assume this is because this inside v-model becomes undefined (?)

How can I make this work?

UPDATE

I would also be curious to know why this does not work (compilation error):

<template>
  <div>
    <input v-model="getComputed()"/>
  </div>
</template>

<script>
export default {
  computed: {
    foo: {
      get(){ return this.$store.state.foo; },
      set(value){ this.$store.commit('updateValue', {name:'foo', value}); }
    },
    bar: {
      get(){ return this.$store.state.bar; },
      set(value){ this.$store.commit('updateValue', {name:'bar', value}); }
    },
  },
  methods: {
    getComputed(){
      return this[this.$route.params.name]
    }
  }
}
</script>
like image 285
Roy Prins Avatar asked Mar 05 '23 17:03

Roy Prins


1 Answers

Yeah everything inside your <template> is in the this scope, so this is undefined.

v-model is just a syntactic sugar for :value and @input, so you can handle this with a custom event and a computed property for :value.

You can also use a computed property with a getter and setter; Something like

computed: {
  model: {
    get: function () {
      return this.$store.state[this.$route.params.name]
    },
    set: function (value) {
      this.$store.commit('updateValue', { name: this.$route.params.name, value})
    }
  }
}

Edit If you have more logic to do in your setter, i'd separate it like so, keep the getter simple, and stick to one computed property;

computed: {
  model: {
    get: function () {
      return this.$store.state[this.$route.params.name]
    },
    set: function (value) {
      switch(this.$route.params.name) {
        case 'foo':
          return this.foo(value)
        default:
          return this.bar(value)
      }
    }
  }
},
methods: {
  foo(val) {
    this.$store.commit(...)
  },
  bar(val) {
    this.$store.commit(...)
  }
}
like image 195
Erik Terwan Avatar answered Apr 08 '23 22:04

Erik Terwan