@lianulloa/vuex-module-maker
v1.1.1
Published
A helper to reduce repetition inside Vuex modules
Downloads
2
Maintainers
Readme
VuexModuleMaker
It is just an helper to create Vuex modules. Its target is to reduce the amount of code needed to create a Vuex module and thus to keep code DRY
Quick Start
const state = {
categoriesPlan: [],
categoriesTrie: {},
groupingCodes: []
}
const vuexModule = new VuexModuleMaker(
{
state,
actions: {
createCategory: {
service: createCategoryAPI,
attr: "category"
},
getGroupingCodes: {
service: listGroupingCodesAPI,
attr: "groupingCodes"
}
}
}
).getModule()
This code creates a Vuex module following the mapping standard
of vuex-pathify, for example:
| state | getter | mutation | action | | ----- | ------ | -------- | ------ | | foo | foo | SET_FOO | setFoo |
Hence, assuming this module is registered under the name of categories
, then the next code is completely valid:
store.getters["categories/categoriesPlan"]
store.commit("categories/SET_GROUPING_CODES", groupingCodes)
store.dispatch("categories/createCategory", newCategory)
Complex Example
// usersApi is a class with methods to request info from API
import usersApi from "../../api/users"
import { VuexModuleMaker, actionConfigs } from "@lianulloa/vuex-module-maker"
const state = {
users: [],
totalUsers: 0
}
const vuexModule = new VuexModuleMaker({
state,
getters: {
activeUsers: state => {
return state.users.filter(user => user.status === "active")
},
coolUsers: state => {
return state.users.filter(user => user.settings.cool)
}
},
actions: {
getUsers: {
service: usersApi.list.bind(usersApi),
attr: "users",
cacheAPIRequestIn: "users/fetchUsers", // cache usersApi.list responses through this action. See vuex-cache
...actionConfigs.common.list
},
createCustomField: {
service: usersApi.create.bind(usersApi),
attr: "users",
cacheActionToDelete: "users/fetchUsers",
...actionConfigs.common.create
},
editCustomField: {
service: usersApi.edit.bind(usersApi),
attr: "users",
cacheActionToDelete: "users/fetchUsers",
editingRefreshService: usersApi.detail.bind(usersApi),
...actionConfigs.common.edit
},
deleteCustomField: {
service: usersApi.delete.bind(usersApi),
cacheActionToDelete: "users/fetchUsers"
}
}
})
export default vuexModule.getModule()
Clearing cache in all open tabs
Available from version 1.1.0+
Your user can have two or more tabs of your app open in the browser, and these tabs will be using different instances of your store, thus different instances of the cache. So it could be useful, that when some part of the cache gets cleaned in one tab, that change gets reflected in all open tabs.
To enable this behavior, takes just two simple steps:
- Register the plugin
createChannel
in the store
import { createChannel } from "@lianulloa/vuex-module-maker"
export default new Vuex.Store({
plugins: [ createChannel( {channelName:"any-name"} ) ],
modules: {
todos
}
})
- Set the module name as an option to the
VuexModuleMaker
constructor
const state = {
todos: []
}
const vuexModule = new VuexModuleMaker({
state,
actions: {
getC: {
service: list,
attr: "todos",
cacheAPIRequestIn: "todos/fetchTodos",
},
createC: {
service: create,
attr: "todos",
cacheActionToDelete: "todos/fetchTodos",
}
}
}, { namespaced: true, moduleName: "todos" }) // Note the field moduleName
export default vuexModule.getModule()
The module name MUST match with how you register the module at the first step
That's it. Now whenever one part of the store's cache gets cleared, it will do the same in all the open tabs of your app
API
VuexModuleMaker is just a class which generate a Vuex module from a few configurations
Constructor
The constructor takes two parameters. The second is completely optional.
The fist is an object which defines the usual options: state
, getters
, mutations
, actions
The second (and last) is an options object for Vuex. By default it look like this: { namespaced: true }
state
keeps its usual syntax. The syntax for getters
, mutations
, and actions
is described below
getters
- type:
Object.<string,function|string>
- Optional
Example:
const state = {
categoriesPlan: [],
categoriesTrie: [],
groupingCodes: []
}
const getters: {
"tree": "categoriesTrie",
"treeWithFunction": state => {
return state.categoriesTrie
}
}
These(☝) two getters are equivalents.
This way a custom name can be defined for a getter and/or defined explicitly what is returned by using a funciton.
Note: This parameter is optional. Getters are defined automatically based on the fields of state
mutations
- type:
Object.<string,function|string>
- Optional
Por ejemplo:
const state = {
categoriesPlan: [],
categoriesTrie: [],
groupingCodes: []
}
const mutations: {
"SET_CATEG_TRIE": "categoriesTrie",
"SET_CATEG_TRIE_2": (state, payload) => {
state["categoriesTrie"] = payload
}
}
These(☝) two mutations are equivalents.
This way a custom name can be defined for a mutation and/or defined explicitly what is setted by using a funciton.
Note: This parameter is optional. Mutations are defined automatically based on the fields of state
actions
- type:
Object.<string,Function| ActionConfig>
Below you can see what is an ActionConfig - Required
Example:
const state = {
category: null
categoriesPlan: [],
categoriesTrie: [],
groupingCodes: []
}
const actions = {
createCategory: {
service: createCategoryAPI,
attr: "category"
},
getCategories: (context, query) => {
return new Promise(async (resolve, reject) => {
try {
const { data } = await categoriesApiSet.list(query)
if (query.format === "plan") {
context.commit("SET_CATEGORIES_PLAN", data)
resolve(data)
} else {
context.commit("SET_CATEGORIES_TRIE", data)
resolve(data)
}
} catch (error) {
reject(error)
}
})
}
}
For actions
each key's value could be a function or an object with the config needed (See below). If a function is used, it MUST comply with the Vuex's action definition
If an object is used, it will generate an action which at least will:
- Execute the function passed as
service
field. Parameters received by the action (when it is dispatched) will be forwarded to thisservice
- Update
state
with the response fromservice
In order to update state
, it will use field attr
(or mutation
if it is defined)
In the example above, category
would be updated when action createCategory
is dispatched.
On the other side, if mutation
is defined, it will be used to update state
through a context.commit
call.
Example:
const actions = {
createCategory: {
service: createCategoryAPI,
mutation: "SET_CATEGORY_BY_HELPER"
}
}
will be the same as doing:
//...
const {data} = await createCategoryAPI()
context.commit("SET_CATEGORY_BY_HELPER", data)
//...
The field spreadServiceArgs
(See below) controls if actions arguments (when dispatched) should be spreaded when forwarded down to service
. Useful if service
expect more than one argument.
if (actionConfig.spreadServiceArgs) {
// if need to pass more than one parameter to service
data = (await actionConfig.service(...requestBody)).data
} else {
data = (await actionConfig.service(requestBody)).data
}
this way, it is possible to dispatch an action with an array of arguments de esta manera, es posible llamar a un action con un array de parámetros if necessary
this.actionToSetItems( [ item1, item2, item3 ] )
action.service( item1, item2, item3 )
Métodos
getModule
Creates the Vuex module
Example:
const state = {
categoriesPlan: [],
categoriesTrie: [],
groupingCodes: []
}
const vuexHelper = new VuexModuleMaker(
{
state,
actions: {
createCategory: {
service: createCategoryAPI,
attr: "category"
},
getGroupingCodes: {
service: listGroupingCodesAPI,
attr: "groupingCodes"
}
}
}
)
const store = new Vuex.Store({
modules: {
vuexModule: vuexHelper.getModule()
}
})
addGetter
Adds a getter
@param {Object.<string,function|string>} getter
Example:
const state = {
categoriesPlan: [],
categoriesTrie: [],
groupingCodes: []
}
const vuexHelper = new VuexModuleMaker(
{
state,
actions: {
createCategory: {
service: createCategoryAPI,
attr: "category"
},
getGroupingCodes: {
service: listGroupingCodesAPI,
attr: "groupingCodes"
}
}
}
)
vuexHelper.addGetter({customGetterName: "groupingCodes"})
vuexHelper.addGetter({
customGetterName: state => state.groupingCodes
}
)
addMutation
Adds a mutation
@param {Object.<string,function|string>} mutation
Example:
const state = {
categoriesPlan: [],
categoriesTrie: [],
groupingCodes: []
}
const vuexHelper = new VuexModuleMaker(
{
state,
actions: {
createCategory: {
service: createCategoryAPI,
attr: "category"
},
getGroupingCodes: {
service: listGroupingCodesAPI,
attr: "groupingCodes"
}
}
}
)
vuexHelper.addMutation({customGetterName: "groupingCodes"})
vuexHelper.addMutation({
customGetterName: (state, payload) => {
state["categoriesTrie"] = payload
}
)
addAction
Adds an action
@param {actionName, action} action
Example:
const state = {
categoriesPlan: [],
categoriesTrie: [],
groupingCodes: []
}
const vuexHelper = new VuexModuleMaker(
{
state,
}
)
vuexHelper.addAction({
createCategory: {
service: createCategoryAPI,
attr: "category"
}
})
vuexHelper.addAction({
getCategories: (context, query) => {
return new Promise(async (resolve, reject) => {
try {
//...
} catch (error) {
//...
}
})
}
)
buildGetters
Generates the getters
Example:
const state = {
categoriesPlan: [],
categoriesTrie: [],
}
const vuexHelper = new VuexModuleMaker(
//...
)
vuexHelper.buildGetters()
/*
{
categoriesPlan: state => state.categoriesPlan
categoriesTrie: state => state.categoriesTrie
}
*/
buildMutations
Generates the mutations
Example:
const state = {
categoriesPlan: [],
categoriesTrie: [],
}
const vuexHelper = new VuexModuleMaker(
//...
)
vuexHelper.buildMutations()
/*
{
SET_CATEGORIES_PLAIN: (state, payload) => state.categoriesPlan = payload
SET_CATEGORIES_TREE: (state, payload) => state.categoriesTrie = payload
}
*/
buildActions
Generates the actions
Example:
const state = {
categoriesPlan: [],
categoriesTrie: [],
}
const vuexHelper = new VuexModuleMaker({
actions: {
createCategory: {
service: createCategoryAPI,
attr: "category"
}
}
}
)
vuexHelper.buildActions()
/*
{
createCategory: (context, argument) => new Promise(() => {
// basic action logic
})
}
*/
ActionConfig
The action configuration object
- @typedef {Object} ActionConfig
- @property {Function} service - Service used to obtain data
- @property {String} attr - The attribute of the state which will contain the data (if any)
- @property {String} [mutation] - Mutation to used to set data when it is not the default for the attr
- @property {boolean} [spreadServiceArgs] - Whether action argument should be spreaded into the service
- @property {boolean} [append] - Whether data should be appended to existing data in a list format if requestBody.append === true
- @property {boolean} [appendAlways] - Whether data should be appended to existing data in a list format even though is not requested
- @property {boolean} [editing] - Whether it is an editing action. CANNOT be used along with append or appendAlways
- @property {Function} [editingRefreshService] - Service used to refresh an edited document. MUST be defined if requestBody.refresh
- @property {boolean} [hasMetadata] - Whether response will included a metadata field. If metadata is true, then mutation MUST NOT be defined
- @property {String} [cacheAPIRequestIn] - Name of an action, automatically created, to cache API resquest made through service
- @property {boolean} [cacheActionToDelete] - Name of an action to delete from cache after the request to API. If it is not in cache, it does nothing
Dependencies
- vuex-cache It MUST be installed by consumer in order to cache actions