Understanding Vue routing - vue.js

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

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>

Vue Router import not working with require

I'm setting up a brand new Vue 2 project. Due to compatibility issues with one of the libraries I need to use, I can't yet move to Vue 3.
I have a very basic setup at the moment, folder structure something like this:
/
App.vue
main.js
router.js
/pages
AboutUs.vue
Home.vue
If I don't use Vue Router, and just import the AboutUs page into App.vue and call its tag in the template, it displays as expected.
When I instead use the Vue Router to render it in the <router-view /> tag in App.vue, I get the error message:
[Vue warn]: Failed to mount component: template or render function not defined.
I suspect that means I'm doing something wrong in the router but I can't see what.
main.js
import Vue from 'vue'
import App from './App.vue'
import router from 'router.js'
new Vue({
render: h => h(App),
router
}).$mount('#app')
App.vue
<template>
<div id="app">
<router-link to="/">Home</router-link>
<router-link to="/About">About</router-link>
<router-view />
</div>
</template>
<script>
export default {
name: 'App'
}
</script>
router.js
import Vue from 'vue'
import VueRouter from 'vue-router'
Vue.use(VueRouter)
const About = require('../pages/AboutUs')
const Home = require('../pages/Home')
const routes = [
{
path: '/about',
name: 'About',
component: About
},
{
path: '*',
name: 'Home',
component: Home
}
]
const router = new VueRouter({
linkExactActiveClass: '',
routes
})
export default router
About.vue
<template>
<div>
<h1>About</h1>
</div>
</template>
<script>
export default {
}
</script>
es6 import
Try to use an es6 import instead of require:
import About from '../pages/AboutUs'
import Home from '../pages/Home'
Then your route syntax will work as is. This is because when you use require, you get the whole module rather than the Component export from the module.
-or-
require
Alternatively, if you wanted to continue using require, you would need the following syntax, using the default property of the module:
const About = require('../pages/AboutUs')
const Home = require('../pages/Home')
const routes = [
{
path: '/about',
name: 'About',
component: About.default
},
{
path: '*',
name: 'Home',
component: Home.default
}
]

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

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

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.