0

I'm using Vue.js with Vuex modules. In almost every module I have an action & mutation called updateProp which looks like this:

updateProp ({commit}, payload) {
  commit('updateProp', payload)
}
updateProp (state, payload) {
  state[payload.propName] = payload.newVal
}

So then when I define some computed property that has a source in the Vuex I write:

myComputedValue: {
  get () {
    return this.$store.state.vuexModuleName.propertyName
  },
  set (newValue) {
    this.$store.dispatch('vuexModuleName/updateProp', {propName: 'propertyName', newVal: newValue})
  }
}

Very often I have to define multiple computed values like this, so I thought I'll create a global helper method instead:

Vue.prototype.$computedDefHelper = (computedPropModel, computedPropName) => {
  return {
    get () {
      return this.$store.state[computedPropModel][computedPropName]
    },
    set (newVal) {
      this.$store.dispatch(`${computedPropModel}/updateProp`, {propName: computedPropName, newVal: newVal})
    }
  }
}

So I'll be able to define these computed values in shorter way:

myComputedValue: this.$computedDefHelper('vuexModuleName', 'propertyName')

But this doesn't work - I get an error that $computedDefHelper is not a function - tried various things like using it like a mixing/plugin/non-arrow function etc. but nothing seem to work - is it even possible?

Any tips will be highly appreciated.

EDIT:

Only way I've managed it to work at the moment is to define it as a helper and import it in every module I want to use it so:

import { computedDefHelper } from '@/helpers/globalHelpers'

so then I can use it to define computed value:

myComputedValue: computedDefHelper('vuexModuleName', 'propertyName')

But I would like to avoid importing this and have it already built-in (globally) in every component.

EDIT 2:

I think this might be related with order of triggering / lifecycle Vue.js stuff, since if I console log this method in created hook, it looks fine, so probably it's something related that these computed methods object definitions are somehow parsed earlier than this global method is registered ?

EDIT 3:

I've created quickly a simpler version what I want to achieve (not working) at code sandbox to play around with: https://codesandbox.io/s/x3661n317o

lukaszkups
  • 5,790
  • 9
  • 47
  • 85
  • 1
    Possibly this would help: https://stackoverflow.com/questions/42613061/vue-js-making-helper-functions-globally-available-to-single-file-components – Varit J Patel Mar 13 '19 at 16:04
  • 1
    What about using mapGetters? https://vuex.vuejs.org/guide/getters.html – Matheus Valenza Mar 13 '19 at 16:13
  • 1
    @MatheusValenza `mapGetters` will only get the value. I want to set it as well. – lukaszkups Mar 13 '19 at 16:21
  • @varit05 thanks but I've seen that thread and tried it but without success - e.g. trying to use it as a mixing throws same error: `TypeError: this.computedDefHelper is not a function` – lukaszkups Mar 13 '19 at 16:31
  • 1
    @lukaszkups and what about `mapActions`? – Matheus Valenza Mar 13 '19 at 16:36
  • 1
    @MatheusValenza I knew you will ask :) mixing `mapGetters` (or `mapState`) with `mapActions` doesn't really make it shorter for me - I've wanted to use it as such one-liner really ;) – lukaszkups Mar 13 '19 at 16:40
  • 1
    @lukaszkups from your codesandbox, you can't define a computed property inside a component by calling `this`. Check this [post](https://forum.vuejs.org/t/cannot-seem-to-recognize-method-call-from-2-way-computed-property/9777/2): you need to define the function outside the `export default` so you can't call it from the Vue instance (defined in main.js) – Sovalina Mar 13 '19 at 18:21
  • yeah that's why I've asked if it's even possible - would really like to see an elegant way of solving it properly where I won't need to import or define it inside every component :| – lukaszkups Mar 13 '19 at 18:30
  • the only alternative now I see is to add it to `window` object to make it reusable? Not really an elegant solution imho :/ – lukaszkups Mar 13 '19 at 18:45

2 Answers2

2

You can define a mixin method in your globalHelpers.js, for example updater:

const computedDefHelper = {
  install(Vue, options) {
    Vue.mixin({
      methods: {
        updater: function(name, payload) {
          return this.$store.dispatch(`${name}/updateProp`, payload);
        }
      }
    });
  }
};
export default computedDefHelper;

Then import it in your main.js:

import computedDefHelper from '@/helpers/globalHelpers';
Vue.use(computedDefHelper);

You can now use it in every components like this:

this.updater('vuexModuleName', payload)

where payload can be reworked according to what you want as parameter.

Sovalina
  • 5,410
  • 4
  • 22
  • 39
  • thanks, tried it already to return `get` and `set` as a result for computed properties, but still receives `TypeError: this.computedDefHelper is not a function`. – lukaszkups Mar 13 '19 at 17:01
  • @lukaszkups `computedDefHelper` is the name of the plugin you import. Use the mixin method name inside the component (in my example it's `updater` but you can call any name you want) – Sovalina Mar 13 '19 at 17:04
  • yes I'm aware of that - I've renamed it to `computedDefHelper` for my case ;) – lukaszkups Mar 13 '19 at 17:05
1

I think you need to create a plugin for this and install this helper property with the Vue instance like this:

  import computedDefHelper from '@/helpers/globalHelpers'

  const Plugin = {

    install: (myVue, options) => {
      const aVue = myVue;
      aVue.prototype.$computedDefHelper = computedDefHelper;
    },
  };

  export default Plugin;

and in your main.js file like this:

Vue.use(Plugin);
Ronin Kr
  • 111
  • 1
  • still receives `TypeError: this.$computedValueHelper is not a function` when assigning it to the computed property - although if I console log it (`this.$computedDefHelper('modelName', 'propName')`) in `created` hook it looks fine. – lukaszkups Mar 13 '19 at 16:46