Seth
Seth

Reputation: 2796

How should I structure multiple-use Vue components to load data from different Vuex paths?

I'd like to create a component that I can instantiate multiple times pointing at (loading its data from) different vuex-namespaces. The component will gets most of its data from Vuex. So say I have a Person component, I could instantiate many copies of the Person component, based on different paths into vuex.

The best way I've figured out how to do this is to pass a vuex path as a prop, but then I don't know how to use mapGetters and friends, because they require a namespace at the time the .vue file is instantiated.

I'd appreciate insight into the best "Vue way" to structure this. Here's the closest approach I've figured out at the moment.

Person.vue:

<template>
    <div>person {{name}} is {{age}} years old</div>
</template>

<script>
  export default {
    props: ['vuexNamespaceToLoadFrom'],

    // FIXME: how do I do a mapGetters with a dynamic namespace that's 
    // set through props???

    // I can't do the following since props aren't in scope yet :-(
    ...mapGetters(this.vuexNamespaceToLoadFrom, [ 'name', 'age'])

  }
</script>

Instantiating a few Person multiple-use components that load their properties from different vuex-namespaces:

<Person vuex-namespace-to-load-from="api/jim">
<Person vuex-namespace-to-load-from="api/someotherpath/rachid">
<div>
 <Person vuex-namespace-to-load-from="api/alternatepeople/grace">
</div>

Upvotes: 1

Views: 250

Answers (2)

Richard Matsen
Richard Matsen

Reputation: 23473

To expand the problem definition a little, this

export default {
  props: ['vuexNamespaceToLoadFrom'],
  ...
  computed: {
    ...mapGetters(this.vuexNamespaceToLoadFrom, [ 'name', 'age'])
  }
}

is a declarative object used by Vue to create instances of components, so instance properties can't be used directly in the helpers like mapGetters.

However, this discussion Generating computed properties on the fly shows a way to defer the evaluation of the instance property.

Essentially, the body of the computed get() will not be evaluated until the instance is fully mounted, so references to this.$store and this[namespaceProp] will work here.

Adapting it to your scenario,

helper function

function mapWithRuntimeNamespace({namespaceProp} = {}, props = []) {
  return props.reduce((obj, prop) => {
    const computedProp = {
      get() {
        return this.$store.getters[this[namespaceProp] + '/' + prop]
      }
    }
    obj[prop] = computedProp
    return obj
  }, {})
}

usage

export default {
  props: ['vuexNamespaceToLoadFrom'],
  ...
  computed: {
    ...mapWithRuntimeNamespace(
      { namespaceProp: 'vuexNamespaceToLoadFrom' }, 
      ['name', 'age']
    )
  }
}

Upvotes: 2

sarneeh
sarneeh

Reputation: 1388

How about this?

Person component:

export default (vuexNamespace) => ({
  ...mapGetters(vuexNamespace, ['name', 'age']) 
})

Parent component:

export default {
  components: {
    Person: Person('someNamespace')
  }
}

Didn't test it out, but I think it should work :)

Upvotes: 0

Related Questions