What is the recommended way to use vue-router after authenticating users? - vue-router

Say I just started a vue-nodejs-webpack simple project with vue cli:
vue create xxx
How should I plug its default client-side routing to allow certain routes based on authentication? in the most idomatic way?
My App.vue will look something like this:
<template>
<v-app class="grey lighten-4">
<Navbar />
<v-content class="mx-4 mb-4">
<router-view></router-view>
</v-content>
</v-app>
</template>
Whereas "Navbar" is a simple top navigation menu, and router-view simply shows content based on current route.
Route.js will look like this:
import Vue from 'vue'
import Router from 'vue-router'
import Dashboard from './views/Dashboard.vue'
import Projects from './views/Projects.vue'
import Team from './views/Team.vue'
Vue.use(Router)
export default new Router({
mode: 'history',
base: process.env.BASE_URL,
routes: [
{
path: '/',
name: 'dashboard',
component: Dashboard
},
{
path: '/projects',
name: 'projects',
component: Projects
},
{
path: '/team',
name: 'team',
component: Team
}
]
})

In roter.js, you can use vue-router hooks to have precise control on route whether to render components or not
Here in the below example isAunthenticates is a separate javascript function which returns boolean, by checking in the backend whether the user is authenticated or nor
router.beforeEach((to, from, next) => {
if (!isAuthenticated) next('/login')
else next()
})
At the same time you can also use pre router hooks to prevent the user to enter specific route
const router = new VueRouter({
routes: [
{
path: '/foo',
component: Foo,
beforeEnter: (to, from, next) => {
// ...
}
}
]
})
Also you can use In-Component Guards at specific components to verify whether the user has specific access to this component or not

Related

Can I directly pass a label for a named route to<router-link>?

Say in my router.js I have something like this:
const routes = [
{
path: '/',
name: 'home',
label: 'Start',
component: Home
}
]
Then where I render my router link, I'd like to be able to use the alternate label property declared above, instead of manually entering it, and to avoid defining it somewhere else. I tried the bit below but it does not work (it works for the 'path' property though)
<router-link :to="{name: 'home'}">{{ this.$router.label }}</router-link>
EDIT: I found that (obviously!) this.$router refers to the current, active route.
For now at least, this is my solution:
In router.js (or /router/index.js in my case) declare the routes with labels as indicated in the original question. And export both the routes and the router object.
const routes = [
{
path: '/',
name: 'home',
label: 'Start',
component: Home
}
...
]
const router = new VueRouter({
mode: 'history',
base: process.env.BASE_URL,
routes
})
export { router as default, routes };
In main.js import both variables and store routes in a way you can retrieve them anywhere.
import router, { routes } from '#/router'
Vue.prototype.routes = routes
new Vue({
router,
store,
vuetify,
render: h => h(App)
}).$mount('#app')
Where you want to "render" the route links (in my case a navbar layout), do this (using vuetify here):
<v-app-bar app color="secondary" dark>
<v-btn v-for="route in routes" :to="{name: route.name}" key='route.name' text rounded :exact="true">
{{ route.label }}
</v-btn>
</v-app-bar>
I'm sure this can be improved probably using Vuex (which I'm still studying about).
UPDATE
Found a much better solution, that don't require the routes export/import and its assignment to Vue.prototype.routes = routes. Simply do this in the component where you want to use the routes data with labels and all. The template piece (v-btn etc) remains the same.
<script>
export default {
name: 'LayoutNav',
data() {
return {
routes: this.$router.options.routes
};
}
};
</script>

How to create a login screen which don't include App.vue components

I am creating a SPA which have a login screen and other views. But the problem I am facing is, the login screen view also includes in the Navigation bar but It should not be there. Then I used Router navigation which displays a navigation bar alone without any views I think it also guards the Login view also.
App.vue
<template>
<section id="app" class="hero">
<section class="main-content columns is-fullheight has-background-white-bis">
<Navigation />
<div class="hero-body">
<router-view />
</div>
</section>
</section>
</template>
<style>
.menu {
margin: 25px;
}
</style>
<script>
import Navigation from "#/components/Navigation.vue";
export default {
name: "app",
components: {
Navigation
}
};
</script>
router/index.js
import Vue from 'vue'
import VueRouter from 'vue-router'
Vue.use(VueRouter)
const routes = [{
path: '/LoginUser',
name: 'login',
component: LoginUser,
},
{
path: '/',
name: 'dashboard',
component: Dashboard,
},
}]
const router = new VueRouter({
mode: 'history',
base: process.env.BASE_URL,
routes
})
var isAuthenticated = false;
router.beforeEach((to, from, next) => {
if (!isAuthenticated) next('/LoginUser')
else next()
})
export default router
To remove the navigation when specifically on the Login route, you can add a v-if to your <Navigation /> component, which checks if the route is not on the login page:
<Navigation v-if="this.$router.currentRoute.name !== 'login'" />
We can conditionally render components by checking the name of the current route using v-if directive. I used this in a recent project.
My route index.js file
import Vue from 'vue'
import VueRouter from 'vue-router'
import Home from '../views/Home.vue'
import Login from '../views/Login.vue'
Vue.use(VueRouter)
const routes = [
{
path: '/',
name: 'home',
component: Home
},
{
path: '/login',
name: 'login',
component: Login
},
{
path: '/register',
name: 'register',
component: Register
}
]
const router = new VueRouter({
mode: 'history',
base: process.env.BASE_URL,
routes
})
export default router
My App.vue file
<template>
<div id="app">
/* Note that this takes an array of route names, so you can simple pass in
the name of the route you dont want this component to be displayed on */
<navbar v-if="!['login', 'register', 'help'].includes($route.name)" />
<main>
<router-view />
</main>
<appfooter v-if="!['login', 'register'].includes($route.name)"/>
</div>
</template>

Understanding Vue routing

I am trying to understand vue-router. But so far I was not able to get what I need. In simple terms, when I go to example.com, it should show the Home component and if I go to example.com/:id, I want SavedComparator component to be loaded. My router.js is as below:
import Vue from 'vue'
import Router from 'vue-router'
import Home from './views/Home.vue'
import SavedComparator from './views/SavedComparator.vue'
Vue.use(Router)
export default new Router({
routes: [
{
path: '/',
component: Home,
children: [
{
path: '/:id',
component: SavedComparator
}
]
}
]
})
In my App.vue, I have below code segment:
<v-content>
<h2>params: {{ $route.params.id }}</h2>
<router-view />
</v-content>
But when I go to example.com/h8s7f, it always shows Home component. In addition, $route.params.id in App.vue doesn't print anything.
The problem is that you are expecting the router to behave as if it had history mode activated
Either turn history mode on:
export default new Router({
mode: "history",
routes: [
Or access the url as example.com/#/h8s7f (with the hash before the route)
Here is an example: you can access the route by adding / + an id
https://codesandbox.io/s/vue-routing-example-33p1n?fontsize=14
you don't have to start your route with '/' in your nested routes because routes starting from '/' will be considered parent route try this in your children
{
path: 'comparator/:id',
component: SavedComparator
}
and then example.com/comparator/id will route you to correct component

Vue-router reloads page and I lose my state, how do i avoid this?

I have a form divided in 5 components and the user can navigate through them via steppers (I'm using vue-material for my project). I use vue-router for that. However, I'm having a serious issue here: components lose all the information in the store (I'm using vuex) when they come back to a route they already filled. So to make it clear: if a user fills the first step of the form and then goes to step two, when he wants to come back to step one data is no longer available and the form is totally empty (and the state in vuex is also reset). What am i doing wrong?
import Vue from 'vue'
import Router from 'vue-router'
import Projet from '#/components/Fiches/Projet/Projet'
Vue.use(Router)
export default new Router({
routes: [
{
path: '/',
name: 'Home',
component: Projet
},
//other routes here
]
})
And this is the html code
<template>
<div class="project-steppers">
<md-steppers md-dynamic-height md-alternative>
<md-step id="first" to="/Projet" md-label="Projet" />
// other steps here
</md-steppers>
</div>
</template>
And an example of one of the inputs I use:
<md-field>
<label for="project-name">Nom du projet</label>
<md-input id="project-name"
v-model="project.projectName"
name="project-name"
#change="updateProjectName"/>
</md-field>
[...]
methods: {
updateProjectName () {
this.$store.commit(projectStore.MUTATE_PROJECTNAME, this.project.projectName)
}
More information: when I fill the different inputs I see that the store is updated with the new values, so the mutation is working.
First of all, Vuex does not store data in the browser - just in memory. That means that you could either install a third party plugin such as vuex persisted state or write your own methods to set and get the items from your storage, e.g.:
const storage = localStorage.getItem('key');
new Vuex({
state: {
yourProp: storage ?
? JSON.parse(storage.yourDataKey)
: 'default-value'
},
actions: {...}
mutations: {...}
})
I think to should use router-link or $router.push().
Vue:
import Vue from 'vue'
import Router from 'vue-router'
import Projet1 from '#/components/Fiches/Projet/Projet1'
import Projet2 from '#/components/Fiches/Projet/Projet2'
import Projets from '#/components/Fiches/Projet/Projet' //with props
Vue.use(Router)
export default new Router({
routes: [
{
path: '/',
name: 'Home',
component: Projet1 // default project
},
{
path: '/Projet1', // url for the same component
name: 'Projet1',
component: Projet1
},
{
path: '/Project2',
name: 'Projet2', // url for the another component
component: Projet2
},
{
path: '/Project/:id',
name: 'Projets', // url for a component with props
component: Projet,
props: true
}
]
})
HTML: A way to call Projet without reloading with router-link
<template>
<router-link to="/Home"></router>
<router-link to="/Projet1"></router>
<router-link to="/Projet2"></router>
</template>
js: I would add a router push
updateProjectName () {
this.$store.commit(projectStore.MUTATE_PROJECTNAME, this.project.projectName)
this.$router.push('/' + this.project.projectName)
}
Your question looks like the issue opened by kristianmandrup:
menu or tabs with router links!?

Issue rendering child views in Vue with Vue Router

I'm having trouble getting my child views to render in Vue.
My main.js file looks like this
import DashboardProducts from './components/Dashboard/DashboardProducts'
import DashboardSettings from './components/Dashboard/DashboardSettings'
Vue.use(VueRouter)
Vue.use(Vuex)
const routes = [
{ path: '/activate', component: Activate },
{ path: '/dashboard/:view', component: Dashboard,
children: [
{ path: 'products', component: DashboardProducts },
{ path: 'settings', component: DashboardSettings }
]
},
{ path: '/login', component: Login },
{ path: '/account', component: UserAccount }
];
const router = new VueRouter({
routes // short for routes: routes
});
export default router;
/* eslint-disable no-new */
new Vue({
el: '#app',
router,
store,
render: h => h(App)
});
As you can see I have imported the components and get no errors. I have also added them as children of Dashboard and set their paths.
In my Dashboard.vue view I do this
<template>
<div>
<dashboard-nav></dashboard-nav>
<!-- Will display product and settings components -->
<router-view></router-view>
</div>
</template>
<script>
import DashboardNav from '../components/Dashboard/DashboardNav'
export default {
name: 'Dashboard',
components: {
DashboardNav
}
};
</script>
<style>
</style>
Urls are matching but no components are rendering. What am I missing?
Here is a JSFiddle of pretty much what I'm going for https://jsfiddle.net/dtac5m11/
It seems to be working fine there but I'm also using single file components in my app so it may be a little different?
Again, the issue is getting the child components to render when their routes match. Currently no components are being mounted.
UPDATE:
I am getting the DashboardProducts component to render but can't get DashboardSettings to render.
Thanks!
{ path: '/dashboard/:view', component: Dashboard,
At first, for what purpose do you add :view after dashboard path? If you are using this one for children path as a parameter, it is an issue. It is the reason, why your children component are not rendering. Because, :view is for dynamic routes. /dashboard/:view is equivalent to /dashboard/* and it means that after /dashboard there can be any route and this route will render Dashboard component. And your children paths /dashboard/products and /dashboard/settings will always match /dashboard/:view and render parent component-Dashboard.
So, in your case, your routes for children components are known. So you do not need to use :view.
More, https://router.vuejs.org/en/essentials/dynamic-matching.html.