Keeping logged in user information - vue.js

I managed to log in with laravel passport. I have the token, great. But I want to keep logged in user information, name, avatar and so on.
My login procedure gets oauth token. In dashboard component I make api call for user data.
Should I keep them in global Vue object or use Vuex? Is it safe?

Some options you might consider
store data in cookies
use localStorage
keep everything in the root vue instance
keep everything in a wrapping vue component
My suggestion would be to store the auth token - that is actually required to successfully call your backend - in a cookie. This will make it super easy to access it with each and every request you send.
To store the user information I'd suggest to either create a wrapping component or use the root vue instance. The following example should clearify this.
wrapping home component (inline template)
data: function() {
return { userinfo: {} }
},
created: function() {
// load your user info
}
Then use it in your index.html / main view
<body>
<home inline-template>
<!-- any stuff here -->
<!-- pass the user prop to every component that is shown in the userinfo -->
<router-view :user="userinfo"></router-view>
</home>
</body>
Your components that are shown in the router-view can then access the user prop
example component
...
props: ['user'],
...
<template>
<span>{{ user.name }}</span>
</template>
...
IMPORTANT: to make this work you will also need to add props: true to the definition of your route. Everything is explained here in detail: https://router.vuejs.org/en/essentials/passing-props.html
Remark: If you don't want to load userdata in your wrapping component you can load it anywhere else and use an event bus to transfer the results to the wrapping component. However, you should always have only ONE source of truth regarding the user info. Store it only at a single place.

I found the session helper really easy to use and it solved the problem of globally using the oauth token:
See: https://laravel.com/docs/5.4/session, heading: The Global Session Helper

Related

Vue - check localStorage before initial route (for token)

I have a Vue app which does a little localStorage and server check on app load, to determine where to initially route the user.
This is in the App's main entry component, in the created() hook
My problem is that the default / route's Component visibly loads first, then the server call and everything happens which causes the user the route to their correct location
How can I delay the rendering of the initial component until my app's main component created() method completes, and then purposely navigates the user to the correct route?
I had this problem before and I firmly believe that you must have the initial files for your routes and your router configuration.
In the configuration, you could handle the permission and router before each route and with next() . In the router file, you can set your params and check them in the index.js file(router configuration)
you could also use your localStorage data in Router.beforeeach
EDIT: I just saw you used the created method... like mentioned below use beforeRouteEnter instead with the next() parameter it provides
First of all I wouldn't recommend using a delay but instead a variable that keeps track if the API call is done or not. You can achieve this using the mounted method:
data() {
return {
loaded: false,
}
}
async mounted() {
await yourAPICALL()
if (checkIfTokenIsOkay) {
return this.loaded = true;
}
// do something here when token is false
}
Now in your html only show it when loaded it true:
<div v-if="loaded">
// html
</div>
An better approuch is using the beforeRouteEnter method which allows you to not even load the page instead of not showing it: https://router.vuejs.org/guide/advanced/navigation-guards.html

Nuxt.js vuex store not persisted

I've got some strange issues with my Nuxt.js Setup.
Some States in Store arent persistent, everytime I load another view, they went back to the default value.
pages/test.vue
<template>
<section class="section">
<b-container>
<b-row>
<b-col cols=12>
<b-button #click="setTest" variant="dark">test</b-button> | {{this.$store.state.test}} |
</b-col>
</b-row>
</b-container>
</section>
</template>
<script>
export default {
name: 'test',
methods: {
setTest() {
this.$store.commit("setTest")
},
}
}
</script>
store/index.js
export const state = () => ({
test: "test"
})
export const mutations = {
setTest: (state) => state.test = 'Hello'
}
Testscenario is to hit the "test"-button who call the method with mutation-commit "setTest" which set the state to "Hello". Currently it works fine, but if I changed the view or reload the page, the state is set to default "test".
What am I doing wrong?
Alright, so the behavior is totally logic. Vuex/Pinia are not supposed to be persistent.
For any persistence on the front-end, you need either:
cookies
localStorage
pass it in the URL (query params)
IndexedDB
get the data back from making a call to a backend
If you are using Vuex or Pinia, there are also packages that you could use to get an easier time (to sync your store into something persistent automatically).
Some of the packages here may be useful: https://github.com/vuejs/awesome-vue#persistence
For pinia: https://stackoverflow.com/a/73863929/8816585
If you reload your page with an F5, all your JS will be wiped and loaded again. Hence, no state will be kept since it will be a brand new page. When working with frontend frameworks, you cannot expect it to just work after a page refresh.
Same go when you do follow an href, it is an actual real navigation. What you need to do, is to use a <nuxt-link></nuxt-link> component, with something like to="/profile" to let VueJS move to this URL.
NuxtLink is a Nuxt.js component and essentially a component on top of <router-link></router-link>, which is Vue router.
TLDR: you cannot use things like window.location.href, nor <a href="...". You may use the given components by either Nuxt (nuxt-link) or Vue's router (router-link), if you're using VueJS only.
Giving a read to the Vue router's documentation may be a good start to understand things a bit more !
If you're using nuxt/auth, give a try to that one: https://auth.nuxtjs.org/api/storage/#universal-storage

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

when to initialize vue and vuex state

Recently I design web that user was separated into "user"/"admin" two roles to login.
Different role would login same page but see the different buttons and functions.
MainPage.vue is the container so I call ajax "/init" to get user info and then commit to Vuex store.
Then Content.vue is the child page inside MainPage.vue. It would show different buttons by $store.state.user message.
However, I would like to call different api in mounted stage inside Content.vue according to user's role.
But the role would not be prepared which is commited by ajax called "/init" at this stage.
The overall flow is
// MainContent.vue
beforeCreate: async function () {
await axios.post('/init').then(response => {
if(response && response.data && response.data.data){
this.$store.commit("login", response.data.data)
}
})
}
// Content.vue
mounted: async function() {
try {
console.log(this.$store, this.$store.state.user, this.$store.getters.isAdmin)
// no value here
}
I have check the vue component lifecycle and saw beforeCreate of parent would be called before mounted of child.
Would lifecycle methods call in order even they are async function?
How should I solve this problem?
Thanks
You could delay the initialization of the Content component until the user state becomes available in Vuex by attaching a v-if directive to the Content component. Just make sure to initialize user with null in the Vuex state.
<!-- MainPage.vue -->
<template>
<Content v-if="$store.state.user" />
</template>
Now this.$store.state.user and everything that depend on it should be available from Content component's mounted lifecycle hook.
Your component lifecycle will not be a dependable way to handle this case since you're using an async call to fetch the user data. I would suggest displaying a loading state overlay and using mapGetters to access the store. This will provide a reactive mechanism for you to know when the user data is available. Below is a general idea for a single file component:
computed: {
...mapGetters({
getLoggedInUser: 'GET_LOGGED_IN_USER',
getIsUserAdmin: 'GET_IS_USER_ADMIN',
})
}
....
<template>
<content-overlay v-if="!getLoggedInUser">
Loading...
</content-overlay>
....
<button v-if="getIsUserAdmin">Admin Button</button>
<button v-if="!getIsUserAdmin">Regular User Button</button>
</template>

vuejs component created wait for data to load first

I have an App.vue component where I want to load the currently logged in user. I also want to redirect the user when he\she tries to go the login route and the currently logged in user is already saved in context (he's already signed in).
// inside App.vue component
created() {
AuthService.getCurrentUser().then((user) => {
this.user = user;
});
}
I have a check in the created method of the login component for whether the currentUser is setted, but then when the user tries to go to the login page it might be possible that the the request for the current user is not finished.
My question is:
How do I wait for the data to load before the App.vue component loads?
I saw something like this:
waitForData: true,
data(transition) {
return AuthService.getCurrentUser().then(currentUser => {
transition.next({ currentUser });
});
}
which doesn't actually wait for the data to be loaded and the component loads anyway.
Edit: I'm aware of beforeRouteEnter but this is App.vue component which is a parent component of all components and not a route specific component.
I ran into a very similar problem and solved by adding a v-if on the element that wrapped the child component that depends on the loaded data. And then of course have an data property to control that v-if. If you don't have a wrapping element you can always use a template element to do the wrapping.
This is not the Vue way of doing things.
Use VUEX to store your currentUser object
Set up the Vuex getters in App.vue's computed section. Your template will be updated dynamically once the data is ready.
See the mapGetters section in this page. It works very well with the computed mechanism.
You can also use v-if in your relevant component, so that the component won't be created before your data is ready in VUEX.
If using vue-router, you can use the beforeRouteEnter guard to load data async, as described here: https://router.vuejs.org/en/advanced/data-fetching.html
One way I can think of is when the user is loaed in the App.vue component to check the current path and if it's \login to redirect.
Something like this:
created() {
AuthService.getCurrentUser()
.then(user => this.setCurrentUser(user))
.then(() => {
const { path } = this.$router.currentRoute;
if (path === '/login') {
this.$router.push('/');
}
});