My current setup involves an action named fetchUserPermissions
that retrieves a permission set from an api endpoint using axios. This action is triggered by another action called init, which is automatically executed through the utility dispatchActionForAllModules
. This utility searches for the init action in all modules/submodules and executes it. Although this mechanism seems to be functioning correctly, the issue arises when the api endpoint returns HTML instead of the expected JSON response. The returned HTML content looks something like this:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Title</title> </head> <body> <noscript> <strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong> </noscript> <div id="app"></div> <script type="text/javascript" src="http://0.0.0.0:8080/bundle.js" ></script> </body> </html>
It's worth noting that I am using Vue with Django through the webpack_loader
package. The HTML content mentioned above is the index.html file that serves the Vue application.
Interestingly, when I dispatch the action within the created()
hook of a component, it works as expected and returns a JSON response like this:
{ "id": 1, "is_staff": true, "is_superuser": true, "permissions": [ "view_user" ], "group_permissions": [ "has_users_export" ] }
.
The dispatchActionForAllModules
utility is essentially copied from the vue-enterprise-bolierplate repository.
How can I ensure that the endpoint returns the JSON response as expected?
state/store.js
import Vue from 'vue'
import Vuex from 'vuex'
import dispatchActionForAllModules from '@/utils/dispatch-action-for-all-modules'
import modules from './modules'
Vue.use(Vuex)
const store = new Vuex.Store({
modules,
strict: process.env.NODE_ENV !== 'production',
})
export default store
// automatically run the `init` action for every module,
// if one exists.
dispatchActionForAllModules('init')
state/modules/index.js
import camelCase from 'lodash/camelCase'
const modulesCache = {}
const storeData = { modules: {} }
;(function updateModules() {
const requireModule = require.context(
// Search for files in the current directory.
'.',
// Search for files in subdirectories.
true,
// Include any .js files that are not this file or a unit test.
/^((?!index|\.unit\.).)*\.js$/
)
// For every Vuex module...
requireModule.keys().forEach((fileName) => {
const moduleDefinition =
requireModule(fileName).default || requireModule(fileName)
// Skip the module during hot reload if it refers to the
// same module definition as the one we have cached.
if (modulesCache[fileName] === moduleDefinition) return
// Update the module cache, for efficient hot reloading.
modulesCache[fileName] = moduleDefinition
// Get the module path as an array.
const modulePath = fileName
// Remove the "./" from the beginning.
.replace(/^\.\//, '')
// Remove the file extension from the end.
.replace(/\.\w+$/, '')
// Split nested modules into an array path.
.split(/\//)
// camelCase all module namespaces and names.
.map(camelCase)
// Get the modules object for the current path.
const { modules } = getNamespace(storeData, modulePath)
// Add the module to our modules object.
modules[modulePath.pop()] = {
// Modules are namespaced by default.
namespaced: true,
...moduleDefinition,
}
})
// If the environment supports hot reloading...
if (module.hot) {
// Whenever any Vuex module is updated...
module.hot.accept(requireModule.id, () => {
// Update `storeData.modules` with the latest definitions.
updateModules()
// Trigger a hot update in the store.
require('../store').default.hotUpdate({ modules: storeData.modules })
})
}
})()
// Recursively get the namespace of a Vuex module, even if nested.
function getNamespace(subtree, path) {
if (path.length === 1) return subtree
const namespace = path.shift()
subtree.modules[namespace] = {
modules: {},
namespaced: true,
...subtree.modules[namespace],
}
return getNamespace(subtree.modules[namespace], path)
}
export default storeData.modules
state/modules/users.js
import axios from 'axios'
export const state = {
requestUserPermissions: [],
}
export const mutations = {
'FETCH_USER_PERMISSIONS' (state, permissions) {
state.requestUserPermissions = permissions
},
}
export const actions = {
init: ({ dispatch }) => {
dispatch('fetchUserPermissions')
},
fetchUserPermissions: ({ commit }) => {
axios.get('user/permissions/').then(result => {
console.log(result.data)
commit('FETCH_USER_PERMISSIONS', result.data)
}).catch(error => {
throw new Error(`API ${error}`)
})
},
}
utils/dispatch-action-for-all.modules.js
import allModules from '@/state/modules'
import store from '@/state/store'
export default function dispatchActionForAllModules(actionName, { modules = allModules, modulePrefix = '', flags = {} } = {}) {
// for every module
for (const moduleName in modules) {
const moduleDefinition = modules[moduleName]
// if the action is defined on the module
if (moduleDefinition.actions && moduleDefinition.actions[actionName]) {
// dispatch the action if the module is namespaced, if not
// set a flag to dispatch action globally at the end
if (moduleDefinition.namespaced) {
store.dispatch(`${modulePrefix}${moduleName}/${actionName}`)
} else {
flags.dispatchGlobal = true
}
}
// if there are nested submodules
if (moduleDefinition.modules) {
// also dispatch action for these sub-modules
dispatchActionForAllModules(actionName, {
modules: moduleDefinition.modules,
modulePrefix: modulePrefix + moduleName + '/',
flags,
})
}
}
// if this is at the root ant at least one non-namespaced module
// was found with the action
if (!modulePrefix && flags.dispatchGlobal) {
// dispatch action globally
store.dispatch(actionName)
}
}
The computed property in the component retrieves the data from the store directly, like this:
permissions() {
return this.$store.state.users.requestUserPermissions
}