Named views with children in Vue? - vue.js

I have a single page with multiple <router-view/> shown at the same time using named views.
That works well, however I need to also add children to each view but can't figure out how to do that.
Here is more details on my layout.
The vue main component:
<template>
<div>
<router-view name="files"/>
<router-view name="forms"/>
<router-view name="tools"/>
</div>
</template>
...
Router:
let router = new Router({
path: ':resourceTab',
name: 'Resources',
components: {
files: FilesMain,
forms: FormsList,
tools: ToolsList,
},
props: true,
})
new Vue({
el: '#app',
router: router,
store,
render: h => h(App)
})
Here is what I'm looking to accomplish:
{
path: ':resourceTab',
name: 'Resources',
components: {
files: {component: FilesMain, children: [{path: file/:fileId}] <- Just made up this syntax to show the idea. How to accomplish something like this?
forms: FormsList,
tools: ToolsList,
},
props: true,
}
At the moment I do it by adding more paths like below, but that just creates redundancy and I have to pass the value for the "resourceTab" everytime:
{
path: 'files',
name: 'Resources',
components: {files: FilesMain},
children:[{path: file/:fileId}]
props () => {resourceTab: "files"},
},
{
path: 'forms',
name: 'Resources',
components: {forms: FormsList},
children:[{path: forms/:formId}]
props () => {resourceTab: "forms"},
}
...etc
Is there a better way of organising subviews in such a dynamic layout?

Related

How do you build page within a folder in Vue?

I'm trying to have my URL as /social/fb/, i've tried looking online and documentation but can't find the answer to this simple question for the life of me.
My folder structure is:
Pages
-social
--fb
I have a social.vue file in pages which works fine as www.example.com/social but can't get www.example.com/social/fb. Any direction would be much appreciated.
Create social folder inside pages, then create fb.vue inside social folder.
This should work
I normally load the layout.vue from the router and treat everything else as a child which is passed though as a router-view, this then saves having an index.vue for each parent.
But you would always want to make a directory to contain the social pages. Then would be a case of simply adding to the router.js file.
layouts/template.vue
<template>
<router-view></router-view>
</template>
<script>
export default {
name: 'layout-template'
}
</script>
router.js
...
/*
* Social
*/
{
path: '/social',
component: () => import('./layout/template.vue'),
props: true,
// rendered inside <router-view>
children: [{
path: '/',
component: () => import('./pages/social/index.vue')
}, {
path: 'fb',
component: () => import('./pages/social/fb.vue')
}, {
path: 'twitter',
component: () => import('./pages/social/twitter.vue')
},
// or do something more dynamic
{
path: ':network', // accessible in component though `$route.params.network`
props: true, // makes accessible though `props: ['network']`
component: () => import('./pages/social/network.vue')
}]
},
...
./pages/social/index.vue - could show something /social homepage or change route to import('./pages/not-found.vue') instead.
./pages/social/network.vue
<template>
...
</template>
<script>
export default {
name: "page-social-network",
props: {
network: {
type: String,
default: ''
}
},
created() {
// or through
this.$route.params.network
}
};
</script>
<style lang="scss" scoped></style>
See: https://router.vuejs.org/guide/essentials/passing-props.html#boolean-mode
Otherwise is just standard vue pages

Vue is there a way to pass data from route to component?

I am using Vue.js 2.0, and I have this exact same code in 2 differents files, so I decided to build only one component and redirect the 2 routes on it, then I just need to pass the ID to the route, this way my 2 components can display differents resultats.
Note: the only thing that changes is the ID dsgh151rhj12t1j5j I would like that each route can send it own ID to my PageContentfulView component
EDIT: I just want to pass data from Route to the component
<template>
<div>
<div class="container">
<hp-row>
<hp-column>
<component v-for="component in content.column" :data="component" :key="component.id" :is="getComponentIdentifier(component.is)"></component>
</hp-column>
</hp-row>
</div>
</div>
</template>
<script>
import ModularView from '#/views/ModularView'
export default {
name: 'PageContentfulView',
mixins: [ModularView],
created () {
this.fetch('blocks/dsgh151rhj12t1j5j')
},
}
</script>
Routes:
{
path: '/satisfaction-guarantee',
name: 'SatisfactionView',
component: load('PageContentfulView'),
},
{
path: '/about-us',
name: 'AboutUsView',
component: load('PageContentfulView'),
},
'props' can solve your problem.
<template>
<div>
<div class="container">
<hp-row>
<hp-column>
<component v-for="component in content.column" :data="component" :key="component.id" :is="getComponentIdentifier(component.is)"></component>
</hp-column>
</hp-row>
</div>
</div>
</template>
<script>
import ModularView from '#/views/ModularView'
export default {
name: 'PageContentfulView',
mixins: [ModularView],
props: ['id'],
created () {
this.fetch('blocks/' + this.id)
},
}
</script>
route
{
path: '/satisfaction-guarantee',
name: 'SatisfactionView',
component: load('PageContentfulView'),
props: { id: 'dsgh151rhj12t1j5j' },
},
{
path: '/about-us',
name: 'AboutUsView',
component: load('PageContentfulView'),
props: { id: 'blablablabla' },
},
You can do it using Dynamic Route Matching. You just need to include the parameter on the definition of the route, like below:
{
path: '/satisfaction-guarantee/:ID',
name: 'SatisfactionView',
component: load('PageContentfulView'),
}
Inside you component, to access the ID, you just need to use the this.$route.params.ID.
Passing props to route component allows an object mode.
Example from the documentation:
const router = new VueRouter({
routes: [
{ path: '/promotion/from-newsletter', component: Promotion, props: { newsletterPopup: false } }
]
})
Route meta fields
Also you always can use the meta property to pass extra data (like a requireAuth flag)
const router = new VueRouter({
routes: [
{ path: '/test', component: TestComponent, meta: { someBoolean: false } }
]
})
And you can access it within your component
created() {
let meta = this.$route.meta;
}
Set the props in your route to true and place /:id at the end of your path
const router = new VueRouter({
routes: [
{ path: '/promotion/from-newsletter/:id',
component: Promotion,
props: true
}
]
})
You can now extract the param id of your route from the props in the corresponding component
<template>
<div>
<div class="container">
<hp-row>
<hp-column>
<component v-for="component in content.column" :data="component" :key="component.id" :is="getComponentIdentifier(component.is)"></component>
</hp-column>
</hp-row>
</div>
</div>
</template>
<script>
import ModularView from '#/views/ModularView'
export default {
name: 'PageContentfulView',
mixins: [ModularView],
props: ['id'],
created () {
this.fetch('blocks/'+id)
},
}
</script>

Vue Router moves to route but loads wrong component

I have the following router config:
export default new Router({
mode: 'history',
routes: [
{
path: '/',
name: 'notselected',
component: PackageUnselected
},
{
path: '/package/:id',
children: [
{ path: 'meta', name: 'packageMeta', component: ViewPackageMeta },
{ path: 'readme', name: 'packageReadme', component: PackageReadme },
{ path: 'docs', name: 'packageDocs', component: PackageDocs },
{
path: 'playground',
name: 'packagePlayground',
component: PackagePlayground
}
]
},
{
path: '/about',
name: 'about',
component: About
},
{
path: '*',
redirect: '/'
}
]
});
And when I'm at the root route it correctly identifies the route name as notselected. When I route to any of the "/package/[id]" routes though it continues to load the PackageUnselected component instead of the appropriate route (aka, ViewPackageMeta, PackageDocs, etc.).
Now at the point in the DOM where I want the route to insert the route's component I have the following template:
<v-tab-item v-for="item in tabs" :id="'tab-item-' + item" :key="item" exact>
item: {{item}}
<router-view :selectedPackage="selected"></router-view>
</v-tab-item>
And because I have installed vuex-router-sync it's easy to see the route state at any given time. So when clicking on the route that should load PackageDocs:
But the component view window of vue-devtools looks like this:
the highlighted area shows that NO component has been loaded into the tabs. I then tried adding a component to the definition of the parent route /package/:id:
{
path: '/package/:id',
component: Packages,
children: [
{ path: 'meta', name: 'packageMeta', component: ViewPackageMeta },
{ path: 'readme', name: 'packageReadme', component: PackageReadme },
{ path: 'docs', name: 'packageDocs', component: PackageDocs },
{
path: 'playground',
name: 'packagePlayground',
component: PackagePlayground
}
]
},
I then had to create the world simplest component for Packages:
<template>
<view-router-view></view-router-view>
</template>
This results in the following:
Hmmm. Can't figure out what to do next. Anyone have any pointers?
When I route to any of the "/package/[id]" routes though it continues
to load the PackageUnselected component instead of the appropriate
route (aka, ViewPackageMeta, PackageDocs, etc.).
That is the correct behavior of the vue-router.
Children can only be loaded when URL paths are:
/package/[id]/meta
/package/[id]/readme
/package/[id]/playground
/package/[id]/docs
Parent path may not have component defined if you have configured redirect option and your user, who opens /package/[id] will be redirected to your default path (could be anything).
Lets move to the next part of your question...
<v-tab-item v-for="item in tabs" :id="'tab-item-' + item" :key="item" exact>
item: {{item}}
<router-view :selectedPackage="selected"></router-view>
</v-tab-item>
You don't need to create here 4 different <router-view> tags, you just need one where all your children components will display html code.
<v-tab-item v-for="item in tabs" :id="'tab-item-' + item" :key="item" exact></v-tab-item>
<router-view></router-view>
Now you will have only one router-view and it's the default one. When user clicks on any of your tabs you just need to this.$router.push a new path on #click-event in Packages component. That's it.
I have created a simple example (codepen) to demonstrate how this task can be solved:
Vue.use(VueRouter);
// Components
let MetaPackages = {
mounted() { console.log('Mounted MetaPackages...'); },
template: `<div>MetaPackages...</div>`,
};
let DocsPackages = {
mounted() { console.log('Mounted DocsPackages...'); },
template: `<div>DocsPackages...</div>`,
};
let ReadmePackages = {
mounted() { console.log('Mounted ReadmePackages...'); },
template: `<div>ReadmePackages...</div>`,
};
let Packages = {
mounted() { console.log('Mounted Packages... ' + this.$route.path); },
template: '<div>Packages (parent) screen...<br/><router-view></router-view></div>',
};
// Router
const router = new VueRouter({
mode: 'hash',
routes: [
{
path: "/packages/:id",
component: Packages,
children: [
{path:"meta", component: MetaPackages},
{path:"docs", component: DocsPackages},
{path:"readme", component: ReadmePackages}
]
}
]
});
// Vue instance
const vm = new Vue({
el: '#app',
router,
components: {Packages, MetaPackages, DocsPackages, ReadmePackages}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue-router/3.0.2/vue-router.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<router-link to="/packages/100/meta">Meta</router-link>
<router-link to="/packages/100/docs">Docs</router-link>
<router-link to="/packages/100/readme">Readme</router-link>
<hr/>
<router-view></router-view>
</div>

Navigating to Child Components with Vue.js

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.

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.