Willem-Aart
Willem-Aart

Reputation: 2430

VueJS component system architecture

What I'm trying to accomplish

I'm trying to figure out a good architecture for the application I'm working on.

If you look at some screenshots of the current version of our application you'll see tables/lists of data in various formats. The table in the second screenshot has its own navigation. In some cases, table rows can be selected. In other screens, the list of items can be filtered on specified keywords, and so on.

The front-end is going to be rebuilt in VueJS. Instead of building one-off components for each specific screen, I'd like to have a more generic solution that I can use throughout the application.

What I need is a system of components where the root contains a set of data and the children provide (possibly multiple) representations of that data. The root component also contains – mostly user-specified – config, filters and selections which is used to control the representations.

What I'm trying to achieve is best illustrated with some pseudocode.

Questions

What is the best way to store the data? How should the child components access this data?

Passing the data-display's data down with props to each of its children seems cumbersome to me. Especially since the representations have implicit access to this data.

Should I use Vuex for this? How would that work with multiple instances of data-display? I read something about dynamic module registration, could that be useful?

Are there better ways? Any help is greatly appreciated!

Upvotes: 2

Views: 546

Answers (1)

Richard Matsen
Richard Matsen

Reputation: 23463

I'm not sure I grasp all the details of your requirements, but understand data is common across multiple 'pages' in the SPA. I would definitely recommend Vuex:

  • same data for all pages
  • access via component computed properties, which are reactive to store changes but also cached to save recomputing if no changes in dependencies
  • saves a lot of potentially complex passing of data in props (passing downwards) and events (passing upwards)

Looking at the pseudocode, you have three types of filters on the sidebar. Put that data in the store as well, then computed properties on views can apply filters to the data. Computation logic is then easily tested in isolation.

child.vue

<template>
  <div>{{ myChildData.someProperty }}</div>
</template>

<script>
export default {
  props: [...],
  data: function () {
    return {
      selection: 'someValue'  // set this by, e.g, click on template
    }
  },
  computed: {
    myChildData() {
      return this.$store.getters.filteredData(this.selection)
    }
  }
}
</script>

store.js

const state = {
  myData: {
    ...
  },
  filters: {  // set by dispatch from sidebar component
    keyword: ...,
    toggle: ...,
    range: ...
  },
}

const getters = {
  filteredData: state => {
    return childSelection => {  // this extra layer allows param to be passed
      return state.myData
       .filter(item => item.someProp === state.filters.keyword)
       .filter(item => item.anotherProp === childSelection)
    }
  },
}

const mutations = {
  'SET_DATA' (state, payload) {
    ...
  },
  'SET_FILTER' (state, payload) {
    ...
  }
}

export default {
  state,
  getters,
  mutations,
}

Generic children

There are probably many ways to tackle this. I've used composition, which means a thin component for each child each using a common component, but you only need this if the child has specific data or some unique markup which can be put into a slot.

child1.vue

<template>
  <page-common :childtype="childType">
    <button class="this-childs-button" slot="buttons"></button>
  </page-common>
</template>

<script>
import PageCommon from './page-common.vue'
export default {
  props: [...],
  data: function () {
    return {
      childType: 'someType'
    }
  },
  components: {
    'page-common': PageCommon,
  }
}
</script>

page-common.vue

<template>
  ...
  <slot name="buttons"></slot>
</template>

<script>
export default {
  props: [
    'childtype'
  ],
  data: function () {
    return {
      ...
    }
  },
}
</script>

Multiple Instances of Data

Again, many variations - I used an object with properties as index, so store becomes

store.js

const state = {
  myData: {
    page1: ...,  // be sure to explicitly name the pages here
    page2: ...,  // otherwise Vuex cannot make them reactive
    page3: ...,
  },
  filters: {  // set by dispatch from sidebar component
    keyword: ...,
    toggle: ...,
    range: ...
  },
}

const getters = {
  filteredData: state => {
    return page, childSelection => {  // this extra layer allows param to be passed
      return state.myData[page]  // sub-select the data required
       .filter(item => item.someProp === state.filters.keyword)
       .filter(item => item.anotherProp === childSelection)
    }
  },
}

const mutations = {
  'SET_DATA' (state, payload) {
    state.myData[payload.page] = payload.myData
  },
  'SET_FILTER' (state, payload) {
    ...
  }
}

export default {
  state,
  getters,
  mutations,
}

Upvotes: 1

Related Questions