how to defime routing for multiple subviews - durandal

I am developing and application using durandal. I have two sections in my page with different navigation menu. I am using composition to keep make these subviews. But I am not sure how to define routes for these sections.

In your shell.js
{ route: 'work*details', moduleId: 'profile/index', title: 'Profil', nav: 1, hash: '#profil', authorize: ["User", "Administrator"] },
in the subfolder index.js
var childRouter = router.createChildRouter()
.makeRelative({
moduleId: 'viewmodels/marketplace',
fromParent: true
}).map(userRoute[
// put you sub nav here
]).buildNavigationModel();
return {
router: childRouter,
work: ko.computed(function () {
return ko.utils.arrayFilter(childRouter.navigationModel(), function (route) {
return route.type == 'intro';
});
})
};
the markup
<ul class="nav navbar-nav">
<!--ko foreach: work-->
<li data-bind="css: { active: isActive }">
<a data-bind="attr: { href: hash }, text: title"></a>
</li>
<!--/ko-->
</ul>
find out more here.

Related

Children of NuxtLink rendered twice (hydration error?)

My hunch is that there is some hydration mismatch where the FontAwesomeIcon was not rendered on the server (only the span) and then on the client both child nodes of the NuxtLink were rendered (the svg and the span), prompting Nuxt to render the span twice.
The console does not return an error, though.
Any thoughts on how to debug this?
This is the Vue component:
<template>
<ul v-if="routes.length > 0" class="col-span-2 flex flex-col">
<li v-for="(item, i) in routes" :key="item.name">
<NuxtLink :to="item.path" target="_blank">
<FontAwesomeIcon :icon="item.icon" class="mr-3" fixed-width />
<span>{{ item.title }}</span>
</NuxtLink>
</li>
</ul>
</template>
<script lang="ts">
export default defineComponent({
props: {
links: {
type: Array,
default: () => ["instagram", "facebook", "email"],
},
},
computed: {
routes() {
return [
{
name: "instagram",
path: "https://www.instagram.com/insta.name/",
title: "Instagram",
icon: ["fab", "instagram"],
},
{
name: "facebook",
path: "https://www.facebook.com/fb.name",
title: "Facebook",
icon: ["fab", "facebook"],
},
{
name: "email",
path: "mailto:hello#example.com",
title: "Email",
icon: ["fas", "envelope"],
},
].filter((e) => this.links.includes(e.name));
},
},
});
</script>

Vue js toogle the items in the list

I wanted to create a side menu bar. I have added a class show when the li should be shown.
But in my case what happen is when one item is shown(1st) and next(2nd) item is clicked, the 1st get collapsed(which is fine) but the 2nd do not show up immediately.
new Vue({
el: '#app',
methods: {
setActiveItemId(itemIndex) {
this.activeItemId = itemIndex
this.isActive = !this.isActive
}
},
data: {
message: 'Hello Vue.js!',
isActive: false,
activeItemId: '',
sideBar: [{
name: "Dashboard",
url: "/dashboard",
icon: "ti-world",
children: [{
name: "Buttons",
url: "/components/buttons",
icon: "fa-book",
},
{
name: "Social Buttons",
url: "/components/social-buttons",
icon: "icon-puzzle",
}
]
},
{
name: "Components",
url: "/components",
icon: "ti-pencil-alt",
children: [{
name: "Buttons",
url: "/components/buttons",
icon: "fa-book",
},
{
name: "Social Buttons",
url: "/components/social-buttons",
icon: "icon-puzzle",
}
]
}
]
}
})
.collapse.show {
display: block;
}
.collapse {
display: none;
}
.list-unstyled {
padding-left: 0;
list-style: none;
}
.collapse.list-unstyled {
padding-left: 15px;
}
<script src="https://unpkg.com/vue"></script>
<div id="app">
<ul class="list-unstyled">
<li>
<a>
<i class="ti-home"></i>Home</a>
</li>
<li v-for="(x, itemIndex) in sideBar" :key="itemIndex">
<a #click="setActiveItemId(itemIndex)">
<i class="fa" :class="x.icon"></i>{{x.name}}
</a>
<ul :id="x.id" class="collapse list-unstyled" :class="{'show':activeItemId === itemIndex && isActive}">
<li v-for="y in x.children" :key="y.id">
<a>{{y.name}}</a>
</li>
</ul>
</li>
</ul>
</div>
So, How can i collapse the 1st and display the 2nd immediately, when 2nd item is clicked?
Fiddle
The problem is that inside your setActiveItemId method you are always toggling the isActive state, regardless of which item is being activated. That means that it toggles the same item, but when jumping to another you'll have to click twice. I'd take a different approach, where isActive is a computed property instead of residing in the data.
// ...
methods: {
setActiveItemId(itemIndex) {
// If item is currently selected, toggle
if (itemIndex === this.activeItemId) {
this.activeItemId = ''
return
}
this.activeItemId = itemIndex
}
},
computed: {
isActive () {
return this.activeItemId !== ''
}
}
Here's the updated fiddle:
https://jsfiddle.net/2ytuL46c/3/
Unrelated but worth noting: remember that your data must be a function that returns the data object, and not an object itself.

active class handle for parent/child routers in aurelia

Am new to aurelia, i have a main menu in left side, one of the menu(mail) is having sub-menus (inbox,sent,trash). i need to do if the sub menu is active(#current URL, #activeclass, #CSS ) needs to keep active class for the parent menu (mail).
app.js
export class App {
configureRouter(config, router){
config.title = 'DMS';
config.map([
{ route: ['dashboard',''], name: 'Dashboard',
moduleId: './templates/dashboard/dashboard', nav: true, title:'Dashboard',settings:{'img' : 'ic-dashboard.png'} },
{ route: ['settings'], name: 'Settings',
moduleId: './templates/settings/settings', nav: true, title:'Settings' ,settings:{'icon' : 'settings'} },
{ route: ['inbox'], name: 'inbox',
moduleId: './templates/mail/inbox/inbox', nav: true, title:'Mail' ,settings:{'img' : 'mail.png'} },
{ route: ['inbox/trash'], name: 'trash',
moduleId: './templates/mail/trash/trash', title:'Mail' },
{ route: ['inbox/sent'], name: 'sent',
moduleId: './templates/mail/sent/sent', title:'Mail'},
]);
this.router = router;
}
}
Menu list also applying active class
<div class="row col s12 ${row.isActive ? 'active' : ''}" repeat.for = "row of router.navigation" >
<a href.bind = "row.href">
<div class="col s2 " >
<div if.bind="row.settings.img">
<img src="src/assets/${row.settings.img}">
</div>
<div if.bind="row.settings.icon">
<i class="tiny material-icons">${row.settings.icon}</i>
</div>
</div>
</a>
</div>
sub-menu url
<div class="col s8 offset-s2 mail_actionLst">
<ul>
<li class="inbox mail_active"> Inbox <span>(43)</span> </li>
<li class="sent">Sent </li>
<li class="trash">Trash</li>
</ul>
</div>
How to set parent class active.
Because these are different routes, not parent + children.
You should describe child route in parent class.
You can add code to inbox.js:
configureRouter(config, router){
config.map([
{ route: ['trash'], name: 'trash',
moduleId: 'path_to_trash', title:'Mail' },
{ route: ['sent'], name: 'sent',
moduleId: 'path_to_sent', title:'Mail'},
]);
this.router = router;
}
Don't forget to add
<router-view></router-view>
to inbox.html.

Nested subroutes using vue-router

Im using Vue.js with Vue-router. I'm trying to create a list of components, each of which shows a subroute.
Example of What I am trying to do:
<ul>
<li>first
<ul><li>nested sub</li></ul>
</li>
<li>second
<ul><li>nested sub</li></ul>
</li>
<li>third
<ul><li>nested sub</li></ul>
</li>
</ul>
I am only able to get the nested subroute to appear in the first component. The rest of my components rendered in the v-for loop have no subroutes appearing.
Here is a fiddle of my code, showing the problem: https://jsfiddle.net/retrogradeMT/73zza1ah/
HTML:
<div id="page-content">
<router-view></router-view> <!-- renders campaign block list -->
</div>
<template id="campaignBlock" >
<ul>
<li v-for="campaign in campaigns">{{ campaign.name }}
<router-view> </router-view> <!-- should show subroute -->
</li>
</ul>
</template>
<template id="subroute">
<ul>
<li>Nested View</li>
</ul>
</template>
JS:
Vue.use(VueRouter)
Vue.component('app-page', {
template: '#campaignBlock',
data: function() {
return{
campaigns: []
}
},
ready: function () {
this.fetchCampaigns();
},
methods: {
fetchCampaigns: function(){
var campaigns = [
{
id: 1,
name: 'First List item'
},
{
id: 2,
name: 'Second List item'
},
{
id: 3,
name: 'Third List item'
},
];
this.$set('campaigns', campaigns);
}
}
})
Vue.component('subroute', {
template: '#subroute'
})
var App = Vue.extend({})
var router = new VueRouter()
router.map({
'/': {
component: Vue.component('app-page'),
subRoutes: {
'/': {
component: Vue.component('subroute')
},
}
}
})
router.start(App, '#page-content')
There can only be one single nested and identical router-view inside another. No way around it. For different routes, you can have multiple instances of router-view.
What you can do is something like this:
<li v-for="campaign in campaigns">{{ campaign.name }}
<subroute></subroute>
</li>

Durandal 2.0 routing parameterized routes (same route different parameter)

I am probably missing something basic, but when building navigation I am attempting to define multiple parameterized routes in the shell. The idea is that all of these routes will pass the user through to the same view/vm, but the parameter will determine content that is displayed after an ajax call). The routing itself works well, but the title is always passed through from the first route in the list.
activate: function () {
router.makeRelative({moduleId: 'viewmodels'}).map([
{
route: 'grid/:page',
title: 'Title 1',
moduleId: 'grid',
nav: 3,
hash: '#grid/param1'
},
{
route: 'grid/:page',
title: 'Title 2',
moduleId: 'grid',
nav: 2,
hash: '#grid/param2'
},
{
route: 'grid/:page',
title: 'Title 3',
moduleId: 'grid',
nav: 4,
hash: '#grid/param3'
},
{
route: 'grid/:page',
title: 'Title 4',
moduleId: 'grid',
nav: 5,
hash: '#grid/param4'
}
]).buildNavigationModel();
};
So, regardless of which of the generated links a user clicks on, the Title is always returned as 'Title 1'. Nav order does not matter. The first physical object in the list will supply the title for all links. If I hard code the links into shell.html and use a splat in the router I do not have this problem, however, hard coding the links is not feasible nor desirable for the app.
So, the question is, what am I doing wrong?
In the code above there's truly only one route 'grid/:page'. By defining a parameterized route the router maps everything the match grid/:page to the first route.
See more about that in the router documentation http://durandaljs.com/documentation/Using-The-Router/.
Instead of using the router.navigationModel() build your own small navigation model.
Top level down approach:
Step 1 Defining a grid route with an optional parameter (/:page).
router
.makeRelative({moduleId: 'viewmodels'})
.map([
{
route: 'grid(/:page)',
title: 'grid page',
moduleId: 'grid',
hash: '#grid'
}
])
.buildNavigationModel();
Step 2 Navigation model
define(['plugins/router', 'knockout', './navItem'], function( router, ko, NavItem ) {
var ctor = function(){
this.childRouter = router;
this.param = ko.observable('');
this.navigation = ko.observableArray([
new NavItem({title: 'Title 1', param: 'param1'}),
new NavItem({title: 'Title 2', param: 'param2'}),
new NavItem({title: 'Title 3', param: 'param3'}),
new NavItem({title: 'Title 4', param: 'param4'})
]);
};
ctor.prototype.activate = function(param){
this.param('Param: ' + (param || 'no param!'));
};
return ctor;
});
Step 3 Navigation item model
define(['plugins/router'], function( router ) {
var ctor = function(options){
this._options = options || {};
this.init(this._options)
};
ctor.prototype.init = function(options){
this.title = options.title;
this.param = options.param;
this.hash = '#extras/optional/' + this.param;
};
ctor.prototype.isActive = function(){
return router.activeInstruction().fragment === this.hash.replace('#', '');
};
return ctor;
});
Step 4 Navigation view
<div class="navbar">
<div class="navbar-inner">
<ul class="nav" data-bind="foreach: navigation">
<li data-bind="css: { active: isActive() }">
<a data-bind="attr: { href: hash }, html: title"></a>
</li>
</ul>
<div class="loader pull-right" data-bind="css: { active: childRouter.isNavigating }">
<i class="icon-spinner icon-2x icon-spin"></i>
</div>
</div>
<div>
<h3 data-bind="text: param"></h3>
</div>
</div>
Live version can be seen at: http://dfiddle.github.io/dFiddle-2.0/#extras/optional.
Feel free to fork and adjust to your liking.