How to commit mutation from `plugins` directory using Nuxt.js? - vue.js

I have just run into such a problem, I am trying to customize Axios module, My aim is to access my dom.js vuex module state from 'plugins' directory, The code below works but I have the following error in the console
Do not mutate vuex store state outside mutation handlers
So, The reason for this error is also clear to me, I wonder how I can Commit mutation from 'plugins' directory to my dom.js vuex module?
Thanks!
//plugins/axios.js
export default function ({ $axios, redirect, store}) {
$axios.onError(error => {
const code = parseInt(error.response && error.response.status)
if (code === 401) {
store.state.dom.alertIs = true
redirect('/')
}
})
}
/store/dom.js
export const state = () => ({
alertIs:false
})

Declare a mutation (named "SET_DOM_ALERT") in your store:
// store/dom.js
export default {
state: () => ({
alertIs: false
}),
mutations: {
SET_DOM_ALERT(state, value) {
state.alertIs = value
}
}
}
Then, use store.commit('dom/SET_DOM_ALERT', newValue) in your plugin (notice the dom/ prefix for the namespace):
// plugins/axios.js
export default function ({ $axios, redirect, store}) {
$axios.onError(error => {
const code = parseInt(error.response && error.response.status)
if (code === 401) {
store.commit('dom/SET_DOM_ALERT', true) // 👈
redirect('/')
}
})
}
demo

Related

Is there any way to get current nuxt.js middleware of page?

I am curious if we can reach the current page's middleware from any component?
Middleware is executed prior to the component and is not initialized within the component in any way.
Update: Persisting the state of middleware in the auth store.
export const state = () => ({
isPrivate: false,
})
export const mutations = {
setPrivatePage(state, value) {
state.isPrivate = value
},
}
export const actions = {
privatePage({ commit }) {
commit('setPrivatePage', true)
},
publicPage({ commit }) {
commit('setPrivatePage', false)
},
}
Dispatch the action in the middleware/auth to set isPrivate:
await store.dispatch('auth/privatePage')
I added another middleware named public to set it back to false
export default async ({ store }) => {
await store.dispatch('auth/publicPage')
}
And finally to trigger public on every route, add it to the nuxt.config.js:
// Router
router: {
middleware: 'public',
},
You can access it on
$router.matched.meta.middleware

Nuxt store mapGetters property is undefined on beforeCreate() hook

I'm experiencing a bug in my nuxt application working with vuex. I'm trying to access a store getter using mapGetters helper but when I access to that property in beforeCreate() hook value is undefined.
store/user.js
import VuexPersistence from "vuex-persist";
export const plugins = [VuexPersistence];
export const state = () => ({
user: null,
});
export const getters = {
isLoggedIn(state) {
if (state && state.user) {
console.log("state.user", state.user);
}
return state.user !== null && state.user !== {};
},
};
mycomponent.vue
export default {
beforeCreate() {
const isLoggedIn = this.$store.getters["user/isLoggedIn"];
console.log("computed isLoggedIn", this.isLoggedIn);
console.log("isLoggedIn", isLoggedIn);
},
computed: {
...mapGetters(["user/isLoggedIn"]),
},
};
</script>
Here is the output result in browser console
The store is not available in the beforeCreate hook. You could move your code to the mounted() hook, but I would recommend placing it in a middleware for checking if the user is logged in.
middleware/auth-check.js
export default function ({ store }) {
const isLoggedIn = store.getters["user/isLoggedIn"];
// do something...
}
Then add to your page:
export default {
...
middleware: 'auth-check'
...
}

Get user on initial page load - Nuxt middleware

I'm using Firebase Auth in my Nuxt app and having some trouble with protected routes. I have the nuxt middleware enabled that handles redirection based on whether or not a user a logged in on page navigation.
My issue is, if you visit a protected route from the address bar and you were previously logged out, you'll be given access to the route. However if you try to visit another protected route from that route you'll be redirected. I am using Vuex to handle the user logged in state.
How can I get the middleware to kick in on a 'cold start' for a protected route.
Middleware: router-auth.js
export default function({ store, redirect, route }) {
const user = JSON.parse(store.state.Authentication.user)
console.log(user);
user !== null && route.name === '/' ? redirect('/dashboard') : ''
//user === null && route.name !== '/' ? redirect('/dashboard') : ''
}
Plugin: auth.js
import firebase from "firebase/app";
import "firebase/auth";
export default function ({store, router}) {
firebase.auth().onAuthStateChanged((user) => {
if(user){
store.dispatch('Authentication/SET_USER',JSON.stringify(user))
}else{
store.dispatch('Authentication/SET_USER', null)
}
})
}
Vuex: Authentication.js
export const state = () => ({
user: ''
});
export const getters = {
user: state => {
return state.user;
},
}
export const mutations = {
SET_USER: (state, payload) => {
state.user = payload;
},
}
export const actions = {
SET_USER({ commit }, payload) {
commit('SET_USER', payload)
},
}
You can return an error func call in middleware:
export default function({ store, redirect, route, error }) {
const accessDenied = true // ... your access route logic goes here
if (accessDenied) {
return error({
message: 'access denied',
statusCode: 403
})
}
}

How do I get nuxtServerInit to dispatch an action on the Server?

I have a nuxt project using firebase. I want to use SSR and initiate and populate the store on SSR but I cannot get the code below to work.
I am working on a nuxt project I have a plugin/firebase project that initiates the firebase sdk. I have an asyncData function that works.
in my /store/index.js file I export the state function and the actions. In the actions I have the async nuxtServerInit that dispatches a `posts/getPosts' action passing the context.
In my store/index I have
export const state = () => ({})
export const actions = {
async nuxtServerInit({ dispatch }, context) {
await dispatch('posts/getPosts', context)
}
}
In my 'store/posts.js` I have
import { db } from '~/plugins/firebase'
export const state = () => ({
ActivePosts: []
})
export const actions = {
getPosts({ commit }) {
const postList = []
return db
.collection('posts')
.where('status', '==', 'approved')
.orderBy('CreatedAt', 'desc')
.get()
.then(docs => {
docs.forEach(doc => {
const newPost = doc.data()
newPost.id = doc.id
this.postList.push(newPost)
console.log(newPost)
})
})
.then(() => {
commit('addPosts', postList)
})
.catch(e => console.log(e))
}
}
In my firebase plugin I have
import firebase from 'firebase'
const firebaseConfig = {
apiKey: '<<correctkey>>.',
authDomain: '<<correctkey>>',
databaseURL: '<<correctUrl>>',
projectId: '<<correctid>>',
storageBucket: '<<correctbucket>>',
messagingSenderId: '<<correctkey>>',
appId: '<<correctkey>>'
}
if (!firebase.apps.length) {
firebase.initializeApp(firebaseConfig)
}
export const db = firebase.firestore()
export const auth = firebase.auth()
This code, at least I thought, should initiate my store on the server and fill it with post values. When I check my store in vue developer tools there are no values in the store, although the getter is present and the state values(empty array) is present. This tells me that the store is initiated and the module is present, at least on the client side.
Turns out the problem was not with my action but the mutation. Here is the final code that got me to working.
import { db } from '~/plugins/firebase'
export const state = () => ({
ActivePosts: []
})
export const getters = {
getPosts(state) {
return state.ActivePosts
}
}
export const mutations = {
addPosts(state, payload) { // had to change { state } to state.
state.ActivePosts.push(payload)
}
}
export const actions = {
getPosts({ commit }) {
const postList = []
return db
.collection('posts')
.where('status', '==', 'approved')
.orderBy('CreatedAt', 'desc')
.get()
.then(docs => {
docs.forEach(doc => {
const newPost = doc.data()
newPost.id = doc.id
postList.push(newPost) //removed the `this.`
})
commit('addPosts', postList) //moved the commit to the // moved the commit out of its own then.
})
.catch(e => console.log(e))
}
}

vuex unknown action (or mutation) type

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')`
})
}
}