Vue children Router: url changed but display parent component - javascript

I am learning Vue and stuck with nested router, I define some children router in the routes,but when I visit the child route it still display the parent component, my code is as following:
App.vue:
<template>
<div id="app">
<img src="./assets/logo.png">
<router-link :to="{name: 'Home'}">Home</router-link>
<router-link to="/cart">Cart</router-link>
<router-link to="/admin">Admin</router-link>
<router-link to="/admin/add">【Admin Add】</router-link>
<router-link to="/admin/edit">Admin Edit</router-link>
<router-view/>
</div>
</template>
<script>
export default {
name: 'app'
}
</script>
<style>
#app {
font-family: 'Avenir', Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
</style>
Router/index.js:
import Vue from 'vue'
import Router from 'vue-router'
import Home from '#/components/pages/Home'
import Cart from '#/components/pages/Cart'
import Index from '#/components/pages/Admin/Index'
import Add from '#/components/pages/Admin/Add'
import Edit from '#/components/pages/Edit'
Vue.use(Router)
export default new Router({
routes: [
{
path: '/',
name: 'Home',
component: Home
},
{
path: '/cart',
name: 'Cart',
component: Cart
},
// {
// path: '/admin/index',
// name: 'Index',
// component: Index
// },
// {
// path: '/admin/add',
// name: 'Add',
// component: Add
// },
// {
// path: '/admin/edit',
// name: 'Edit',
// component: Edit
// }
{
path: '/admin',
// name: 'Admin',
component: Index,
children: [
{
path: 'add',
name: 'Add',
component: Add
},
{
path: 'edit',
name: 'Edit',
component: Edit
}
]
}
]
})
I tried not to use the children router, it will display the component correctly, just as the code which is commented.
I am so confused with this, please help me.

In your Index component you will need to add <router-view></router-view>. Check out this working example from the vue-router docs.
const User = {
template: `
<div class="user">
<h2>User {{ $route.params.id }}</h2>
<router-view></router-view>
</div>
`
}
const UserHome = { template: '<div>Home</div>' }
const UserProfile = { template: '<div>Profile</div>' }
const UserPosts = { template: '<div>Posts</div>' }
const router = new VueRouter({
routes: [
{ path: '/user/:id', component: User,
children: [
// UserHome will be rendered inside User's <router-view>
// when /user/:id is matched
{ path: '', component: UserHome },
// UserProfile will be rendered inside User's <router-view>
// when /user/:id/profile is matched
{ path: 'profile', component: UserProfile },
// UserPosts will be rendered inside User's <router-view>
// when /user/:id/posts is matched
{ path: 'posts', component: UserPosts }
]
}
]
})
const app = new Vue({ router }).$mount('#app')
<script src="https://unpkg.com/vue/dist/vue.js"></script>
<script src="https://unpkg.com/vue-router/dist/vue-router.js"></script>
<div id="app">
<p>
<router-link to="/user/foo">/user/foo</router-link>
<router-link to="/user/foo/profile">/user/foo/profile</router-link>
<router-link to="/user/foo/posts">/user/foo/posts</router-link>
</p>
<router-view></router-view>
</div>

Related

Why is my Vue navbar changing the route but not updating the router-view?

I'm using Vue to make a single-page application and I have a navbar and have set up Vue-Router. For some reason, every time after the first time I use the navbar, the route changes but the router-view does not. Here's the code from NavBar.vue:
<template>
<div id="app">
<v-toolbar id="navbar" app color="#330066" dark>
<v-toolbar-side-icon></v-toolbar-side-icon>
<v-toolbar-title id="appname">{{ appname }}</v-toolbar-title>
<v-spacer></v-spacer>
<span :key="item.link" v-for="item in items" class="nav-elt">
<router-link active-class="nav-elt-active" tag="span" :to="item.link">
{{ item.title }}
</router-link>
</span>
</v-toolbar>
</div>
</template>
<script>
export default {
name: "NavBar",
props: {
appname: String,
},
data() {
return {
items: [
{ title: "Home", link: "/" },
{ title: "Search", link: "/search" },
],
};
},
};
</script>
And here's from App.vue:
<template>
<v-app>
<v-main>
<NavBar appname="Newsfacts" />
<router-view />
</v-main>
</v-app>
</template>
<script>
import NavBar from "./components/NavBar";
export default {
name: "App",
components: {
NavBar,
},
data() {
return {};
},
};
</script>
Here's from router/index.js:
import Vue from 'vue';
import Router from 'vue-router';
import Home from '../views/Home.vue';
import Search from '../views/Search.vue';
Vue.use(Router);
export default new Router({
mode: "history",
routes: [
{
path: "/",
name: "Home",
component: Home
},
{
path: "/search",
name: "Search",
component: Search
}
]
});
If you need anything else, the full code is on my github, and a demo is at a netlify site
The link attribute inside the array of items should contain a '/' at the beginning of every route.
So, it should look like this:
data() {
return {
items: [
{ title: "Home", link: "/home" },
{ title: "Search", link: "/search" },
],
};
},
Also, it is probable that the component is not being rendered because you are using <v-btn/> for routing, instead of <router-link/>.
I recommend you use <router-link/> instead of <v-btn/> for navigation in order to support all the features provided by Vue Router like history mode, base, etc.
In case you necessarily need the v-btn, I think you can wrap the <router-link/> inside the button or viceversa.
For more information about Vue Router and <router-link/>, check out this link:
https://router.vuejs.org/api/#router-link
where is your vue router component definitions?
e.g
const FooHome= { template: '<div>Home</div>' }
const Search= { template: '<div>Search</div>' }
const routes = [
{ path: '/', component: Home},
{ path: '/search', component: Search}
]
const router = new VueRouter({
routes
})
const app = new Vue({
router
}).$mount('#app')
Turns out I was returning an empty dictionary in a few data() functions, and that caused the app to break. Thanks to #inked6233 on the Vue Land discord for helping me find that!

My children paths in vue refuse to render their view when resolved, they are rendering the parent view

I've tried redoing the whole process, refactoring the code a bit (I'm still rather new to vue so some of my code is a bit sloppy) but nothing seems to render the component for the child path. It always resolves to the parent path.
The code in my products.js (all paths under products)
import Products from '../views/Products.vue'
import Product from '../views/Product.vue'
export default {
path: '/products',
name: 'Products',
component: Products,
children: [
{
path: ':id',
name: 'OneProduct',
component: Product
}
]
}
The code in my product.view (the view for the path: /products/:id)
<template>
<div>
<ExpandedProduct
:prodNmae="$route.query.productName"
:price="$route.query.prodPrice"
:description="$route.query.description"
/>
</div>
</template>
<script>
import ExpandedProduct from '../components/ExpandedProduct'
export default {
name: 'Product',
components: {
ExpandedProduct
}
}
</script>
<style lang="scss" scoped>
</style>
the ExpandedProduct component is the component which is supposed to be rendered when the route resolves to '/products/:id'
ExpandedProduct.vue
<template>
<div>
<div class="carousel-holder">
<v-carousel>
<v-carousel-item
v-for="(item,i) in items"
:key="i"
:src="item.src"
reverse-transition="fade-transition"
transition="fade-transition"
></v-carousel-item>
</v-carousel>
</div>
<div class="description-holder">
<h2>{{ $route.query }}</h2>
<h4>{{ $route.query.prodPrice }}</h4>
<h3>{{ $route.query.description }}</h3>
</div>
</div>
</template>
<script>
export default {
name: 'ExpandedProduct',
props: {
prodName: {
type: String,
required: true,
default: 'N/A'
},
price: {
type: String,
required: true,
default: 'N/A'
},
description: {
type: String,
required: true,
default: 'N/A'
}
},
data () {
return {
items: [
{ src: 'https://cdn.vuetifyjs.com/images/carousel/squirrel.jpg' },
{ src: 'https://cdn.vuetifyjs.com/images/carousel/sky.jpg' },
{ src: 'https://cdn.vuetifyjs.com/images/carousel/bird.jpg' },
{ src: 'https://cdn.vuetifyjs.com/images/carousel/planet.jpg' }
]
}
}
}
</script>
<style lang="scss" scoped>
</style>
If someone could explain to me what I'm missing
Creating a /product route and a separate /product/:id route is not the same as adding children with :id to a parent /product route.
The former creates a page without the :id parameter and a page that handles the :id parameter. The latter creates ONE page and a child router-view gets the :id parameter.
I think it's better explained with snippets:
1. This is the /product + /product/:id
const Foo = {
template: `
<div>Foo</div>
`
}
const FooId = {
template: `
<div>Foo+{{ $route.params.id }}</div>
`
}
const Bar = {
template: `
<div>Bar</div>
`
}
const Baz = {
template: `
<div>Baz</div>
`
}
const routes = [{
path: '/foo',
component: Foo
},
{
path: '/foo/:id',
component: FooId
},
{
path: '/bar',
component: Bar
},
{
path: '/baz',
component: Baz
}
]
const router = new VueRouter({
routes
})
new Vue({
router,
el: "#app",
})
div {
border: 1px solid black;
background: rgba(0, 0, 0, 0.2);
padding: 8px 16px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<script src="https://unpkg.com/vue-router/dist/vue-router.js"></script>
<div id="app">
<h3>APP</h3>
<p>
<router-link to="/foo">Go to Foo</router-link><br />
<router-link to="/foo/1">Go to Foo-1</router-link><br />
<router-link to="/foo/2">Go to Foo-2</router-link><br />
<router-link to="/bar">Go to Bar</router-link><br />
<router-link to="/baz">Go to Baz</router-link><br />
</p>
<h3>ROUTES:</h3>
<router-view></router-view>
</div>
2. This is the /product parent + :id children
const Foo = {
template: `
<div>
Foo
<router-view></router-view>
</div>
`
}
const FooId = {
template: `
<div>Foo+{{ $route.params.id }}</div>
`
}
const Bar = {
template: `
<div>Bar</div>
`
}
const Baz = {
template: `
<div>Baz</div>
`
}
const routes = [{
path: '/foo',
component: Foo,
children: [{
path: ':id',
component: FooId
}]
},
{
path: '/bar',
component: Bar
},
{
path: '/baz',
component: Baz
}
]
const router = new VueRouter({
routes
})
new Vue({
router,
el: "#app",
})
div {
border: 1px solid black;
background: rgba(0, 0, 0, 0.2);
padding: 8px 16px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<script src="https://unpkg.com/vue-router/dist/vue-router.js"></script>
<div id="app">
<h3>APP</h3>
<p>
<router-link to="/foo">Go to Foo</router-link><br />
<router-link to="/foo/1">Go to Foo-1</router-link><br />
<router-link to="/foo/2">Go to Foo-2</router-link><br />
<router-link to="/bar">Go to Bar</router-link><br />
<router-link to="/baz">Go to Baz</router-link><br />
</p>
<h3>ROUTES:</h3>
<router-view></router-view>
</div>
3. Difference
The other name for children routes is nested routes. That means that a route is treated as root, and there's another router-view inside it (Ok, this is only for concept-explanation). (Nested routes)
So, the question is: do you want separate pages for products and each product (v1 - two separate routes), or you want a parent page for all your products and control the subsection of THAT page for each product (v2 - nested routes).
EDIT
Added a bit of CSS, so it's more understandable that what's inside what.

VueJS Router-link not linking to the view

I have a problem with the router link. When I put it in a view, it works very well, I have a clickable link. But when I put a router link in a component, built into a view, then the router link no longer works: I would like "just" to link to the detail of a project.
This work (resources/js/views/HomepageView.vue)
<router-link :to="{ name: 'projects.show', params: { slug: slideshowProject.slug }}">
<a href="#" class="btn-secondary">See
Campaign</a>
</router-link>
This doesn't work (resources/js/components/UserProject.vue)
<router-link :to="{ name: 'projects.show', params: { slug: project.slug }}">
<h4>{‌{ project.title}}</h4>
</router-link>
Script part of the page :
<script>
export default {
name: "user-projects",
data() {
return {
projects: null
}
},
mounted() {
this.getUserProject()
},
methods: {
getUserProject() {
this.$store.dispatch('getUserProjects', {
limit: 2
}).then(response => {
this.projects = response.data.data;
})
}
},
}
</script>
My router.js
import Homepage from '../views/frontend/HomepageView';
import ProjectDetails from '../views/frontend/ProjectDetailsView';
import LoginView from '../views/frontend/LoginView';
import RegisterView from '../views/frontend/RegisterView';
import DashboardIndexView from '../views/dashboard/DashboardIndexView';
export const routes = [
{
path: '',
name: 'homepage',
component: Homepage
},
{
path: '/login',
name: 'frontend-login',
component: LoginView
},
{
path: '/projects/:slug',
name: 'projects.show',
component: ProjectDetails
},
{
path: '/register',
name: 'frontend-register',
component: RegisterView
},
{
path: '/dashboard',
name: 'dashboard-index',
component: DashboardIndexView
}
];
I don't understand where is my mistake :/
You can check slug is not empty before render a router link, like below sample:
<router-link v-if="project.slug" to="{ name: 'projects.show', params: { slug: project.slug }}">
<h4>{‌{ project.title}}</h4>
</router-link>
But I am sure, why slug is empty.
Thanks to Jom, the problem was that my "slug" was empty, and indeed when i look at the console, the warning was there.
[vue-router] missing param for named route "projects.show": Expected "slug" to be defined

How to pass app data to a VueRouter component

I am using VueRouter to load templates depending on the URL. When I try to use a property defined in app.data in the components, I receive a [VueWarn] Property or method "role" is not defined.
How can I pass every data property to the child components?
This is my script:
const Home = { template: '<p>home page, {{role}}</p>' }
const NotFound = { template: '<p>Page not found</p>' }
const routes = [
{path: '/', component: Home}
{path: '*', component: NotFound}
]
Vue.use(VueRouter)
var app = new Vue({
el: '#app',
data: {
role: 0,
cookiesAlert: true
},
router: new VueRouter({routes})
})
I think your router construction options are not set properly. Usually, I prefer to use 'named routes', therefore what I will set is:
const router = new VueRouter({
routes: [
{ name: 'role', path: '/:role', component: User },
{ path: '*', component: Home }
]
})
And if I don't understand your question wrong, what you want to do is pass the 'data' in parent components to child components via url and read the data in child component?
const User = {
template: '<div><br/>Role read from url is {{ $route.params.role }}</div>'
}
const Home = {
template: '<div><br/>Welcome</div>'
}
const router = new VueRouter({
routes: [
{ name: 'role', path: '/:role', component: User },
{ path: '*', component: Home }
]
})
new Vue({
router,
el: '#app',
data: {
role: 'default role'
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue-router/3.0.2/vue-router.min.js"></script>
<div id="app">
The role is (Please change the 'role' value and click on '/User' link): <input v-model="role" type="text" /> <br/>
<router-link :to="{ name: 'role', params: { role: role }}">User</router-link>
<router-link to="/">Home</router-link>
<router-view></router-view>
</div>
Or if you don't want to use $route.params.role, what I will do is to set the 'props' in router config options to be true.
When props is set to true, the route.params will be set as the component props.
const router = new VueRouter({
routes: [
{ name: 'role', path: '/:role', component: User, props: true },
{ path: '*', component: Home }
]
})
And bind to the 'props' inside child components.
const User = {
props: ['role'],
template: '<div><br/>Role read from url is {{ role }}</div>'
}

Vue.js nested routing

My Current Route is
function route(path, view) {
return {
path: path,
meta: meta[path],
component: resolve => import(`pages/${view}View.vue`).then(resolve)
}
}
route('/', 'Home'),
route('/help', 'Help),
route('/blog', 'BlogList'),
route('/blog/:slug', 'BlogDetails'),
Now Everything is working fine. but when I visit /blog/:slug route and from that component when I click back to /help from button Route pattern is /blog/help not the /help.
See my jsfiddle Vuejs route , I mean this help you.
const Home = {
template: '<h1>Home</h1>'
};
const Help = {
template: '<h1>Help</h1>'
};
const Profile = {
template: '<h1>Profile</h1>'
};
const User = {
template: '<h1>User</h1>'
};
routes = [
{path: '/', component: Home},
{path: '/help', component: Help},
{path: '/user', component: User},
{path: '/user/:id', component: {
render(h) {
return h(
'h1',
{ style: {color: 'skyblue'} },
`User id: ${this.$route.params.id}`
);
}
}}
];
const router = new VueRouter({
routes
});
new Vue({
router
}).$mount('#app');
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<script src="https://unpkg.com/vue-router/dist/vue-router.js"></script>
<div id="app">
<router-link :to='{path: "/"}'>/</router-link> <br>
<router-link :to='{path: "/help"}'>/help</router-link> <br>
<router-link :to='{path: "/user"}'>/user</router-link> <br>
<router-link :to='{path: "/user/userid"}'>/user/userID</router-link>
{{$route.path}}
<router-view></router-view>
<br>
<button #click='$router.go(-1)'>go back</button>
<button #click='$router.go(1)'>go forward</button>
</div>

Categories

Resources