I'm a little bit confused with vuex store component.
How should I obtain state of another module?
I tried a different ways to get data from store and always got Observer object. What is the correct way to knock knock to observer?
If I try to get anything from this object directly, like rootState.user.someVariable then I got undefined response.
Don't have a problem getting state from components.
Edit. Add code
User module
import * as Constants from './../../constants/constants'
import * as types from '../mutation-types'
import axios from 'axios'
const state = { user: [] }
const getters = {
getUser: state => state.user
}
const actions = {
getUserAction ({commit}) {
axios({method: 'GET', 'url': Constants.API_SERVER + 'site/user'})
.then(result => {
let data = result.data
commit(types.GET_USER, {data})
}, error => {
commit(types.GET_USER, {})
console.log(error.toString())
})
}
}
const mutations = {
[types.GET_USER] (state, {data}) {
state.user = data
}
}
export default { state, getters, actions, mutations }
Mutatinos
export const GET_LANGS = 'GET_LANGS'
export const GET_USER = 'GET_USER'
Store
import Vuex from 'vuex'
import Vue from 'vue'
import user from './modules/user'
import lang from './modules/lang'
Vue.use(Vuex)
const store = new Vuex.Store({
modules: {
user,
lang
}
})
Main app
import Vue from 'vue'
import App from './App'
import router from './router'
import store from './store/index'
Vue.config.productionTip = false
new Vue({
el: '#app',
router,
store,
template: '<App/>',
components: { App }
})
Lang module, here is the place where I'm trying get store
import * as types from '../mutation-types'
import {axiosget} from '../../api/api'
const state = { langList: [] }
const getters = {
getLangs: state => state.langList
}
const actions = {
// this two action give me similar result
getLangsAction (context) {
axiosget('lang') // described below
},
getAnotherLangsAction (context) {
console.log(context.rootState.user) <----get Observer object
}
}
const mutations = {
[types.GET_LANGS] (state, {data}) {
state.langList = data
}
}
export default { state, getters, actions, mutations }
axiosget action, api module
import * as Constants from './../constants/constants'
import store from '../store/index'
import axios from 'axios'
export const axiosget = function (apiUrl, actionSuccess, actionError) {
console.debug(store.state.user) // <----get Observer object, previously described
// should append user token to axios url, located at store.state.user.access_token.token
axios({method: 'GET', 'url': Constants.API_URL + apiUrl
+ '?access_token=' + store.state.user.access_token.token})
.then(result => {
let data = result.data
// todo implement this
// }
}, error => {
if (actionError && actionError === 'function') {
// implement this
}
})
}
Component, that call dispatcher. If i get state via mapGetters in computed properties - there is no problems
<template>
<div>
{{user.access_token.token}}
</div>
</template>
<script>
import { mapGetters } from 'vuex'
export default {
name: 'ArticlesList',
computed: mapGetters({
user: 'getUser'
}),
created () {
this.$store.dispatch('getLangsAction')
this.$store.dispatch('getAnotherLangsAction')
}
}
</script>
What I'm trying to do in this code - get user access token in main site (after login) and all further manipulations with data will be produced via api host.
Let's say you want to fetch state an attribute userId from object userDetails in Vuex store module user.js.
userDetails:{
userId: 1,
username: "Anything"
}
You can access it in following way in action
authenticateUser(vuexContext, details) {
userId = vuexContext.rootState.user.userDetails.userId;
}
Note: After rootState and before file name user, add the path to the store module file if it is inside nested folders.
Related
I havent been able to figure out what the problem is here. I am using vue 3.
I trye to add an obejct to another object inside of the store state. I can add it to the firebase console, but the only thing that has not been solved yet is how to add it to the store in the state.coaches as an object within an object. In this way I would be able to display the list of object into the view of the screen.
//store-coach.js
import Vue from 'vuex'
import { uid, Notify } from 'quasar'
import firebase from 'boot/firebase'
const state = {
coaches: {
}
}
const mutations = {
addCoach(state, coach) {
Vue.set(state.coaches, coach.id, coach.coach)
}
}
const actions = {
fbReadDataCoaches({ commit }) {
let coachesfb = firebase.database().ref('coaches')
//child added
coachesfb.on('child_added',snapshot => {
let coachfb = snapshot.val()
let payload = {
id: snapshot.key,
coach: coachfb
}
commit('addCoach', payload)
})
},
fbaddCoach({}, payload){
let coachRef = firebase.database().ref('coaches/' + payload.id)
coachRef.set(payload.coach, error =>{
if(!error){
Notify.create('Coach added!!')
}
})
}
}
//index.js
import Vuex from 'vuex'
import coaches from './store-coach'
export default function (/* { ssrContext } */) {
const Store = new Vuex.Store({
modules: {
coaches
}
})
return Store
}
The error that I get is when
Vue.set(state.coaches, coach.id, coach.coach) is fired
Uncaught TypeError: vuex__WEBPACK_IMPORTED_MODULE_3__.default.set is not a function
thanks in advance!
Appreciate your time!
In first file (store-coach.js), change:-
From -
import Vue from 'vuex'
To -
import Vue from 'vue'
As .set method is available in vue package. Its not available in vuex package that is why you are getting this error. Documentation reference.
I have this Read.js that reads an image. And I want to save the content of the image (in bytes) into a vuex Store.
I'm trying to import the store into Read.js with:
import Store from "src/store/index";
After I read the bytes, I try to access the actions to save it to the state using:
Store.actions.A_updateImage(payload);
But i get that ".actions" is "Undefined".
This is the main store file that import the modules:
//src/store/index.js
import Vue from 'vue'
import Vuex from 'vuex'
import My_store from './modules/My_store'
Vue.use(Vuex)
export default function (/* { ssrContext } */) {
const Store = new Vuex.Store({
modules: {
My_store
},
strict: process.env.DEBUGGING
})
return Store
}
This is the store module that has the action i want to use:
//src/store/modules/My_store.js
const state = {
Image: {}
}
const mutations = {
M_updateImage: (state, Image) => {
state.Image = Image;
},
}
const actions = {
A_updateImage({ commit }, Image) {
commit('M_updateImage', Image)
}
}
const getters = {
getImage:(state) =>{
return state.image
}
}
export default {
namespaced: true,
getters,
mutations,
actions,
state
}
How can I import and call the action I need to use from the Read.js file?
Thank you
I have a vue application.
How to access the store from javascript/typescript modules files (import/export)?
for example, I create auth-module that export state, actions, mutations.
export const auth = {
namespaced: true,
state,
actions,
mutations,
getters,
};
In my app I import the module to my store:
Vue.use(Vuex);
export const store = new Vuex.Store({
modules: {
auth,
}
});
Now, I want to create interceptor (inside my auth-module) for my http calls to add the token from the store.
Vue.http.interceptors.push((request: any) => {
// ---> store.state.token???
// request.headers.set('Authorization', 'Bearer TOKEN');
});
But how can I get access to the state of the store without be depend on my app?
import {store} from './store' but it's okay to import the store instance from vue or vuex module.
You can do that using Plugin.
When you using Plugin, you will get the store instance.
Subscribe to the instance and you get the state, extract token from the state and save it to local variable.
In the Interceptor read this module-global variable.
Here is the solution I built for you:
StoreTokenInterceptorPlugin.ts
import Vue from 'vue';
import VueResource from 'vue-resource';
import { get } from 'lodash';
Vue.use(VueResource);
export const StoreTokenInterceptorPlugin = (store: any) => {
let token: string | null = null;
(Vue.http.interceptors as any).push((request: any) => {
if (token && !request.headers.get('Authorization')) {
request.headers.set('Authorization', `Bearer ${token}`);
}
});
store.subscribe((mutation: any, state: any) => {
token = get(state, 'auth.token') || null;
});
};
in your app store:
import Vue from 'vue';
import Vuex from 'vuex';
import { auth, StoreTokenInterceptorPlugin } from '#modules/auth';
Vue.use(Vuex);
export const store = new Vuex.Store({
state,
modules: {
auth,
} as any,
....
plugins: [StoreTokenInterceptorPlugin],
});
I am using vuex, axios for my app and I want to use getter in axios initiation to pass basic auth. This is my axios init (http-common.js):
import axios from 'axios'
import store from '#/store'
export default axios.create({
baseURL: 'http://localhost:8081/',
auth: store.getters['authentification']
})
When I am debugging my app I find store undefined. Can someone explain what am I doing wrong? Store itself works fine in all the components.
My store has several modules and those modules. store index.js:
import m1 from './modules/m1'
import m2 from './modules/m2'
import authentification from './modules/authentification'
Vue.use(Vuex)
export default new Vuex.Store({
modules: {
authentification,
m1,
m2
}
})
And modules uses axios init function for calling REST api i.e. :
import HTTP from '#/common/http-common'
.....
const actions = {
action ({commit}) {
HTTP.get('item')
.then(response => {
commit('MUTATION', {response})
})
}
}
.....
export default {
state,
getters,
actions,
mutations
}
I think this creates loop and calls http-common before store is being initialized.
Edit: adding authentification module as requested:
import * as types from '../mutation-types'
const state = {
isLoggedIn: !!localStorage.getItem('auth'),
auth: JSON.parse(localStorage.getItem('auth'))
}
const getters = {
isLoggedIn: state => {
return state.isLoggedIn
},
authentification: state => {
return state.auth
}
}
const mutations = {
[types.LOGIN] (state) {
state.pending = true
},
[types.LOGIN_SUCCESS] (state) {
state.isLoggedIn = true
state.pending = false
},
[types.LOGOUT] (state) {
state.isLoggedIn = false
}
}
const actions = {
login ({
state,
commit,
rootState
}, creds) {
console.log('login...', creds)
commit(types.LOGIN) // show spinner
return new Promise(resolve => {
setTimeout(() => {
localStorage.setItem('auth', JSON.stringify(creds))
commit(types.LOGIN_SUCCESS)
resolve()
}, 1000)
})
},
logout ({ commit }) {
localStorage.removeItem('auth')
commit(types.LOGOUT)
}
}
export default {
state,
getters,
actions,
mutations
}
This is actually a better solution shown to me by Thorsten Lünborg (LinusBorg) of the Vue core team:
https://codesandbox.io/s/vn8llq9437
In the file that you define your Axios instance in and set configuration, etc., you have also got a Vuex plugin that watches your store and sets/deletes your Authorization header based on the presence of whatever auth token in your store.
I have found a sollution. I had to assign auth before the call and not during inicialization of axios object:
var axiosInstance = axios.create({
baseURL: 'http://localhost:8081/'
})
axiosInstance.interceptors.request.use(
config => {
config.auth = store.getters['authentification']
return config
}, error => Promise.reject(error))
export default axiosInstance
I am using VueJS in conjunction with vuex and vue-router. I have a vuex module that is making a mutation to its store, and trying to use that to determine whether or not a user is authenticated.
Here is what my code looks like in relevant part.
main.js
import Vue from 'vue'
import App from './App.vue'
import store from './store'
import router from './router'
router.beforeEach((to, from, next) => {
console.log(router.app) // prints a Vue$2 object
console.log(router.app.$store) // undefined
console.log(store.getters.isAuthenticated) // false
...
}
const app = new Vue({
store,
router,
...App
})
app.$mount('#app')
/store/index.js
import Vue from 'vue'
import Vuex from 'vuex'
import core from './modules/core'
Vue.use(Vuex)
const store = new Vuex.Store({
modules: {
core: core
}
})
export default store
/store/modules/core.js
import * as types from '../types'
import api from '../../api'
import router from '../../router'
const state = {
token: null,
user: null,
authenticated: false
}
const mutations = {
[types.LOGIN_SUCCESS] (state, payload) {
console.log('mutate')
state.token = payload.token
state.user = payload.user
state.authenticated = true
router.go('/')
}
}
const getters = {
isAuthenticated: state => {
return state.authenticated
}
}
const actions = {
[types.LOGIN] (context, payload) {
api.getToken(payload).then(response => {
context.commit(types.LOGIN_SUCCESS, response)
})
}
}
export default {
state,
mutations,
actions,
getters
}
When I go thru my logic to trigger the LOGIN action, I can see that the mutation executed properly, and when I use the Chrome extension to view the vuex state for my core module, the state for user and authenticated have been properly mutated.
QUESTION
It seems like this module just simply has not been loaded by the time the router is running in the .beforeEach loop. Is this true?
If yes, what are some other suggestions on how to handle this situation?
If no, what am I doing incorrect?
console.log(store.state.core.authenticated) return false because you not make a login yet.
In your code you not persist user info in anywhere. E.g. using localstorage
Same considerations:
Not use router.app.$store, use store that you import
In your LOGIN_SUCCESS mutation, store login info and token into localstorage
In your beforeEach hook, check localstorage, if was populated with token, get user information and apply the mutation. If not, just call login page
Something like this..
const mutations = {
[types.LOGIN_SUCCESS] (state, payload) {
state.token = payload.token
state.user = payload.user
state.authenticated = true
localstorage.setItem('token', payload.token)
localstorage.setItem('user', payload.user)
}
}
const actions = {
[types.LOGIN] (context, payload) {
return api.getToken(payload).then(response => {
context.commit(types.LOGIN_SUCCESS, response)
return response
})
}
}
router.beforeEach((to, from, next) => {
let user = localstorage.getItem('user')
let token = localstorage.getItem('token')
if (user && token) {
store.commit(types.LOGIN_SUCCESS, {token, user})
next()
}
else if (!store.getters.isAuthenticated) {
store.dispatch(types.LOGIN).then(() => next())
} else {
next()
}
}