Committing a mutation from another module - vue.js

I would like to reset the state of my stores upon a logout action. I created mutations in each store to reset the state.
I tried doing something like this with no luck.
logout: function ({commit}) {
commit('resetLoginState')
commit('menu.resetMenuState')
}
the logout function is in my login module and the resetMenuState is in another module called menu
Is it possible to do this?

You can use:
commit('path/from/root/to/your/module/menu/resetMenuState', null, { root: true })
you can read more about it on this page

Related

How do we call a function within the html file of vuejs defined in the javascript file of vuejs without creating a button in the html file?

I pretty new to vuejs and am building a vuejs project. One of the tasks I am stuck at is, that I want to call a function written in the javascript part of vuejs from the html part of vuejs without creating any buttons or textboxes. I want to call this function as soon as my app starts. How do I achieve this? When I use mounted (vue lifecycle hook), the page it redirects to keeps refreshing. would appreciate some leads on this.
For example, I have a code:
<template>
<h1>Hello World!</h1>
<!--I WANT TO CALL THE auth0login function here. How do I do that without creating a button/text field,etc -->
</template>
<script>
export default {
data: () => ({
return : {
clientID: process.env.example
}
}),
methods:{
auth0login(){
console.log('logging in via Auth0!');
Store.dispatch('login');
}
}
}
</script>
I want to call the auth0login function defined in the script part in the html part above in order to execute the functionalities of the auth0login function. I want to do this without creating any buttons in the html file and simply just execute the auth0login function when the app launches.
How do I do this?
You need to call auth0login() only when you're not already logged I guess.
I think the reason is after user already logged in, they go back to the initial page, and the function in mounted() hook run again. Thats why they keep get redirect to login page. This not happen when you insert the button, because the button require user to click at it.
To fix this, you need to define a vuex state to store whether user has logged in or not, and in mounted() hook, just called it when they not login yet.
So. Instead of
mounted: function() { console.log('logging in via Auth0!'); this.auth0login() } }
Add a check login state
mounted() {
if(!this.$store.state.userLoggin) // here i define userLoggin and vuex state, you should update it to true whenever user successfully login
{ console.log('logging in via Auth0!'); this.auth0login() //only call it when user not loggin
}
}
And remember to update your vuex state.userLoggin when user successfully login in the auth0login() function

Save user authentication status in vuex state is safe Solution?

I've watched various videos on spa vue authentication. All those videos provided a vuex based solution
Like this ;
They created function in getters such as loggedIn that it returns user authentication status .
store.js
[...]
state:{
token: null,
user: null
},
getters: {
loggedIn: (state) => {
return state.token && state.user ? true : false
},
},
[...]
Dashboard.vue (This is a protected route)
[...]
beforeRouteEnter (to, from, next) {
if(store.getters.loggedIn)
next()
else
next({ name:'login' })
}
[...]
Everything is working correctly but I can change state manually by vue dev tools thus I can enter the protected route so authentication is not working properly.
My questions :
Can the user modify the vuex state in production mode?
Save user authentication status in vuex state is safe Solution?
Can the user modify the vuex state in production mode?
Yes, the user can do anything to the state of the app. You can obfuscate some things, but you cannot prevent the user from inspecting or modifying the data of your app.
Save user authentication status in vuex state is safe Solution?
It is one approach, and I do recommend it especially as your app size grows it will become easier to manage shared state in your app. But I wouldn't say it is "safe" or "unsafe".
I can change state manually by vue dev tools thus I can enter the protected route so authentication is not working properly.
The app's behavior can be easily circumvented as you have described. You only have complete control over the server code. If the protected route becomes accessible on the client because the user tampered with the data using dev tools, then the server should reject any requests for privileged data that the protected route requires.

Commit mutation from another file nuxt

How can I in user.js call mutation from whoToFollow.js called reset ? Is it even possible ? Heres my code:
async logOut({commit}) {
this.$cookies.remove('token');
commit('set_token', null);
commit('whoToFollow/reset');
this.$router.push('/sign-in');
},
But it doesn't work I get this error:
unknown local mutation type: whoToFollow/reset, global type: user/whoToFollow/reset
It is possible to call mutations from other stores directly. You are just missing the option '{root: true}', which is needed for namespaced modules.
I would recommend calling an action in the other store first though, which then again calls the mutations to stay true to the Vuex pattern. Actions -> Mutations
async logOut({commit, dispatch}) {
this.$cookies.remove('token');
commit('set_token', null);
// in the reset action you can then call the commit
dispatch('whoToFollow/reset', payloadHere, { root: true })
this.$router.push('/sign-in');
},
I advise you to check out the Vuex Api Documentation to learn more about this and why you will need 'root: true'.
https://vuex.vuejs.org/api/#vuex-store-instance-methods

Mutating a state from one module to another Vuex

I'm pretty new to Vuex and am having difficulty understanding how to handle a state change from one module to another. Currently, I have a module called transactions which does an Ajax request and if successful it should close the Modal that is open. I have my modal state set in a separate module called General. I originally tried to set the General State of modal but committing my general mutation closeModal. I realized this won't work and as it sounds like Mutations aren't supposed to do this sort of heavy lifting. I've searched for another method to handle this sort of work and have been lead to Actions but I'm not clear on how to implement it or if it is even supposed to do this kind of work. Would someone please let me know if an Action is the correct method for this problem or if there is another way I should be addressing things.
I have a module called transactions that is running an ajax request and should close a modal if successful. For the sake of this issue, I've simplified my module.
const Transactions = {
state: {
},
mutations: {
CONFIRM_TRANSACTION_CANCEL: function(state) {
this.$store.commit('CLOSE_MODAL')
}
}
And I also have a second module called general which I want to use for general state management and error handling. I'm attempting to call a mutation from transactions into this general module.
const General = {
state: {
modalState: null,
},
mutations: {
...
CLOSE_MODAL: function(state) {
state.modalState = null
},
...
}
}
You should not make a commit inside a mutation. Mutations are only to change the state.
You could do this in two ways:
1.- Using Vue's Watch spying transaction state. Then, if the transaction is correctly done, you dispatch an action to close the Modal.
2.- You can dispatch an action to close the modal inside the action that launches the ajax call (after the success).
apiCall({ dispatch, commit }) {
api.get('/transaction')
.then((response) => {
dispatch('closeModal');
commit('TRANSACTION_SUCCESS', response);
})
.catch((error) => commit('TRANSACTION_ERROR', error));
}
These methods below are done with thinking of the modal as it should use vuex too but, if you want to simplify you can just:
3.- Pass the status of the transaction that comes from vuex directly to the modal by prop and handle the modal with it.

Checking auth token valid before route enter in Vue router

I have a simple use case, where my application is using vue-router and vuex. Then store contains a user object which is null in the beginning. After the user is validated from the server it sends back an user object which contains a JWT auth token which is assigned to the user object in the store. Now lets assume that the user came back after 3 hours and tried to visit a route or perform any other action, considering that the auth token has expired by then, what would be the best way to check that(need to call axios post to check it) and redirect user to the login page. My app will have loads of components so I know I can write logic to check the token valid in the mounted hook of each component but that would mean repeating it all of the components. Also I don't want to use the beforeEach navigation guard because I cannot show any visual feedback to the user like checking... or loading....
I do something similar in one of my projects, it's actually deceptively difficult to handle these types of situations, but you can add a beforeEnter guard to your protected routes, then redirect if the authentication failed.
const guard = function(to, from, next) {
// check for valid auth token
axios.get('/api/checkAuthToken').then(response => {
// Token is valid, so continue
next();
}).catch(error => {
// There was an error so redirect
window.location.href = "/login";
})
};
Then on your route you can do:
{
path: '/dashboard',
component: Dashboard,
beforeEnter: (to, from, next) => {
guard(to, from, next);
}
},
You may notice I've used location.href rather than router.push. I do that because my login form is csrf protected, so I need a new csrf_token.
Your other issue is going to be if the user tries to interact with your page without changing the route (i.e. they click a button and get a 401 response). For this I find it easiest to check authentication on each axios request and redirect to login when I receive a 401 response.
In terms of adding a loading spinner during the guard check you can simply add a loading flag to your vuex store then import your store into your router. Honestly though I wouldn't bother, on a decent production server the check will be done so quickly that the user is unlikely to ever see it.
Try Vue.JS Mixins
You can define a Global Mixin and use it via Vue.use(myMixin) - then all Components will inherit this mixin. If you define a mounted or probably better activated hook on the mixin, it will be called on every component.
There you can use everything a component can do - this will point to your component. And if the component also defines a hook itself, the mixin hook of the same type will run before the components own hook.
Or try a single top-level login component
We used a little different solution - we have a single component which handles everything login-related, which exists outside of the router-view in the parent index.html. This component is always active and can hide the div router-view and overlay a loading message or a login-screen. For an intranet-application this component will also use polling to keep the session alive as long as the browser stays open.
You can load of your router-navigation to this component. - So a child-component which wants to trigger a router-navigation just sets a global reactive property navigateTo which is watched by the top level authentication component. This will trigger an authentication check, possibly a login-workflow and after that the top-level component will call $router.push() With this approach you have complete control over any navigation.
You can use interceptors to silently get the auth token when some request happens.
axios.interceptors.response.use(function (response) {
return response;
}, function (error) {
const originalRequest = error.config;
if (error.response.status === 401 && !originalRequest._retry) {
originalRequest._retry = true;
const rToken = window.localStorage.getItem('rToken');
return axios.post('url/to/get/refresh/token', { rToken })
.then(({data}) => {
window.localStorage.setItem('token', data.token);
window.localStorage.setItem('rToken', data.refreshToken);
axios.defaults.headers.common['Authorization'] = 'Bearer ' + data.token;
originalRequest.headers['Authorization'] = 'Bearer ' + data.token;
return axios(originalRequest);
});
}
return Promise.reject(error);
});
Because you use vuex, you can add some state like isLoading or isChecking.
And in your router.beforeEach, you can check and set isLoading or isChecking follow your current checking state. Then you can show loading message follow this state.
In our route.js we check in beforeEnter hooks the user has token or
not.
route.js
{
path: '/dashboard',
name: dashboard,
meta: {
layout: 'home-layout'
},
components: {
default: Dashboard,
header: UserHeader
},
beforeEnter: ifAuthenticated,
}
route.js
const ifAuthenticated = (to, from, next) => {
if (localStorage.getItem(token)) {
next();
return;
}
router.push({
name: 'login',
params: {
returnTo: to.path,
query: to.query,
},
});
};