[SOLVED] Use child router for "main" routes

Hi,

I am building a site which has two different part: a website and a game. All pages of the site have some common elements (a navbar and a footer) which must not appear on the game related pages. To achieve this, I decided to use two child routers: one for the game related pages and one for the site ones to easily include the navbar and the footer on all the pages. I want to have URLs like: /game/something (ie with a /game prefix for all routes) for the game and something like /synopsis for the site (ie without a prefix for my routes).

My problem is that I get route not found errors for all routes of the site except the homepage (the empty route of the child router).

I tried to rely on config.mapUnknownRoutes to redirect unknown routes to the site child router but all I can get is the homepage. Here is my config.mapUnknownRoutes function:

config.mapUnknownRoutes(instruction => {
        // moduleid maps to the subrouter. If I change it to point to a page, I get `plugin.load is undefined` errors.
        return { route: 'site', moduleId: 'site/site' };
    });

I also saw issues for something similar (https://github.com/aurelia/router/issues/117, https://github.com/aurelia/router/issues/27) but it looks that the only fixed case is for the homepage.

The configuration of my routers:
In app.js:

    configureRouter(config, router) {
        this.router = router;
        config.title = 'Arena of Titans';
        config.options.pushState = true;
        config.map([
            {
                route: '',
                name: 'site',
                moduleId: 'site/site',
                nav: true,
                title: 'Homepage',
            }, {
                route: 'game',
                name: 'game',
                moduleId: 'game/game',
                nav: true,
                title: 'Game',
            },
        ]);
    }

In my site sub-router:

    configureRouter(config, router) {
        // config.baseUrl = 'site';
        config.baseUrl = '/';
        config.options.pushState = true;
        config.map([
            {
                route: [''],
                name: 'home',
                moduleId: './routes/home/home',
                nav: false,
                title: 'Home',
            },
            {
                route: ['synopsis'],
                name: 'synopsis',
                moduleId: './routes/synopsis/synopsis',
                nav: false,
                title: 'Synopsis',
            },
        ]);
    }

Is it possible to do what I want or should I declare the routes of the site in the main router and use if.bind to show/hide the navbar and the footer?

I would do this with aurelia.setroot(ā€˜appā€™) and aurelia.setroot(ā€˜gameā€™). App.html would include a nav-bar, a body with class=ā€œpage-hostā€ and a footer.

This could be a solution. I donā€™t have time to do much testing right now (maybe later today) but I have two questions that come to mind:

  1. How do you detect which root to set?
  2. How you the links from one section of the site to the other work? For instance, I am on the site, and I want to go to /game/create? My guess would be that the site application will try to find the route but wonā€™t be able to do so.

Can you provide some insights on these points?

You wouldnā€™t need a a /game/create route, and you wouldnā€™t need any child routes. The navbar would have a playGame link, which would setroot(ā€˜gameā€™). When the user tires of the game and clicks the endGame button, it calls setroot(ā€˜appā€™).

I see what you mean. But with this solution the URLs would be broken which is not really good for sharing the link to the game (the game can have multiple players and we rely on the URL for all of them to join). But I think I can solve my problem by building on your proposal. I hope Iā€™ll be able to test tonight. Iā€™ll keep you posted.

I would probably use multiple router viewports for this and use the new option to set viewports to empty.

@rhassler I was able to try your solution yesterday and I donā€™t think it will work for my use case. Since I have to handle the URLs, switching root is not practical. I managed to rely on the mapUnknowRoutes function to detect when a game page is asked and switch the root accordingly but I also have to set the correct root when the application starts and when the user wants to return to the site. It feels hackish and complex. I also encounter some issues with URLs redirects so I donā€™t think it is realible. But Iā€™ll keep it in mind if I ever encounter a case where I donā€™t care about the URLs.

@jwx I was also able to test your idea and it is probably the safest and easiest one. Iā€™ll discuss it with other members of the team.

You donā€™t need to specify

// config.baseUrl = 'site';
    config.baseUrl = '/';
    config.options.pushState = true;

on the child route

and router-view supports layout http://aurelia.io/docs/routing/configuration#layouts so you should be able to specify different layouts for each route if you want

We decided use use layouts, it appears to be the easiest solution and the one that integrates the best without our use case.

Thank you all for your help.

2 Likes

Stumbled over the same problem, ultimately needing the ā€œmainā€ routes on the child router. But in fact, I stumbled over it in reverse. I used first layouts (layout-view-model to be exact), just to be frustrated as soon as I realized that layouts rerender COMPLETELY on each route change. So this is not a solution either, I explained here why:

The only workaround seems to be setroot, to have multiple entry points and switch the root element out on the authorization step.