Reputation: 2430
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.
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
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:
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