Router link not calling router view on nested routes - vuejs2

I have a router
routes: [
{
path: '/',
name: 'home',
meta : {
label : 'Home'
},
component: Home
},
{
path: '/usuarios',
name: 'usuarios',
meta : {
label : 'Usuarios'
},
component: Users,
children : [
{
path: '/listar',
meta : {
label : 'Listar'
},
name : 'listUser',
component: Wrapper,
},
{
path: '/cadastrar',
meta : {
label : 'Cadastrar !'
},
name : 'userCreate',
component: UserCreate
},
],
},
]
And I have a template for rendering this router at my navbar
<div class="main">
<ul class="menu-list">
<li v-for="item in menus" v-on:click="toggleActive(item)">
<router-link class="font-gray" :to="item.path" :exact="true">{{item.meta.label}}</router-link>
<ul class="menu-list" v-if="item.children && item.isActive">
<li v-for="child in item.children">
<router-link class="font-gray" :to="{path : item.path+child.path}" :exact="true" :append="true" >{{child.meta.label}}</router-link>
</li>
</ul>
</li>
</ul>
</div>
When I click on the first router-link, the view is rendered with no problem at the router view. But when I click on the router link of childrens, it does not work,
Anyone have any idea why?

I finally figured it out.
If you have children, the father component should have a <router-view> as well
Found the answer here: Vue router2 not catching deep nested routes
Thanks everybody for trying help

This issue is happening because you have forward slashes / prepended to the paths of your nested routes.
From the documentation on nested routes:
Note that nested paths that start with / will be treated as a root path. This allows you to leverage the component nesting without having to use a nested URL.
This means that, even though your "listar" route is a child of your "usuarios" route, the path is /listar, not /usuarios/listar.
You should remove the forward slashes from the paths of your nested routes, and then specify the path in your template like so:
:to="{ path : item.path + '/' + child.path }"

Related

What would be the best way to use vue routes?

I'm a bit new to Vue routes. I have a table in Home.vue and each row has a button in order to go to the details. I'll try to explain what I imagine. I want the redirection to open a new screen Overview with a sidebar that has 3 options: Overview, Commits and Files. My problem is to understand what should be the parent and what should the child. I'm sure that Commits and Files are children but should Overview be also a child or the parent of Commits and Files? The row that redirects to details:
<router-link :to="{ 'name': 'details', 'params': { 'tool': tool } }">{{id}}</router-link>
The routes that I currently have:
const DetailsChildren = [
{
path: 'commits/:tool',
name: 'commits',
component: commits
},
{
path: 'files/:tool',
name: 'files',
component: files
}
];
export default new Router({
routes: [
{
path: '/',
name: 'home',
component: Home
}
{
path: '/overview/:tool',
name: 'details',
component: details,
children: DetailsChildren
}
],
mode: 'history'
});
And DetailsChildren is as follows:
<template>
<div class="main">
<sidebar />
<router-view />
</div>
</template>
Currently DetailsChildren is Overview. But I think DetailsChildren should be the parent of Overview, Files and Commits and then I need to create another component Overview. But then I have two problems. First one is how I load Overview when I move from the table to DetailsChildren? The second one is that I want the route to be /overview/:tool. I'm a bit confused. What would be best way to handle the routes in this situation?
What you want to do is have three children under your details route, each with their own absolute path from /.
const DetailsChildren = [
{
path: '/overview/:tool',
name: 'overview',
component: overview
},
{
path: '/commits/:tool',
name: 'commits',
component: commits
},
{
path: '/files/:tool',
name: 'files',
component: files
}
];
This will create the following path mappings
/overview/:tool
Top-level component: details
Child component: overview
/commits/:tool
Top-level component: details
Child component: commits
/files/:tool
Top-level component: details
Child component: files
See the guide on Nested Routes.
You may think you can use an empty path for the overview route but this would then require the URL to have a trailing slash (ie /overview/tool/) and I figured you don't want that.
It's also recommended to remove the name from your details route and instead, link to the default child route (ie overview). Eg
<router-link :to="{ name: 'overview', params: { tool } }">{{id}}</router-link>
Otherwise, the router gets confused about which to display, the parent (empty) or child.
Your sidebar links can simply use the child route names to create their links, eg
<nav>
<ul>
<li>
<router-link :to="{name: 'overview'}">Overview</router-link>
</li>
<li>
<router-link :to="{name: 'commits'}">Commits</router-link>
</li>
<li>
<router-link :to="{name: 'files'}">Files</router-link>
</li>
</ul>
</nav>

Rendering VUE router links with v-for

i want to create an app that renders links dynamically with v-for and opens different components with each link.
I know how to render the <router-link> itself ,But I don't know how to dynamically change the destination url of the to="" prop.
If your array looks for example like this:
components: [
{
path: "/a",
name: "Component A"
},
{
path: "/b",
name: "Component B"
}
]
You can use it in v-for like:
<router-link
v-for="(comp, i) in components"
:key="i"
:to="{ path: comp.path }"
>
{{comp.name}}
</router-link>
You can achieve this using bind :to="{path: '/to'}" in <router-link>.

Where should route meta data be loaded in a Vue app?

I'm in the process of setting up a VueJs SPA. I'm using vue-router and I'm trying to find the best solution to the following problem. I have a series of routes. Each of which needs to call an API to get the meta data for the given ID.
/industry/:id/overview
/industry/:id/top-stories
/industry/:id/top-tweets
/brand/:id/overview
/brand/:id/top-stories
/brand/:id/top-tweets
I've been looking at using created or beforeRouteEnter/beforeRouteUpdate and I'm a bit lost. Ideally, I would only fetch new data when a new /industry/:id is reached, not when navigating between pages within the same ID. Also, I'd like to avoid having to define the fetch to grab data in every page component. Also don't want to over complicate this, so my question is, Is there a standard method for tackling this issue?
Clarification:
When I say meta here, I mean data returned from an API about the given industry or brand which I pull using the ID in the route. The api call includes the name of the industry/brand which I want to have on page as soon as the page is presented to the user.
I have something similar. I tackle this using the following approach:
I use the same component for all /industry/:id Vue likes to reuse components wherever it can so if two routes (for example /industry/:id/overview and /industry/:id/top-stories) are using the same component it will stay the same.
What does change, however, is the route meta. So if you add a page key to the meta object in the route objects, and probably add a computed property called page that return this.$route.meta.page, you can use v-if attributes to conditionally render any component. So you might have something like <div v-if="page === 'overview'"></div><div v-else-if="page==='top-stories'"></div>
What this allows you to do is fetch all the data from the API during created or mounted lifecycle and store it as the state. Since the route change doesn't reload the component the state stays the same.
Here is a code example
// router.js
const Project = () =>
import(/* webpackChunkName: "projects" */ "./views/projects/_id");
export default new Router({
mode: "history",
routes: [
{
path: "/projects/:project_id/views",
name: "ViewProject",
component: Project,
meta: {
page: "views",
}
},
{
path: "/projects/:project_id/export",
name: "ExportProject",
component: Project,
meta: {
page: "exports"
}
},
{
path: "/projects/:project_id/recommendations",
name: "ProjectRecommendations",
component: Project,
meta: {
page: "recommendations"
}
},
]
});
And here is the template
<template>
<div v-if="project">
<h1>{{ project.name }}</h1>
<router-link :to="/project/someid/views">Views</router-link>
<router-link :to="/project/someid/exports">Exports</router-link>
<router-link :to="/project/someid/recommendations">Recommendations</router-link>
<ul v-if="page==='views">
<li v-for="(view, i) in project.views" :key="i">{{ views }}</div>
</ul>
<ul v-else-if="page==='exports">
<li v-for="(export, i) in project.exports" :key="i">{{ export }}</div>
</ul>
<ul v-else-if="page==='recommendations">
<li v-for="(recommendation, i) in project.recommendations" :key="i">{{ recommendation }}</div>
</ul>
</div>
</template>
<script>
export default {
data() {
return {
project: null
}
},
computed: {
page() {
return this.$route.meta.page;
}
},
mounted() {
this.getProject()
},
methods: {
getProject() {
axios
.get(`/projects/someid`)
.then(res => this.project = res.data)
}
}
}
</script>

placing a router-view in template as a sub template this isn't working

When I place a route view in a template my vue router doesn't load the content, how can I load this?
In my homepage I've got
<router-view name="homepage"></router-view>
The code gets loaded perfectly from my vue router but the contained in the homepage template doesn't get loaded.
...
routes:
[
{
path: '/',
name: 'homepage',
components: {
homepage: HomepageTemplate,
subPart: subPartTemplate
}
}
]
...
homepageTemplate:
<template>
<div>
<router-view name="subPartTemplate"/>
</div>
</template>
You named the component subPart but are referencing to it as subPartTemplate;
<template>
<div>
<router-view name="subPart"/>
</div>
</template>
Edit: I see you edited your post; what's going wrong is you are using a child <router-view> without defining children in your routes;
You should define children in your path:
{
path: '/',
name: 'homepage',
component: HomepageTemplate,
children: [
{ path: '/', component: subPartTemplate }
]
}
Now the <router-view> in the homepageTemplate should be the subPartTemplate. No need to use named components for this. Named components are useful when you need multiple <router-view>'s in one template.
Checkout the documentation for more details.

Make the first child route as the active class whenever the sub-route path is an empty path

I have a Main Navigation nav bar with some links. In the home page, i.e., in the root path ('/'), I have nested (child) routes.
Route configration:
const routes = [
{path: '/', component: HomeMainContent, children: [
{path: '', component: MovieList},
{path: 'in-theaters', component: MovieList},
{path: 'coming-soon', component: ComingSoonList},
{path: 'trailers', component: TrailerList},
]},
{path: '/about', component: About},
{path: '/contact',component: Contact},
];
So when the root path ('/') is active, its child router root path ('') is activated.
The template of child component router is in this way:
<template>
<div id="homeSubMenuWrapper">
<ul id="subMenuList">
<li v-for="menu in menuList">
<router-link class="menuItem" :to="menu.path">{{menu.name}}</router-link>
</li>
</ul>
</div>
</template>
<script>
export default {
data() {
return {
menuList: [
{name: 'IN THEATER', path: '/in-theaters'},
{name: 'COMING SOON', path: '/coming-soon'},
{name: 'TRAILERS', path: '/trailers'},
]
}
}
}
</script>
Since, both the path ('') and ('in-theaters') have the same component, I would like to make the router-link of path ('in-theaters') to have the class of router-link-active whenever the child path ('') of its parent path ('/') is active. How can I do that?
Meaning the first child route ('in-theaters') should have the active class whenever the sub-route path is an empty path ('').
With #Potray's advice, I ended up checking the $route.path and the child path inside the template.
<template>
<div id="homeSubMenuWrapper">
<ul id="subMenuList">
<li v-for="menu in menuList">
<router-link :class="[$route.fullPath ==='/' && menu.path === '/in-theaters' ? 'menuItem router-link-active' : 'menuItem']" :to="menu.path">{{menu.name}}</router-link>
</li>
</ul>
</div>
</template>