once again a Durandal question ^^ I'd like to make a subnavigation menu.
The subpage is called per url
<a class="btn btn-primary" data-bind="attr: { href: '#customer/'+ id }">Open</a>
The shell.js routes this to the customers/shell2.js (the child router) that you can see here:
define(['plugins/router', 'knockout', 'utility', 'logger', 'datacontext'], function (router, ko, utility, logger, datacontext) {
var displayName = 'Customer';
var cId = ko.observable(0);
var childRouter = router.createChildRouter()
.makeRelative({ moduleId: 'viewmodels/customers' , route: 'customer/:id/'});
return {
router: childRouter,
cId: cId,
displayName: displayName,
activate: activate
};
function activate() {
cId(parseInt(router.activeInstruction().params[0]));
childRouter.map([
{
route: ['', 'overview'],
moduleId: 'overview',
name: 'Overview',
hash: '#customer/' + cId() + '/overview',
nav: false,
settings: {}
},
{
route: 'sessions',
moduleId: 'sessions',
name: 'Sitzungen',
hash: '#customer/' + cId() + '/sessions',
nav: false,
settings: {}
}
]).buildNavigationModel();
}
});
In short: I want the link to be like #customer/17/overview with 17 being the route parameter for calling the subnavigation.
When i call the url #customer/17, the content of the route is displayed, but not the subnavigation. Moreover the urls #customer/17/overview and #customer/17/session don't work...
Has someone a working example where the subnav is built with parameters?
EDIT:
shell.js
define(['plugins/router', 'knockout' ,'session', 'toastr','logger', 'durandal/app']
, function (router, ko, session, toastr, logger, app) {
var routes= [
{ route: '',
moduleId: 'login',
name: 'Login',
hash: '',
nav: false,
settings: {}
},
{ route: 'dashboard',
moduleId: 'dashboard/dashboard',
name: 'Dashboard',
hash: '#dashboard',
nav: true,
settings: {}
},
{ route: 'customers',
moduleId: 'customers/customers',
name: 'Customers',
hash: '#customers',
nav: true,
settings: {}
},
{ route: 'customer/:id',
moduleId: 'customers/shell2',
name: 'customer',
hash: '',
nav: false,
settings: {}
},
{ route: 'logout',
moduleId: 'logout',
name: 'Logout',
hash: '',
nav: true,
settings: {}
}];
return {
activate: activate,
router: router,
session: session,
app: app
};
function activate() {
router.makeRelative({moduleId:'viewmodels'})
.map(routes)
.buildNavigationModel() // Finds all nav routes and makes them ready
.mapUnknownRoutes('notfound', 'notfound');
router.activate();
}
});
I guess the problem was really in the parent shell.js. I changed the route to the child shell this way:
{
// add *details for splat route, even when there is nothing named 'details'
route: 'customer/:id*details',
moduleId: 'customer/shell2',
name: 'Customer',
nav: true
}
The childrouter shell2.js looks like this:
define(['plugins/router', 'knockout'], function (router, ko) {
var displayName = 'Customer';
var customerId = ko.observable(0);
var childRouter = router.createChildRouter();
return {
router: childRouter,
customerId: customerId,
displayName: displayName,
activate: activate
};
function activate(newCustomerId) {
customerId(newCustomerId);
// resetting, otherwise every activation adds elements
childRouter.reset();
// individual mapping on each activation, so the id is updated in the hashes
childRouter.makeRelative({ moduleId: 'viewmodels/customers' , route: 'customer/:id/'})
.map([
{
route: '',
moduleId: 'overview',
name: 'overview',
hash: '#customer/'+ v6Id(),
nav: true,
},
{
route: 'sessions',
moduleId: 'sessions',
name: 'Sessions',
hash: '#customer/'+ v6Id() + '/sessions',
nav: true,
}
])
.buildNavigationModel();
}
});
P.S.: Don't forget to set 'nav' to true, that was the reason why my navigation didn't show up.
P.S.2: Perhaps you should add properties to each route to differ in the view between main-nav and sub-nav.
Related
Using Vue3 - Vuerouter4 - Vite
I try to import the components and routes in vue router but I face this error (only for the route that has children in my paths):
my router code:
import { createRouter, createWebHistory } from "vue-router";
import paths from "./path";
import { TokenService } from "#/services/storage.services.js";
function route(options) {
let path = options.path;
let view = options.view;
let name = options.name;
let meta = options.meta ? options.meta : "";
let children = options.children ? options.children : null;
let redirect = options.redirect ? options.redirect : null;
let currentRoute = {
name: name || view,
path,
meta,
component: (resolve) => import(`#/views/${view}.vue`).then(resolve),
};
if (children && Array.isArray(children)) {
children = children.map((path) => {
path.view = view + "/" + path.view;
return path;
});
currentRoute["children"] = children.map((path) => route(path));
}
if (redirect) {
currentRoute["redirect"] = redirect;
}
return currentRoute;
}
// Create a new router
const router = createRouter({
history: createWebHistory(),
routes: paths
.map((path) => route(path))
.concat([{ path: "/:pathMatch(.*)", redirect: "admin/home" }]),
scrollBehavior(to, from, savedPosition) {
if (savedPosition) {
return savedPosition;
}
if (to.hash) {
return { selector: to.hash };
}
return { left: 0, top: 0 };
},
});
export default router;
my paths that are in paths.js:
export default [
{
path: "/admin",
name: "Admin",
view: "Admin",
redirect: "Admin/Home",
children: [
{
path: "Home",
name: "Home",
view: "Home",
meta: {
auth: true,
title: "داشبورد",
},
},
{
path: "TRANSACTION",
name: "TRANSACTION",
view: "Transaction",
meta: {
auth: true,
title: "تراکنش ها",
},
},
{
path: "SMS-MANAGEMENT",
name: "SMSManagement",
view: "SMSManagement",
meta: {
auth: true,
title: "مدیریت پیامک ها",
},
},
{
path: "CAR-LIST",
name: "CAR-LIST",
view: "Car-List",
meta: {
auth: true,
title: "لیست خودرو های اجاره ای",
},
},
{
path: "ADDRENTCAR",
name: "ADDRENTCAR",
view: "AddRentCar",
meta: {
auth: false,
title: "افزودن خودرو اجاره ای",
},
},
{
path: "EDITRENTCAR",
name: "EDITRENTCAR",
view: "AddRentCar",
meta: {
auth: false,
title: "ویرایش خودرو اجاره ای",
},
},
{
path: "USERS",
name: "USERS",
view: "Users",
meta: {
auth: true,
title: "لیست کاربران",
},
},
{
path: "CARS",
name: "CARS",
view: "Cars",
meta: {
auth: true,
title: "لیست خودرو ها",
},
},
{
path: "REQUESTS",
name: "REQUESTS",
view: "REQUESTS",
meta: {
auth: true,
title: "لیست درخواست ها",
},
},
],
},
{
path: "",
name: "MAIN-HOME",
view: "main-home",
meta: {
auth: true,
title: "صفحه اصلی",
public: true,
},
},
{
path: "/PROFILE",
name: "PROFILE",
view: "PROFILE",
meta: {
auth: true,
title: "پروفایل من",
},
},
{
path: "/LOGIN",
name: "LOGIN",
view: "Login",
meta: {
auth: true,
title: "ورود",
},
},
{
path: "/ALLCARS",
name: "ALLCARS",
view: "ALLCARS",
meta: {
public: true,
auth: true,
title: "لیست تمام خودرو ها",
},
},
{
path: "/ABOUTUS",
name: "ABOUTUS",
view: "ABOUTUS",
meta: {
public: true,
auth: true,
title: "درباره ما",
},
},
];
Any ideas what causes the error specifically for my admin route that has children??!!
........................................................................................................................................................................................................................................................................................................................................................................................................................................................................
I changed the "../views/" to "/src/views/" and problem solved.
Seems like using '..' wouldn't make it to the src folder!!!!!
Thank you #Mohammad Masoudi
As my project is growing, I've noticed a lot of repetitions. I'm starting with the navigations buttons, as they can appear in multiple places (side menu, navbar).
I'd like to centralize them and let the component import them as needed. So I've tried creating this file to hold all my menu items:
// menuItems.js
export default {
data() {
return {
items: [
{ title: 'Profile', icon: 'mdi-account-circle', reqAuth: true, hideOnAuth: false},
{ title: 'Dashboard', icon: 'mdi-view-dashboard', reqAuth: true, hideOnAuth: false },
{ title: 'Settings', icon: 'mdi-cog', reqAuth: true, hideOnAuth: false },
{ title: 'Signup', icon: 'mdi-account-circle-outline', reqAuth: false, hideOnAuth: true},
{ title: 'Login', icon: 'mdi-login', reqAuth: false, hideOnAuth: true },
{ title: 'Logout', icon: 'mdi-logout', reqAuth: true, hideOnAuth: false},
]
}
},
methods: {
menuItems: function(authenticated) {
if (!authenticated) {
// Gets items that do and don't require Auth, except for ones that should hide on Auth
return this.items.filter(o => o.reqAuth || !o.reqAuth && !o.hideOnAuth)
}
// Gets items that don't require Auth
return this.items.filter(o => !o.reqAuth)
}
}
}
Buttons can require authentication to be visible, and they can also be hidden once authenticated (eg. The login button).
Now lets assume I have a vue component for my nav bar, how do I import in the method that returns the filtered items?
// NavBarComponent.vue
<template>
<div>
<v-btn v-for="(item, i) in menuItems(authenticated)">
{{ item.title }}
</v-btn>
</div>
</template>
<script>
export default {
name: "NavBarComponent",
data() {
return {
authenticated: true,
}
},
methods: {
}
}
</script>
In this case, how do i make menuItems in the component reference the external file that will do the work of filtering?
You can create a mixin file and put your methods in that mixin and apply the mixin your component.
https://v2.vuejs.org/v2/guide/mixins.html
Your mixin would look like this:
// /mixins/menuItemsMixin.js
const menuItemsMixin= {
data() {
return {
items: [
{ title: 'Profile', icon: 'mdi-account-circle', reqAuth: true, hideOnAuth: false},
{ title: 'Dashboard', icon: 'mdi-view-dashboard', reqAuth: true, hideOnAuth: false },
{ title: 'Settings', icon: 'mdi-cog', reqAuth: true, hideOnAuth: false },
{ title: 'Signup', icon: 'mdi-account-circle-outline', reqAuth: false, hideOnAuth: true},
{ title: 'Login', icon: 'mdi-login', reqAuth: false, hideOnAuth: true },
{ title: 'Logout', icon: 'mdi-logout', reqAuth: true, hideOnAuth: false},
]
}
},
methods: {
menuItems: function(authenticated) {
if (!authenticated) {
// Gets items that do and don't require Auth, except for ones that should hide on Auth
return this.items.filter(o => o.reqAuth || !o.reqAuth && !o.hideOnAuth)
}
// Gets items that don't require Auth
return this.items.filter(o => !o.reqAuth)
}
}
};
export default menuItemsMixin
And in your component:
// NavBarComponent.vue
<script>
import menuItemsMixin from './mixins/menuItemsMixin.js'
export default {
mixins:[menuItemsMixin]
}
</script>
You can import this mixin in multiple components and you can also add more mixins in this component where the unique methods will be added.
I ended up creating a javascript file:
// views.js
export const views = [
{title: 'Dashboard'},
{title: 'Profile'},
{title: 'Login/Signup'},
]
then in my navbar component I imported it like so:
import {mapGetters} from "vuex";
import {views} from "../../common/views";
export default {
data: () => ({
items: views
}),
computed: {
...mapGetters(['isAuthenticated',])
menuItems: function (){
if (this.isAuthenticated) {
// do this
} else {
// do this
}
},
}
}
Then I did the same for the filtering function, but I could also just re-code it as needed if required in each component. I determined authentication state using Vuex's store, and retrieve it with mapgetters.
<componentA v-if='isAuthenticated'>
<navItem v-for='item in menuItems'>
</componentA>
When i use refresh button in my browser or hit f5 on keyboard instead of refreshing my page it redirects to home page.
Code
router.js
import Vue from "vue";
import VueRouter from 'vue-router';
import store from './store';
Vue.use(VueRouter);
import NotFoundComponent from './components/NotFoundComponent.vue';
const router = new VueRouter({
mode: "history",
routes: [
{
path: '*',
name: 'notFound',
component: NotFoundComponent,
meta: {
breadCrumb: 'Not Found'
}
},
//rest of routes...
]
});
router.beforeEach((to, from, next) => {
if (to.matched.some(record => record.meta.requiresAuth)) {
if (!store.getters.isLoggedIn) {
next({
name: 'login'
})
} else {
next()
}
}
if (to.matched.some(record => record.meta.admin)) {
if (store.getters.loggedUser.type !== 'admin') {
next({
name: 'home'
})
} else {
next()
}
}
next()
});
router.afterEach((to, from) => {
Vue.nextTick(() => {
document.title = to.pageTitle || 'Test App';
});
});
export default router;
For instance if i am in this URL
https://example.com/products
after refresh i will redirect to
https://example.com
Any idea?
Update
route.js full code
import Vue from "vue";
import VueRouter from 'vue-router';
import store from './store';
Vue.use(VueRouter);
//admins
import pageHome from './components/HomePage.vue';
import Dashboard from './components/admin/Dashboard.vue';
import AdminProducts from './components/admin/Products/Products.vue';
import AddProducts from './components/admin/Products/Add.vue';
import CurrencySettings from './components/admin/Settings/Currencies/Currency.vue';
import AddCurrencies from './components/admin/Settings/Currencies/Add.vue';
import editCurrencies from './components/admin/Settings/Currencies/Edit.vue';
import SlideSettings from './components/admin/Settings/Slides/Slide.vue';
import addSlides from './components/admin/Settings/Slides/Add.vue';
import editSlides from './components/admin/Settings/Slides/Edit.vue';
import categoriesSettings from './components/admin/Categories/Categories.vue';
import addCategories from './components/admin/Categories/Add.vue';
import editCategories from './components/admin/Categories/Edit.vue';
import tagsSettings from './components/admin/Tags/Tags.vue';
import addTags from './components/admin/Tags/Add.vue';
import editTags from './components/admin/Tags/Edit.vue';
import brandsSettings from './components/admin/Brands/Brands.vue';
import addBrands from './components/admin/Brands/Add.vue';
import editBrands from './components/admin/Brands/Edit.vue';
import usersSettings from './components/admin/Users/Users.vue';
import addUsers from './components/admin/Users/Add.vue';
import editUsers from './components/admin/Users/Edit.vue';
import reviewsSettings from './components/admin/Reviews/Reviews.vue';
import editReviews from './components/admin/Reviews/Edit.vue';
// users
import Register from './components/auth/Register.vue';
import Login from './components/auth/Login.vue';
import Profile from './components/auth/Profile.vue';
import SingleProduct from './components/Front/SingleProduct.vue';
import NotFoundComponent from './components/NotFoundComponent.vue';
const router = new VueRouter({
mode: "history",
routes: [
{
path: '*',
name: 'notFound',
component: NotFoundComponent,
meta: {
breadCrumb: 'Not Found'
}
},
// ADMIN ROUTES
{
path: '/dashboard',
name: 'dashboard',
component: Dashboard,
meta: {
requiresAuth: true,
admin: true,
layout: 'admin',
name: 'dashboard',
breadCrumb: 'Dashboard'
}
},
{
path: '/dashboard/products',
name: 'adminProducts',
component: AdminProducts,
meta: {
requiresAuth: true,
admin: true,
layout: 'admin',
breadCrumb: 'Products'
}
},
{
path: '/dashboard/products/add',
name: 'addProducts',
component: AddProducts,
meta: {
requiresAuth: true,
admin: true,
layout: 'admin',
breadCrumb: 'Add Product'
}
},
{
path: '/dashboard/currencies',
name: 'CurrencySettings',
component: CurrencySettings,
meta: {
requiresAuth: true,
admin: true,
layout: 'admin',
breadCrumb: 'Currencies'
}
},
{
path: '/dashboard/currencies/add',
name: 'addCurrencies',
component: AddCurrencies,
meta: {
requiresAuth: true,
admin: true,
layout: 'admin',
breadCrumb: 'Add Currency'
}
},
{
path: '/dashboard/currencies/:id/edit',
name: 'editCurrencies',
component: editCurrencies,
meta: {
requiresAuth: true,
admin: true,
layout: 'admin',
breadCrumb: 'Edit Currency'
}
},
{
path: '/dashboard/slides',
name: 'SlideSettings',
component: SlideSettings,
meta: {
requiresAuth: true,
admin: true,
layout: 'admin',
breadCrumb: 'Slides'
}
},
{
path: '/dashboard/slides/add',
name: 'addSlides',
component: addSlides,
meta: {
requiresAuth: true,
admin: true,
layout: 'admin',
breadCrumb: 'Add Slide'
}
},
{
path: '/dashboard/slides/:id/edit',
name: 'editSlides',
component: editSlides,
meta: {
requiresAuth: true,
admin: true,
layout: 'admin',
breadCrumb: 'Edit Slider'
}
},
{
path: '/dashboard/categories',
name: 'categoriesSettings',
component: categoriesSettings,
meta: {
requiresAuth: true,
admin: true,
layout: 'admin',
breadCrumb: 'Categories'
}
},
{
path: '/dashboard/categories/add',
name: 'addCategories',
component: addCategories,
meta: {
requiresAuth: true,
admin: true,
layout: 'admin',
breadCrumb: 'Add Category'
}
},
{
path: '/dashboard/categories/:id/edit',
name: 'editCategories',
component: editCategories,
meta: {
requiresAuth: true,
admin: true,
layout: 'admin',
breadCrumb: 'Edit Category'
}
},
{
path: '/dashboard/tags',
name: 'tagsSettings',
component: tagsSettings,
meta: {
requiresAuth: true,
admin: true,
layout: 'admin',
breadCrumb: 'Tags'
}
},
{
path: '/dashboard/tags/add',
name: 'addTags',
component: addTags,
meta: {
requiresAuth: true,
admin: true,
layout: 'admin',
breadCrumb: 'Add Tag'
}
},
{
path: '/dashboard/tags/:id/edit',
name: 'editTags',
component: editTags,
meta: {
requiresAuth: true,
admin: true,
layout: 'admin',
breadCrumb: 'Edit Tag'
}
},
{
path: '/dashboard/brands',
name: 'brandsSettings',
component: brandsSettings,
meta: {
requiresAuth: true,
admin: true,
layout: 'admin',
breadCrumb: 'Brands'
}
},
{
path: '/dashboard/brands/add',
name: 'addBrands',
component: addBrands,
meta: {
requiresAuth: true,
admin: true,
layout: 'admin',
breadCrumb: 'Add Brand'
}
},
{
path: '/dashboard/brands/:id/edit',
name: 'editBrands',
component: editBrands,
meta: {
requiresAuth: true,
admin: true,
layout: 'admin',
breadCrumb: 'Edit Brand'
}
},
{
path: '/dashboard/users',
name: 'usersSettings',
component: usersSettings,
meta: {
requiresAuth: true,
admin: true,
layout: 'admin',
breadCrumb: 'Users'
}
},
{
path: '/dashboard/users/add',
name: 'addUsers',
component: addUsers,
meta: {
requiresAuth: true,
admin: true,
layout: 'admin',
breadCrumb: 'Add User'
}
},
{
path: '/dashboard/users/:id/edit',
name: 'editUsers',
component: editUsers,
meta: {
requiresAuth: true,
admin: true,
layout: 'admin',
breadCrumb: 'Edit User'
}
},
{
path: '/dashboard/reviews',
name: 'reviewsSettings',
component: reviewsSettings,
meta: {
requiresAuth: true,
admin: true,
layout: 'admin',
breadCrumb: 'Reviews'
}
},
{
path: '/dashboard/reviews/:id/edit',
name: 'editReviews',
component: editReviews,
meta: {
requiresAuth: true,
admin: true,
layout: 'admin',
breadCrumb: 'Edit Review'
}
},
// public routes
{
path: "/",
name: 'home',
component: pageHome,
meta: {
breadCrumb: 'Home Page'
}
},
{
path: "/products/:slug",
name: 'SingleProduct',
component: SingleProduct,
meta: {
breadCrumb: 'Product'
}
},
// auth
{
path: '/profile',
name: 'profile',
component: Profile,
meta: {
requiresAuth: true,
breadCrumb: 'Profile'
}
},
{
path: '/register',
name: 'register',
component: Register,
meta: {
breadCrumb: 'Register'
}
},
{
path: '/login',
name: 'login',
component: Login,
meta: {
breadCrumb: 'Login'
}
}
]
});
router.beforeEach((to, from, next) => {
if (to.matched.some(record => record.meta.requiresAuth)) {
if (!store.getters.isLoggedIn) {
next({
name: 'login'
})
} else {
next()
}
}
if (to.matched.some(record => record.meta.admin)) {
if (store.getters.loggedUser.type !== 'admin') {
next({
name: 'home'
})
} else {
next()
}
}
next()
});
router.afterEach((to, from) => {
Vue.nextTick(() => {
document.title = to.pageTitle || 'Test App';
});
});
export default router;
Solved
working code
router.beforeEach((to, from, next) => {
if (to.matched.some(record => record.meta.requiresAuth)) {
// this route requires auth, check if logged in
// if not, redirect to login page.
if (!store.getters.isLoggedIn) {
next({
name: 'login'
})
} else {
next()
}
}
if (to.matched.some(record => record.meta.requiresAdmin)) {
// this route requires auth, check if logged in
// if not, redirect to home page.
if (!store.getters.loggedUser.type == 'admin') {
next({
name: 'home'
})
} else {
next()
}
}
else {
next() // make sure to always call next()!
}
})
Hope it help others.
When I press refresh(F5) button then my router directs me to the main page. How do I stay on the same page when the page is refreshed?
that's route, mode is 'history'
const routes = [
{ path: '*', redirect: '/' },
{
path: '/', component: template_Default, redirect: '/Login',
children: [
{ path: 'Login', component: login_, name: 'Login' },
{ path: 'SignUp', component: signUp_, name: 'SignUp' }
]
},
{
path: '/Main', component: template_Home, name: 'Main', redirect: '/Main/Overview',
children: [
{ path: 'Overview', component: overview_, name: 'Overview' },
{ path: 'Customer', component: customer_, name: 'Customer' },
{ path: 'Task', component: task_, name: 'Task' },
{ path: 'Order', component: order_, name: 'Order' },
{ path: 'Product', component: product_, name: 'Product' },
{ path: 'Setting', component: setting_, name: 'Setting' }
]
},
{
path: '/ForgotPassword', component: forgotPassword_, name: 'ForgotPassword',
// redirect: '/ForgotPassword/GetEmail',
// children: [
// { path: 'GetEmail', component: forgotPasswordGetEmail, name: 'GetEmail' },
// { path: 'ReSend', component: forgotPasswordReSend, name: 'ReSend' }
// ]
}
]
And my controller,
router.beforeEach((to, from, next) => {
// redirect to login page if not logged in and trying to access a restricted page
const publicPages = ['/Login','/SignUp','/ForgotPassword'];
const authRequired = !publicPages.includes(to.path);
const loggedIn = store.getters.isLoggedIn;
if (authRequired && !loggedIn) {
return next('/Login');
}else next();
if(loggedIn){
AuthController.IsOnline();
}
})
thanks in advance..
The issue is that you have { path: '*', redirect: '/' } as the first item in your routes array. According to the documentation:
When using asterisk routes, make sure to correctly order your routes so that asterisk ones are at the end.
This is because Vue Router searches through your list of routes in order and returns the first matching route. An asterisk is going to match any and all routes so, when you refresh the page, it returns the first route and redirects you to '/'.
Same problem here, but my route have no { path: '*', redirect: '/' }
Here is my code.
export const routes = [
{
path:'/login',
component: login,
name: 'home'
},
{
path:'/',
component: mainapp,
name: 'mainapp',
meta: {
requiresAuth: true
},
children: [
{
path:'/dash',
component: dashboard,
name: 'dashboard'
},
{
path:'/dept',
component: dept,
name: 'department'
},
{
path: '/deptDetail',
component: deptDetail,
name: 'detail'
},
{
path:'/hrd',
component: HRD
},
{
path:'/notify',
component: notify
},
{
path:'/device',
component: device
},
{
path:'/setup',
component: setup,
name: 'setup'
},
{
path:'/scanArea',
component: area,
},
{
path:'/env',
component: environment,
},
{
path:'/record', /* debug page */
component: record,
},
{
path:'/redis', /* debug page */
component: redis,
},
{
path:'/permit',
component: permit,
},
{
path:'/app',
component: app,
}
]
},
]
I am looking at the Durandal samples trying to understand how routing works.
The shell.js specifies these routes:
{ route: ['', 'knockout-samples*details'], moduleId: 'ko/index', title: 'Details...', nav: true, hash: '#knockout-samples' },
{ route: 'view-composition',moduleId: 'viewComposition/index', title: ...
under knockout-samples:
{ route: '', moduleId: 'helloWorld/index', title: 'Hello World', type: 'intro' },
{ route: 'helloWorld', moduleId: 'helloWorld/index', title: 'Hello World', type: intro', nav: true},
What I am trying to achieve is having another hierarchy under helloWorld. Something like this:
I tried this but no luck:
{ route: '', moduleId: 'helloWorld/index', title: 'Hello World', type: 'intro' },
{ route: 'helloWorld*details', moduleId: 'helloWorld/index', title: 'Hello World', type: 'intro', nav: true, hash:'#knockout-samples/helloWorld'}
However, this is not working.
Does Durandal routing not support this level of navigation?
When creating a 'grandchild' or 'great grandchild' or deeper child router, the trick is to reference the relative parent router, not the root router. To get a reference to the parent router, add the module that contains the parent router as a dependency to your 'grandchild' module. You can nest routers like this indefinitely. For example:
myModuleWithChildRouter.js
define(['plugins/router'], //reference to durandal root router
function(router) {
var _childRouter = router.createChildRouter();
return { myNewChildRouter: _childRouter}
}
myModuleWithGrandchildRouter.js
define(['myModuleWithChildRouter'], //reference to module with child router
function(childRouterModule) {
var _grandChildRouter = childRouterModule.myNewChildRouter.createChildRouter();
.....
}
Hope that helps!
To get more than one navigation level I'm doing this:
The only accesible router is the root router so to have acces to the child routers, everytime that I'm creating a child router, i store it on a module.
Then, when i want to create another level, I get the child router from the module and call createChildRouter.
define([], function () {
return {
root: null,
level1: null,
level2: null
};
});
define(['plugins/router', 'routers'], function (router, routerContainer) {
var childRouter = router.createChildRouter()
.makeRelative({
moduleId: 'viewmodels/companyplussplat',
//fromParent: true
route: 'company'
}).map([
{ route: 'order/:orderID', moduleId: 'orderdetail', title: 'Order', nav: false },
{ route: 'order/:orderID*details', moduleId: 'orderdetailplussplat', title: 'Order plus splat', nav: false }
]).buildNavigationModel();
routerContainer.level1 = childRouter;
return {
activate: function () {
console.log("Activating company plus splat");
},
deactivate: function () {
console.log("Deactivating company plus splat");
},
router: childRouter
};
});
define(['plugins/router', 'routers'], function (router, routerContainer) {
//debugger;
var childRouter = routerContainer.level1.createChildRouter()
.makeRelative({
moduleId: 'orderteailplussplat',
//fromParent: true
route: 'company/order/:orderID'
}).map([
{ route: 'orderline/:orderlineID', moduleId: 'orderlinedetail', title: 'Order line detail', nav: false },
]).buildNavigationModel();
routerContainer.level2 = childRouter;
return {
activate: function (orderID) {
console.log('Activating order detail for: '+ orderID +' plus splat');
},
deactivate: function () {
console.log('Deactivating order detail plus splat');
},
router: childRouter
};
});
I hope this will help you.
I added the child as a reference to the parent router itself. Maybe a bit sneaky, but working happily:
Top level router
define(["plugins/router"], function (router) {
// create the constructor
var ctor = function() {
};
ko.utils.extend(ctor.prototype, {
activate: function () {
//var self = this;
var map = router.makeRelative({ moduleId: "viewmodels" }).map([
{ route: "", moduleId: "index", title: "Overview", nav: true, hash: "#/", enabled: true },
{ route: "data*details", moduleId: "data/shell", title: "Data Loading", nav: true, hash: "#/data", enabled: false },
{ route: "reporting*details", moduleId: "reporting/shell", title: "Reporting", nav: true, hash: "#/reporting", enabled: true },
{ route: "query*details", moduleId: "query/shell", title: "Query", nav: true, hash: "#/query", enabled: true },
{ route: "login", moduleId: "login", title: "Login", hash: "#/login", state: "out" }
]);
return map.buildNavigationModel()
.mapUnknownRoutes("404")
.activate();
});
});
return ctor;
});
Child router
define(["plugins/router"], function (router) {
var childRouter = router.createChildRouter()
.makeRelative({
moduleId: "viewmodels/reporting",
fromParent: true
}).map([
{ route: "", moduleId: "index", title: "Reporting", nav: false, hash: "#/reporting" },
{ route: "standard", moduleId: "standard", title: "Standard Reports", nav: true, hash: "#/reporting/standard" },
{ route: "alert*details", moduleId: "alert/shell", title: "Alerts", nav: true, hash: "#/reporting/alert" }
]).buildNavigationModel();
// for alerts
router.child = childRouter;
var vm = {
router: childRouter
};
return vm;
});
Grandchild router
define(["plugins/router"], function (router) {
var grandchildRouter = router.child.createChildRouter()
.makeRelative({
moduleId: "viewmodels/reporting/alert",
fromParent: true
}).map([
{ route: "", moduleId: "index", title: "Alerts", hash: "#/reporting/alert" },
{ route: ":id", moduleId: "case", title: "Alert Details", hash: "#/reporting/alert" }
]).buildNavigationModel();
var vm = {
router: grandchildRouter
};
return vm;
});
Hope that helps.
If you are using durandal 2.0 you can set up the child router. This will allow you to create a new router under the hello world that you can chain on additional info for for sub views in your view. You can look these up on the docs but make sure you set up that router within the view so when you hit a route like
#helloworld/subview
you have already activated helloworld