#navigation-panel #javascript #vue 2017-05-01
If you’re vue developer this article will hit the spot. Beginner or not.
Today I’ll show how to make your own navigation component while attaining minimal duplication.
Here’s the code for code guys.
Take a look at the following code. That’s how your navigation goes normally.
<router-link :to="{name: user}">User</router-link>
<div>
<router-link :to="{name: settings}">Settings</router-link>
<router-link :to="{name: company}">Company</router-link>
<div>
<!-- ... -->
</div>
</div>
<!-- ... -->
const routes = [{
name: 'user',
path: 'user',
component: vmLayout,
children: [
{
name: 'settings',
path: 'settings',
component: vmSettings,
}, {
name: 'company',
path: 'company',
component: vmCompany,
children: [
// ...
]
},
// ...
]
}]
You might notice that template part is clearly abundant in many cases. Optimal navigation component might look like this:
<vm-navigation :routes="routes"/>
Let’s try doing just that.
We also want our navigation to be reactive and respond to route changes. For that I’d require full branch of routes
nested object/array (sorry for my french). Assume we landed on user/company
page. Given example above that would be an array like this:
[
routes, // DEPTH
user, // ||
userChildren, // ||
company, // \/
]
Going into details of how it’s done is out of scope of this article, so I got a helper class on the ready.
First let’s declare the regular stuff:
export default {
name: 'Navigation',
props: {
routes: {
type: Array,
required: true,
}
},
}
Next let’s prepare our routes to be used in template.
import ObjectHelpers from './Utility/ObjectHelpers.js'
// ...
computed: {
nestedParents () {
return ObjectHelpers
.traverseBranch(this.routes, { name: this.$route.name })
.filter(route => !Array.isArray(route))
.filter(route => route.children)
},
},
Our actions were:
Now for template:
<template>
<div>
<div class="navigation-block"
v-for="parent in nestedParents"
>
<router-link class="navigation-block__item"
v-for="route in parent.children"
:key="route.name"
:to="{name: route.name}"
v-text="composeName(route)"
/>
</div>
</div>
</template>
We should also compose name for route to define how the name would be displayed. I use route.meta.title
and then fall back to route.name
. But you might as well derive your own scheme, using vue-i18n
, for example.
methods: {
composeName (route){
if (route.meta && route.meta.title) {
return route.meta.title
}
return route.name
},
},
Toss in some styling and we’re mostly done.
Why not make breadcrumbs if all it takes is several lines of code?
<div class="breadcrumbs">
<router-link class="breadcrumbs__item"
v-for="route in nestedRoutes"
:key="route.name"
:to="{name: route.name}"
v-text="composeName(route)"
/>
</div>
computed: {
nestedRoutes () {
return ObjectHelpers
.traverseBranch(this.routes, { name: this.$route.name })
.filter(route => !Array.isArray(route))
},
nestedParents () {
return this.nestedRoutes.filter(route => route.children)
},
},
If you missed something check the complete component. You might also use it “as is” in case of harsh deadlines.
That’s it. Let me know if it works for you.