when to initialize vue and vuex state - vue.js

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>

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

Detect exiting route changes in Vue

I'm working on a component which enables the user to undo the deletion of an item. However, the item should be deleted when the user navigates to another route. To achieve this I'm watching the route like so:
`watch: {
$route(to, from) {
if (this.showUndo === true) {
console.log('item will be deleted');
this.confirmDelete();
}
},
},
`
Unfortunately, this gets only triggered when I enter this specific route and not on exiting it. An explanation why that is or an alternative to my watch: - method would be much appreciated!
Basically I'm looking for an alternative to beforeRouteLeave since this is a sub-component and therefore I can't use Navigation Guards. Thanks!
Vue lifecycle hook- BeforeDestroy is fired right before teardown. Your component will still be fully present and functional. If you need to cleanup events or reactive subscriptions,
beforeDestroy would probably be the time to do it.
<script>
export default {
beforeDestroy() {
//Try like this
this.confirmDelete();
console.log('item will be deleted');
}
}
</script>
Ref - https://v2.vuejs.org/v2/api/#beforeDestroy

How would a WebTorrent VueJS component could be build?

Hy there. Given webtorrent.io I would like to build a VueJS component that shows loading magnet files, and status and also when downloads finishes triggers players.
Is the Vuex Store a good place to keep a list of active download queues and a stream o data?
All that is possible with webtorrent.
var WebTorrent = require('webtorrent')
var client = new WebTorrent()
var magnetURI = 'magnet: ...'
client.add(magnetURI, { path: '/path/to/folder' }, function (torrent) {
torrent.on('done', function () {
console.log('torrent download finished')
})
})
How could I structure that architecture/pattern with VueJS? Any insights, apreciated
Thanks!
I will answer cause I found a solution. Then if the community wants this to be deleted, its all right.
Basically what I did is wait for a Component to be mounted.
Inside AComponent.Vue
<template>
<keep-alive>
<!-- HTML binding webtorrent client data -->
</keep-alive>
</template>
<script>
export default {
mounted() {
var client = WebTorrent()
}
}
</script>
Why this achieve what I was looking for ? :
keep alive keep that parts of the DOM alive, when i render another template and come back to this one (as if the download queue is in a widget, modal, or another view that needs to keep alive).
mounted, its part of the component lifecycle hooks (https://v2.vuejs.org/v2/guide/instance.html#Lifecycle-Diagram), and happens once the DOM has been maniuplated, window exists (needed by WebTorrent), and the component has been rendered.

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('/');
}
});

Keeping logged in user information

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