I have two navigation menu in my page structure, one is main header navigation and other one is in one of those main navigated page. I added transition onto both router views but child router view's transition doesn't work only parent. Let me explain with codes;
my routes:
const routes = [
{
path: '/',
component: () => import('layouts/MainLayout.vue'),
children: [
{ path: '', component: () => import('pages/Home.vue') },
{
path: '/features',
redirect: '/features/f1',
component: () => import('pages/Features'),
children: [
{ path: 'f1', component: () => import('pages/Feature1') },
{ path: 'f2', component: () => import('pages/Feature2') },
{ path: 'f3', component: () => import('pages/Feature3') },
{ path: 'f4', component: () => import('pages/Feature4') }
]
},
{ path: 'pricing', component: () => import('pages/Pricing')},
{ path: 'register', component: () => import('pages/Register')}
]
}
]
this is parent router view in main layout
<q-page-container class="l-background overflow-hidden">
<router-view ref="main" v-slot="{ Component, route }">
<transition name="fade-transform" mode="out-in">
<component :is="Component" :key="route.path" />
</transition>
</router-view>
</q-page-container>
and this is the component which includes child router view and its navigation:
<template>
<q-page class="flex flex-center">
<q-list class="custom-list">
<div class="relative-position full-height">
<feature-button v-for="(navi, index) in manufacturingNavi" :key="index" :nav="navi" buttonText="sample" />
</div>
</q-list>
<div class="row q-gutter-x-md justify-between full-width q-px-xs">
<div class="col-auto relative-position full-height self-center">
<p class="text-h4">What can I do<br/> with this app?</p>
</div>
<div class="col-8">
// must change only this part of page via router view but this transition doesn't work..
<router-view ref="feature" v-slot="{ Component, route }">
<transition name="fade-transform" mode="out-in">
<component :is="Component" :key="route.path" />
</transition>
</router-view>
</div>
</div>
</q-page>
</template>
child routes of features page work but not as expected. I expect that when I click one of child routes, only child route's component (that part of page) changes with its transition property. When child route clicked parent routes transition works (whole page changes). When I remove transition from child router view, nothing changes. It behaves like child router view doesn't exist. Where is my mistake?
I use vue 3 and vue router 4. Thank you.
I tried to explain by visualizing below.
Reason found. Using key attribute on parent router-view (in main layout in my case), always its transition props run. I removed key attributes from all router-views and problem solved.
Related
I have a ContainerMain component in which the RouterView is rendered as a simple default slot in a MainLayout Component.
// routes.ts
const mainMenuRoutes = [{
path: "blood-drop",
name: RouteNameMenu.MENU_BLOOD_DROP,
component: BloodDropMain,
children: [
{
path: "checkups",
name: RouteName.BLOOD_CHECKUPS,
component: CheckupResults,
}
],
}];
{
path: "/",
name: RouteName.MENU_MAIN,
component: ContainerMain,
children: mainMenuRoutes,
}
// ContainerMain.vue
<template>
<!-- <RouterView /> --> THIS WILL WORK !!!
<MainLayout> ---> THIS DOESN'T WORK !!!
<RouterView />
</MainLayout>
<div></div>
</template>
If I navigate to '/blood-drop' I get the BloodDrop Component rendered successfully. But when I navigate or refresh to '/blood-drop/checkups' I get an error.
// BloodDropMain.vue
<template>
<div class="blood-drop-container">
...etc
<div class="content">
<RouterView />
</div>
</div>
</template>
enter image description here
If however I move out the parent <RouterView /> outside of <MainLayout></MainLayout> it will work, no error, except of course not having the correct UI.
I am using router-view. In App.vue I want this structure so in every other pages I can have different header, footer but overall structure:
<router-view>
<template>
<div class="container">
<header>
<slot name="header"></slot>
</header>
<main>
<slot></slot>
</main>
<footer>
<slot name="footer"></slot>
</footer>
</div>
</template>
</router-view>
In home page component I want to do something like this:
<slot name="header">header content</slot>
<slot name="footer">footer content</slot>
Othercontent here....
In about us page component I want to do something like this:
<slot name="header">about header content</slot>
<slot name="footer">about footer content</slot>
Othercontent here....
how can I achive this?
The solution goes around layouts and sub-pages where layouts are parents and related pages placed as children.
Consider these two layouts:
// src/layouts/PublicLayout.vue
<template>
<section class="public-layout">
<router-view/>
</section>
</template>
<script>
export default {
name: 'PublicLayout',
};
</script>
and
// src/layouts/MainLayout.vue
<template>
<section class="main-layout">
<!-- header -->
<router-view/>
<!-- footer -->
</section>
</template>
<script>
export default {
name: 'MainLayout',
};
</script>
Now, you can tune routes in your flavor:
// src/router/index.js
import Vue from 'vue';
import VueRouter from 'vue-router';
const publicRoutes = [
{
path: '/public',
component: () => import('layouts/PublicLayout.vue'),
children: [
{
path: 'login',
name: 'Login',
component: () => import('pages/ULogin.vue'),
},
{
path: 'sign-up',
name: 'SignUp',
component: () => import('pages/USignUp.vue'),
},
],
},
];
const mainRoutes = [
{
path: '/',
component: () => import('layouts/MainLayout.vue'),
children: [
{
path: '',
name: 'Home',
component: () => import('pages/UHome.vue'),
},
{
path: 'profile',
name: 'Profile',
component: () => import('pages/Profile.vue'),
},
],
},
];
const Router = new VueRouter({
routes: mainRoutes.concat(publicRoutes),
});
// snippet skipped
Here, MainLayout and PublicLayout play the structural role while other pages are placed as their children. By navigating to each page (e.g. from /login to /profile) the proper layout will get loaded as well.
<your-base-layout>
<slot #header>about header content</slot>
<slot #footer>about footer content</slot>
</your-base-layout>
its called "Named Slot"
I would like to show a certain div for every child route of the route
So I thought simply adding something like this would work, but it does not:
<div v-if="$route.path == '/news/:child'">
</div>
So any route that is nested under /news/ still does not show that <div>
How can I display that div for every child route of the /news route?
So actually this will work if I set v-if="$route.path.includes('/news/')", thus div only shows in children routes (not in parent).
Use Vue Nested Routes:
const routes = [
{
path: '/news',
component: NewsComponent,
children: [
{
path: ':child',
component: ChildComponent,
},
]
}
]
// NewsComponent
<template>
<div>
My News Component
<router-view />
</div>
</template>
// ChildComponent
<template>
<div>
My Child Component
</div
</template>
Will render:
<div>
My News Component
<div>
My Child Component
</div>
</div>
i have a router called '/shop' and the children is /list/:id for the component named is listproduct
but when i render on the link i go like mylocalhost/shop/list/0812018381 it does render ?
here is my routes
{
path: '/shop',
name: 'shop',
component: () => import('./components/shop.vue'),
children: [
{
path: '/list/:id',
name: 'list',
component: () => import('./views/detail.vue'),
},
],
}
the component on my shop is something like this
<b-col>
<div class="thiscaption my-4 p-2">
<b-link :to="`/shop/${product._id}`">
<h4>{{ product.productName }}</h4>
</b-link>
<span>
{{
product.desc
.split(' ')
.slice(0, 8)
.join(' ')
}}...</span
>
<br />
<span>
<b>${{ product.price }} </b>
</span>
<br />
<b-button type="submit" variant="primary" class="p-2
my-4">add to cart</b-button>
</div>
</b-col>
i have tried to move that component list to not in children of shop, it was work, but when i use it on children shop, it doesnt render and work
If you use the children property of a route, any child routes are mounted in the parent component. In your case it means it is mounted in shop.vue. To be able to mount the component in the parent component, the parent component must contain a <router-view /> element.
Take for example the following components:
<!-- App.vue -->
<template>
<div id="app">
<span>Start App.vue</span>
<router-view/>
<span>End App.vue</span>
</div>
</template>
<!-- ParentComponent.vue -->
<template>
<div class="parent-component">
<span>Start Parent component</span>
<router-view/>
<span>End Parent component</span>
</div>
</template>
<!-- Child1.vue -->
<template>
<div class="comp-child1">Child1.vue</div>
</template>
Furthermore, we have the following router:
// router.js
import Vue from "vue";
import VueRouter from "vue-router";
import ParentComponent from "./components/ParentComponent";
import Child1 from "./components/Child1";
import Child2 from "./components/Child2";
Vue.use(VueRouter);
const routes = [
{
path: "/",
component: ParentComponent,
children: [
{
path: "",
redirect: "/child1"
},
{
path: "child1",
component: Child1
},
{
path: "child2",
component: Child2
}
]
}
];
export default new VueRouter({
routes
});
In this case, App.vue is the root component, because this is defined in main.js. The router says that child1 is a child route from /, which renders component ParentComponent. Therefore we will see the start and end of app.vue. Nested in there we will see the start and end of ParentComponent. Then nested inside of that, we see Child1. Without router-view in either of these components, there would be no place for their children to be mounted.
So basically I have something like this in my home page
<template>
<div id="body">
<sideBar :menu="menu" :tableName='tableName' titleOfPage='titleOfPage'></sideBar>
<div id="menu">
HOME PAGE
</div>
</div>
</template>
In the sidebar template you can select your page. It will forward you this template
<template>
<div id="body">
<sideBar :menu="menu" :tableName='tableName' :titleOfPage='titleOfPage'></sideBar>
<div id="main">
<h1>{{ titleOfPage }}</h1>
....
I need the sidebar template to set the titleOfPage as it corresponds to the page clicked. I tried different ways but didn't manage. I appreciate the help by explaining me how to solve this issue.
The Vue docs recommend not using $router directly but instead configuring props to pass to the routed components:
const routes = [
{ path: '/', redirect: '/page-a', component: { template: `<router-view></router-view>` }, name: 'Home' },
{ path: '/page-a', component: Page, props: { titleOfPage: 'Page A' }},
{ path: '/page-b', component: Page, props: { titleOfPage: 'Page B' }},
];
If your props are dynamic and depend on the current route, you can pass a function to the props property of each route configuration:
{ path: '...', component: ..., props: (route) => ({ query: route.query.q }) }
To solve your issue, you can simply create a page component which receives the props from the router and render your sidebar component in its template:
<!-- template for page component -->
<template>
<div id="body">
<sideBar :menu="menu" :tableName='tableName' :titleOfPage='titleOfPage'></sideBar>
<div id="main">
<h1>{{ titleOfPage }}</h1>
<!-- ... -->
</div>
</div>
</template>
DEMO: stackblitz