Nested routes not rendering in router-view - vue.js

I'm setting up what is going to be a large system, so I want to have decentralised router.js files for every module. Each of them would have routes arrays declarations that would be pushed to the main routes array.
So for now I have a kind of root of routes file, which have the VueRouter instances and settings, and it imports the first level of route.js files to build the routes array.
main routes.js file
import DashboardRoutes from './views/dashboard/routes'
Vue.use(Router)
export default new Router({
mode: 'history',
base: process.env.BASE_URL,
routes: [
{// Root public site
path: '/',
component: () => import('./views/pages/Index'),
children: PagesRoutes
},
name: 'page_error',
path: '*',
component: () => import('./views/error/Error')
}
].concat(
DashboardRoutes
)
})
module (first level) routes.js file
import InventoryRoutes from './inventarios/routes'
const routes = {
path: '/panel',
component: () => import('./Index'), // This Index has a layout with a router-view in it
children: [
// Inicio del panel
{
name: 'dashboard',
path: '',
component: () => import('./Dashboard'),
}
].concat(
InventariosRoutes
)
}
export default routes
Components (second level) routes.js file
const routes = {
path: 'inventario',
name: 'panel_inventario',
component: { template: '<router-view/>' },
children: [
// Productos y servicios
{
name: 'panel_inventarios_productos-servicios',
path: 'productos-servicios',
component: () => import('./productos-servicios/ProductosServiciosHome'),
},
]
}
export default routes
In the components tree of vue-tools I see an AnnonymousComponent in the place where my children's router-view should be.
Update
I just create an external component to name it and check if it's being rendered like this
Components (second level) routes.js file
const loader = {
template: `
<div class="InventarioLoader">
<router-view></router-view>
</div>
`,
name: 'InventarioLoader'
}
const routes = {
path: 'inventario',
name: 'panel_inventario',
component: loader,
children: [
// children go here
]
}
Now I see my InventarioLoader component, but I still don't see my children components rendered in it
Update
I see this error on the console
You are using the runtime-only build of Vue where the template compiler is not available. Either pre-compile the templates into render functions, or use the compiler-included build.

You are using the runtime-only build of Vue where the template compiler is not available. Either pre-compile the templates into render functions, or use the compiler-included build.
By default, Vue does not compile string templates. This is for performance reasons.
Since the runtime-only builds are roughly 30% lighter-weight than their full-build counterparts, you should use it whenever you can
See https://v2.vuejs.org/v2/guide/installation.html#Runtime-Compiler-vs-Runtime-only
Either create a real single-file component or use a render function
import Vue from 'vue'
const RouterView = Vue.component('router-view')
const loader = {
name: 'InventarioLoader',
render (createElement) {
return createElement(RouterView)
}
}

Apparently now you can't point a route to a inline component like {template:'<router-view/>} and my solution was to create a loader component with that template and use it on the parent route

Related

VueJS-3 How to render <router-view></router-view> from vue-router?

In vuejs 2 I was able to render directly in the router by return a render html tag. I'm trying to do the same things in vue3 with vue-router 4, but it doesn't appear to work:
{
path: 'posts',
redirect: '/posts/all',
name: 'posts',
meta: {'breadcrumb': 'Posts'},
component: {
render() {
return h('router-view');
}
},
children: [ //Lots of sub routes here that I could load off the anonymous router-view]
}
Any idea how I can get the component to render that router-view and allow me to use my children routes? I rather not load up a single component just to house "". This worked perfectly in vue-router 3, no idea what is wrong with 4. I also am importing the {h} from 'vue'.
From the Vue 3 Render Function docs:
In 3.x, with VNodes being context-free, we can no longer use a string ID to implicitly lookup registered components. Instead, we need to use an imported resolveComponent method:
You can still do h('div') because it's not a component, but for components you have to use resolveComponent:
import { h, resolveComponent } from Vue; // import `resolveComponent` too
component: {
render() {
return h(resolveComponent('router-view'))
}
},
Alternatively, if you wanted to use the runtime compiler, you could use the template option (not recommended because the compiler increases app size):
component: {
template: `<router-view></router-view>`
}
Since Vue Router 4, you can use the RouterView component directly:
import { RouterView } from 'vue-router';
//...
{
path: 'posts',
redirect: '/posts/all',
name: 'posts',
meta: {'breadcrumb': 'Posts'},
component: RouterView, // <- here
children: [ /* ... */ ]
}
Source: https://github.com/vuejs/vue-router/issues/2105#issuecomment-761854147
P.S. As per linked issue, there are allegedly some problems with <transition> when you use this technique, so use caution.

Can i split up children routes into component files?

When learning the Vue Router on https://router.vuejs.org/, the section on nested routes shows how to declare children routes.
But the only way it shows it being possible is declaring all of them in the single Router file. So if i were to build a somewhat large app index consisting of several independent apps, and i wanted each of those apps to have routing and links pointing to whatever pages they use, then it would be inconsistent and hard to maintain if it were mandated that those routes were all to be declared in the main router config. I guess i'm looking for a more separation-of-concerns approach.
Let's say if one of my apps was a Todo App whose main component is defined in TodoApp.vue, the kind of thing i'm hoping for is that i could define all the routes for this Todo app in its .vue file, and then the main router config would import it, and treat those routes as children of the main /todo route, assuming that /todo is the path to the Todo App.
For example, let's say this is an excerpt of my Router definition, showing that Todo is one of my apps that has some subnavigation going on:
const router = new Router({
base: process.env.BASE_URL,
routes: [
{
path: '/todo',
name: 'TodoApp',
component: Todo
children: [{
path: 'create-task',
component: TodoCreateTask,
},{
path: 'edit-tasks',
component: TodoEditTask,
},{
path: 'create-task',
component: TodoCreateTask,
}]
]
});
I am wondering if it would be possible to remove the children part from this declaration, move it into the Todo component file, and then do some kind of an import here?
You can simply store children routes in a separate file as a regular array, for example:
subroutes.js
// import the components that are being referenced
export default [{
path: 'create-task',
component: TodoCreateTask,
},{
path: 'edit-tasks',
component: TodoEditTask,
},{
path: 'create-task',
component: TodoCreateTask,
}]
App.vue
import subroutes from './subroutes';
const router = new Router({
base: process.env.BASE_URL,
routes: [
{
path: '/todo',
name: 'TodoApp',
component: Todo,
children: subroutes,
}
]
});

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!?

How to get component name or the python from vue-router?

I use vue-router likeļ¼š
export default new Router({
routes: [
{
path: '/',
components: {
navbar: Navbar,
subnavbar: SubNavbar,
main: WhoisPage //********
}
}
In my component WhoisPage.vue, I want to know the component path or the main module(which is //****** line) use which component?
How to do it with vue or vuex?
First of all, there is no "components" property on vue-router.
You need to use "component" property to inject your shell component (in this case, it looks like WhoisPage) then you can call your sub-components to build your page inside your shell component.
If you want to change your sub-components based on route automatically, you can send their names as string and render in your shell-component.
// in your router config.
export default new Router({
routes: [
{
path: '/',
component: WhoisPage,
meta: {
navbar: 'NavbarComponent'
}
}
]
})
<!-- in your shell component, ofc your navbar needs to be imported -->
<component :is="$route.meta.navbar"></component>
Not sure i correctly answered what you need tho.

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.