I am trying to do a .find() method on a Vuex getter.
However when I do so, it gives me the following error:
Uncaught TypeError: ur.find is not a function
I have followed the docs and I cannot seem to understand why I cannot do methods like .find() or .forEach() on the user-Object provided by the vuex Getter.
Am I doing something wrong, or is this type of thinking out of the scope?
This is an example of my mixin auth.js
export const auth = {
methods: {
y(r){
const ur = this.$store.getters.getUserRoles;
console.log(ur); // Returns {__ob__: Observer}...
ur.find( uRole => { // ur.find is not a function
return uRole === r
})
}
}
};
And this is my Vuex instance from the main vue; app.js
const store = new Vuex.Store({
state: {
user: {}
},
mutations: {
setUser (state, data) {
state.user = data;
},
},
getters: {
getUserRoles(state){
return state.user.roles;
}
},
actions: {
getUser ({commit}) {
axios.get('/api/user').then(response => {
const data = response.data.data;
commit('setUser', data);
});
}
}
});
The response from /api/user:
{
admin: true,
projectManager: false,
timeAdmin: false
}
Related
I am trying to switch authenticated from false to true, a property in the state, It's not working.
My codes from store.js
state() {
return{
authenticated : false,
user : {}
}
},
getters : {
authenticated(state){
return state.authenticated
}
},
mutations : {
set_authenticated(state, value){
return state.authenticated = value
}
},
My updated code from login.vue (script)
data() {
return {
allerrors : [],
success : false,
data: {
email : "",
password : ""
}
}
},
methods : {
login: function() {
this.$store
.dispatch("login", this.data)
.then(response => {
this.allerrors = [],
this.success = true,
this.data = {}
alert(response.data)
})
.catch((error) => {
this.allerrors = error.response.data.error
this.success = false
alert(allerrors)
})
},
My updated action is :
async login({ commit }, data) {
await axios.post('login', data)
.then(response => {
commit('set_authenticated',true);
})
.catch((error) => {
this.allerrors = error.response.data.error
this.success = false
})
}
There are a few problems here:
First, if that is the full code for your store.js file, then you are missing the call to createStore() (for Vue 3) or new Vuex.Store() (for Vue 2)
import { createStore } from 'vuex'
// Create a new store instance.
const store = createStore({
state () {
return {
count: 0
}
},
mutations: {
increment (state) {
state.count++
}
}
})
Source
The second problem is that you shouldn't be committing mutations from your Single File Components. The typical flow is:
Components dispatch actions
Actions commit mutations
Mutations update state
You're trying to commit a mutation directly from the component.
You need to add an action to your store.js
async login({ commit }, userData) {
await axios.post('login', userData)
.then(response => {
commit('set_authenticated',true);
})
.catch((error) => {
this.allerrors = error.response.data.error
this.success = false
})
}
Mutation:
mutations : {
set_authenticated(state, value){
state.authenticated = value
}
},
Then your Login.vue would change to something like:
methods: {
login: function() {
this.$store
.dispatch("login", { userData })
.then(() => )) // whatever you want to do here.
.catch(err => console.error(err));
}
}
mutations shouldn't have a return statement. it should be like this
mutations : {
set_authenticated(state, value){
state.authenticated = value
}
},
Im devoloping a Vue Frontend and my problem is the flow between Vuex and vue component.
Vuex component
const state = () => ({
User: {
Firstname: '',
Lastname: '',
MainAccountBalance: '',
SubAccount: []
}
})
const getters = {
getUserData(state) {
console.log("3 Dashboard: getter")
return state.User;
}
};
const actions = {
async fetchUserProfile({commit}, payload) {
await instance.post(UserAccounts, {
token: payload.token
})
.then(response => {
console.log("1 Dashboard: fetchUserProfile");
commit('saveAccounts', response.data);
return JSON.stringify(response.data);
})
}
};
const mutations = {
saveAccounts(state, data) {
const newModel = {
Firstname: data.Firstname,
Lastname: data.Fastname,
MainAccountBalance: data.MainAccountBalance,
SubAccount: data.SubBankAccounts
}
console.log("2 Dashboard: Mutations")
state.User = newModel;
}
};
fetchUserProfile is getting called from Vue component and gets commited to saveAccounts where it will set data for state variables. All of this works fine, and in correct order. The problem starts with when i want to get the data from the State using getters.
Vue component
computed: {
...mapGetters("auth", {
getUserData: 'getAuthData'
}),
},
methods: {
...mapActions("dashboard", {
userProfileaction: "fetchUserProfile"
}),
getUserToken() {
this.access_token = localStorage.getItem("access_token")
return this.access_token;
},
async saveUserData() {
var userToken = this.getUserToken();
await this.fetchUserProfile({token: userToken})
this.Firstname = this.getUserData.Firstname;
this.Lastname = this.getUserData.Lastname;
console.log("Last Show accounts: "+ this.getUserData.User.Firstname)
}
},
mounted() {
this.saveUserData()
console.log("test")
}
How would i use getters correct so it doesent execute before the variables are set on Vuex? Is there an better alternative way?
I have Vuex Store that will look like this
const config = {
featureA: { isEnabled: true, maxUser: 2 },
featureB: { isEnabled: false, maxData: 5 },
}
const actions = {
getDataCompany(context, payload) {
return new Promise(async (resolve, reject) => {
try {
const result = await firebase.firestore().collection(payload.collection).doc(payload.companyId).get()
if (result) {
if (payload.isLogin) await context.commit('setConfig', result.data())
return resolve(result.data())
}
reject(new Error('Fail To Load'))
} catch (e) {
reject(new Error('Connection Error'))
}
})
}
}
const mutations = {
setConfig(state, payload) {
state.config = payload
}
}
const getters = {
getData: ({ config }) => (feature, key) => {
const state = config
if (state) if (state[feature]) if (state[feature][key]) return state[feature][key]
return null
}
}
export default new Vuex.Store({
state: { config },
actions: { ...actions },
mutations: { ...mutations },
getters: { ...getters }
})
It's working fine with this method to get the data
computed: {
featureAEnabled() {
return this.$store.getters.getData('featureA', 'isEnabled')
},
}
But I have a problem when the data is change, the value is not update in component, and now I want to use mapGetters because it say can detect changes, But I have problem with the documentation and cannot find how to pass params here,
import { mapGetters } from 'vuex'
computed: {
...mapGetters({
featureAEnabled: 'getData'
})
}
I'am calling the action from here
async beforeMount() {
await this.$store.dispatch('getDataCompany', {collection: 'faturelsit', companyId: 'asep', isLogin: true})
}
And try to detect change in here
mounted() {
if (this.featureAEnabled) console.log('feature enabled')
}
The value change is not detected, and need to refresh twice before the changes is implemented in component
My main target is to detect if there any data change in Vuex and make action in component,
nevermind just working with watch without mapgetter,
I just realize that computed cannot re-run the mounted, so I make method that will called when the variable change in watch. thank you.
The main purpose is fulfilled, but the mapgetter with params is still not answered. so if anyone want to answer please share the way to use mapgetter with params.
You could try to use get and set methods for your computed property.
Example:
computed: {
featureAEnabled: {
get() {
return this.$store.getters.getData('featureA', 'isEnabled')
},
set(value) {
...update featureEnabled property in vuex store
}
},
}
I'm writing a simple code to set token in store in an Nuxt application. when I tried to call a mutation or action from my store, this error is logged in console: [vuex] unknown action type: setToken
import Vuex from 'vuex';
export const store = new Vuex.Store({
state:()=> ({
token: ''
}),
getters: {
getToken: state => {
return state.token;
}
},
mutations: {
setToken: (tokenStr) => {
state.token = tokenStr;
}
},
actions: {
setToken: ({ commit }, tokenStr) => {
commit('setToken', tokenStr);
}
}
})
This is a method trying to call the mutation:
methods:{
setToken(){
this.$store.dispatch('setToken','token1');
this.token = this.$store.getters.token;
}
}
You are using the 'classic' and now deprecated method of setting the vuex store in nuxt. You should set it up like this:
// store/index.js
export const state = () => ({
token: ''
})
export const mutations = {
SET_TOKEN (state, tokenStr) {
state.token = tokenStr
}
export const actions = {
setToken ({ commit }, tokenStr) {
commit('SET_TOKEN', tokenStr)
}
}
export const getters = {
token: (state) => state.token
}
Nuxt will build the store for you from there. You can see it in the doc here.
You can dispatch actions in components with this.$store.dispatch('xxx'), or use the mapActions helper which maps component methods to store.dispatch calls (requires root store injection):
Try Another Method For Dispatching An Action
import { mapActions } from 'vuex'
export default {
// ...
methods: {
...mapActions([
'increment',
// map `this.increment()` to
this.$store.dispatch('increment')
// `mapActions` also supports payloads:
'incrementBy' // map `this.incrementBy(amount)` to `this.$store.dispatch('incrementBy', amount)`
]),
...mapActions({
add: 'increment' // map `this.add()` to `this.$store.dispatch('increment')`
})
}
}
I have the next code with vuejs, i call axios method post and set the authenticated user correctly(cosole show the user), but when i call the computed property in the component the user is empty
export default {
data() {
return {
isAuth: null,
}
},
computed: {
authenticatedUser () {
return this.getAuthenticatedUser()
}
},
created() {
this.isAuth = this.$auth.isAuthenticated()
this.setAuthenticatedUser()
},
methods: {
setAuthenticatedUser () {
axios.get('/api/user')
.then(response => {
this.$auth.setAuthenticatedUser(response.data)
console.log(this.$auth.getAuthenticatedUser())
})
},
getAuthenticatedUser(){
return this.$auth.getAuthenticatedUser()
}
},
router
}
And this my code for get the authenticated user
export default function (Vue) {
let authenticatedUser = {};
Vue.auth = {
//set token
setToken (token, expiration) {
localStorage.setItem('token', token)
localStorage.setItem('expiration', expiration)
},
//get token
getToken() {
var token = localStorage.getItem('token')
var expiration = localStorage.getItem('expiration')
if( !token || !expiration)
return null
if(Date.now() > parseInt(expiration)){
this.destroyToken()
return null
}
else{
return token
}
},
//destroy token
destroyToken() {
localStorage.removeItem('token')
localStorage.removeItem('expiration')
},
//isAuthenticated
isAuthenticated() {
if(this.getToken())
return true
else
return false
},
setAuthenticatedUser(data){
return authenticatedUser = data;
},
getAuthenticatedUser(){
return authenticatedUser;
},
}
Object.defineProperties(Vue.prototype, {
$auth: {
get() {
return Vue.auth
}
}
})
}
When i not use the computed property
When i use the computed property in the model
Your computed property won't be updated because this.$auth is out of the instance’s scope (i.e. not reactive).
I would use vuex, putting the user inside the global state:
const store = new Vuex.Store({
state: {
user: {}
},
mutations: {
user (state, user) {
state.user = user
}
}
})
and then watch changes in your component:
import store from 'path/to/store'
store.watch(state => {
return state.user
}, () => {
// process authenticated user
})
Make $auth another Vue instance and install it as a plugin, this way, it will be accessible from any other Vue instance.
function Auth(Vue) {
let auth = new Vue({
data: {
// your auth data
authenticatedUser = {}, // This one is now reactive
},
computed: {
// your auth computed properties
},
methods: {
// your auth methods
setAuthenticatedUser(data){
return this.authenticatedUser = data
},
}
})
Vue.prototype.$auth = auth
}
To use this plugin, simply call:
Vue.use(Auth)
Now, you can access the authenticated user from any Vue component like this:
this.$auth.authenticatedUser