I have an app built with Vue.js. In this app, I'm using the Vue Router. I successfully have top-level routes working. However, I'm unable to get child routes to work. My code seems to ignore any routes defined within the children property of a route. I've setup the problem in this fiddle. The relevant code looks like this:
const CustomerMenu = { template: '<div><br />please choose: <router-link to="/customers/list">list</router-link> <router-link to="/customers/stats">stats</router-link></div>' }
const CustomerList = { template: '<div>A list of customers here</div>' };
const CustomerStats = { template: '<div>Customer statistics</div>' };
const Suppliers = { template: '<div>Suppliers</div>' }
const router = new VueRouter({
mode: 'history',
routes: [
{ path: '/customers', component: CustomerMenu, children: [
{ path: 'list', component: CustomerList },
{ path: 'stats', component: CustomerStats }
]},
{ path: '/suppliers', component: Suppliers }
]
})
new Vue({
router,
el: '#app',
data: {}
});
How do I show the content associated with a route defined in the children property of a route. In other words, how do I get the "list" or "stats" components to appear when a user selects there respective links?
Thank you!
You were missing <router-view/> inside your CustomerMenu component.
http://jsfiddle.net/sn5vu6ek/
Remember - every component which has children routes has to have to be able to display children components.
If you just want to have some part of path common between components, you can either create parent with just <router-view/> inside, or specify full path for multiple components.
Related
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>
I have an App component and it renders fine. It's basically a component comprising two components.
App (Vue component):
1. Navigation // a menu bar with menu items
//like <router-link to="/">Home</router-link>
//like <router-link to="/contact">Contact</router-link>
2. Library // a component listing books.
I want to add another component Contact which I tried adding using vue-router, so I use the above navigation component. However, the routes don't seem to affect anything at all. What am I doing wrong?
My main.js file
const Contact = {
template: '<div>Contact Us</div>'
}
const routes = [
{
path: '/',
component: App // any change here also does not make any difference!!!
},
{
path: '/contact',
component: Contact, // this or any component doesnt make any difference!!!
}
];
const router = new VueRouter({
routes,
});
new Vue({
render: h => h(App),
router,
}).$mount('#app')
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
I am researching whether a vue router is the best approach for the following scenario:
I have a page containing 'n' number of divs. Each of the divs have different content inside them. When a user clicks on a button in the div, I would like the div to open in a separate browser window (including its contents).
Can a route name/component be created on the fly to route to? Since I have 'n' number of divs, that are created dynamically, I cannot hard-code name/components for each one
<router-link :to="{ name: 'fooRoute'}" target="_blank">
Link Text
</router-link>
I want to avoid the same component instance being used (via route with params) since I may want multiple divs to be open at the same time (each one in their own browser window)
If the link is opening in a separate window, it makes no sense to use a <router-link> component as the application will load from scratch in any case. You can use an anchor element instead and generate the href property dynamically for each div.
To answer your questions:
A route name cannot be created dynamically since all routes must be defined at the beginning, when the app (along with router) is being initialized. That said, you can have a dynamic route and then dynamically generate different paths that will be matched by that route.
There is no way for the same component instance to be reused if it's running in a separate browser window/tab.
It is possible to create dynamic router name.
profileList.vue
<template>
<main>
<b-container>
<b-card
v-for="username in ['a', 'b']"
:key="username"
>
<b-link :to="{ name: profileType + 'Profile', params: { [profileType + 'name']: username }}">Details</b-link>
</b-container>
</main>
</template>
<script>
export default {
name: 'profileList',
data () {
return {
profileType: ''
}
},
watch: {
// Call again the method if the route changes.
'$route': function () {
this.whatPageLoaded()
}
},
created () {
this.whatPageLoaded()
},
methods: {
whatPageLoaded () {
this.profileType = this.$route.path // /user or /place
}
}
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style>
</style>
b-container, b-card, b-link are taken from bootstrap-vue, so you can freely change it.
router.js
const router = new Router({
mode: 'hash',
base: process.env.BASE_URL,
linkExactActiveClass: 'active',
routes: [
// USERS
{
path: '/user/:username',
name: userProfile,
component: userProfile
},
{
path: '/user',
name: 'userList',
component: profileList
},
// PLACES
{
path: '/place/:placename',
name: placeProfile,
component: placeProfile
},
{
path: '/place',
name: 'placeList',
component: ProfileList
}
]
})
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.