How can I escape client side routing when wanting to navigate outside the set list of routes setup in the router?
I created a project using Durandal.js and have created SPA's inside different Areas. The problem I ran into is that when I want to navigate outside the current SPA and into another or say back to the home page of the entire application which is not an SPA at all but simply a cshtml page.
What I have tried to do is use Durandal's mapUnknownRoutes handler to intercept and then use window.location.href to navigate out. This works, but when I want to go the home page of the application ("/"), the router matches the "root" of the SPA and doesn't navigate out to the home page but instead the SPA's root route.
The area route in this example is "/spaHome" whose MVC route looks like:
context.MapRoute(
"spaHome_default",
"spaHome/{*catchall}",
new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
Here's what I've done to set up the Durandal router:
var routes = [
{ route: ['spaHome', ''], moduleId: 'spaHome', title: "spaHome", hash: "#spaHome"},
{ route: 'one/anotherPage', moduleId: 'one/anotherPage', title: "one/anotherPage", hash: "#one/anotherPage"}
];
router.makeRelative({ moduleId: 'viewmodels' });
router.map(routes)
.mapUnknownRoutes(function (instruction) {
utils.navigateToPath(instruction.fragment);
return false;
})
.activate({ pushState: true, root: "/spaHome" });
Any pointers or leads into the right direction for this would be much appreciated!
After some trial and error I was able to come up with a solution to what I was trying accomplish. It looks like a solid solution, so hopefully it doesn't cause any issues down the road.
I changed the routes array and removed the default route of '' from the first route. This allowed me to have a unknown route to go off of when wanting to hit the normal MVC homepage. I also had to remove the "root" property from the activate function of the router. Which in turn meant I had to explicitly declare the routes in the route array with the extra area portion or the URL ("spaHome/").
Then in my mapUnknownRoutes handler I checked the route for the area portion of the URL, and if that existed, I used the SPA router to show a notfound page for that SPA. Else I assumed that the route exists outside the area and I need to hard navigate to the URL using window.location.href.
var routes = [
{ route: ['spaHome'], moduleId: 'spaHome', title: "spaHome", hash: "#spaHome"},
{ route: 'spaHome/one/anotherPage', moduleId: 'one/anotherPage', title: "one/anotherPage", hash: "#spaHome/one/anotherPage"}
];
router.makeRelative({ moduleId: 'viewmodels' });
router.map(routes)
.mapUnknownRoutes(function (instruction) {
if (instruction.fragment.toLowerCase().indexOf("spaHome/", 0) === -1) {
utils.navigateToPath(instruction.fragment);
} else {
var notfoundRoute = "notfound";
instruction.config.moduleId = notfoundRoute;
history.navigate(notfoundRoute, { trigger: false, replace: true });
}
})
.activate({ pushState: true });
If anyone has a better solution please let me know!
EDIT
Ok, I ran into an issue with the history while doing this. I had to add a line to the Durandal router.js file to stop the previous route from being added to the history queue.
if (!instruction.cancelQueue) {
router.trigger('router:route:before-config', instruction.config, router);
router.trigger('router:route:after-config', instruction.config, router);
queueInstruction(instruction);
}
Edit 2
I also ran into an issue with this method where the navigation doesn't work quite right for IE9 and below.